一、前言
上一篇我們講解了OpenCV的掩膜操作。本篇主要向大家介紹下圖像處理中的圖像混合。按慣例,先來一張效果圖。

二、圖片相加
要疊加兩張圖片,可以將Mat直接相加,相加兩幅圖片的形狀(高度/寬度/通道數(shù))必須相同。但這樣的效果不一定是你想要的。我們來看一個例子。
Mat src;
Mat src2;
UIImageToMat(image, src);
UIImageToMat(image2, src2);
Mat dst;
dst = src + src2;
UIImage* result = MatToUIImage(dst);

三、線性混合
圖像混合其實也是一種圖片相加的操作,只不過兩幅圖片的權(quán)重不一樣。
g(x) = α*f0(x) + β*f1(x)
OpenCV處理圖像混合主要是根據(jù)線性混合函數(shù),通過在0到1范圍內(nèi)改變α的值,使兩幅圖像或者視頻產(chǎn)生在時間上的畫面疊化得效果。實際上α和β的和不一定為1,只是為了防止圖像出現(xiàn)過飽和的現(xiàn)象。
OpenCV的addWeighted函數(shù)便是對應(yīng)線性混合操作。這個函數(shù)的作用是,計算兩個圖像的加權(quán)和。
void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1);
- src1:需要加權(quán)的第一個數(shù)組,常常填一個Mat對象。
- alpha:第一個數(shù)組的權(quán)重值,0~1之間。
- src2:第二個數(shù)組,它需要和第一個數(shù)組擁有相同的尺寸和通道數(shù)。
- beta:第二個數(shù)組的權(quán)重值,一般為1-alpha。
- gamma:一個加到權(quán)重總和上的標(biāo)量值。
- dst:輸出的數(shù)組,它和輸入的兩個數(shù)組擁有相同的尺寸和通道數(shù)。
- dtype:輸出陣列的可選深度,有默認值-1;當(dāng)兩個輸入數(shù)組具有相同的深度時,這個參數(shù)設(shè)置為-1(默認值),即等同于src1.depth()。
當(dāng)后羿遇到阿珂

+ (UIImage *)addWeighted:(UIImage *)image image2:(UIImage *)image2 alpha:(double)alpha gamma:(double)gamma {
Mat src;
Mat src2;
UIImageToMat(image, src);
UIImageToMat(image2, src2);
Mat dst;
addWeighted(src, alpha, src2, gamma, 0, dst);
UIImage* result = MatToUIImage(dst);
return result;
}
class MixViewController: UIViewController {
@IBOutlet weak var resultImageView: UIImageView!
private let ake = UIImage(named: "ake")
private let houyi = UIImage(named: "houyi")
override func viewDidLoad() {
super.viewDidLoad()
resultImageView.image = OpenCV.addWeighted(ake, image2: houyi, alpha: 0.5, gamma: 0.5)
}
@IBAction func onSliderValueChanged(_ sender: UISlider) {
let alpha = Double(sender.value)
resultImageView.image = OpenCV.addWeighted(ake, image2: houyi, alpha: alpha, gamma: (1.0 - alpha))
}
}
一些思考
- 為何直接的圖像相加效果較差,而線性混合后的效果較好?
可以這樣簡單理解:像素值范圍為0~255,兩張圖的Mat直接相加,就是每個像素點的值相加,這樣容易出現(xiàn)像素值較大的像素,像素“越大越白”,這樣就出現(xiàn)了圖像過曝的現(xiàn)象。而線性混合則是加上了權(quán)重,保證了像素值不至于過大,這樣就不會出現(xiàn)過曝現(xiàn)象。以公式來說明就是
//相加
g(x) = 1*f0(x) + 1*f1(x)
//線性混合
g(x) = α*f0(x) + β*f1(x)
即相加操作相當(dāng)于α和β都等于1的線性混合。
class MixViewController: UIViewController {
@IBOutlet weak var resultImageView: UIImageView!
private let ake = UIImage(named: "ake")
private let houyi = UIImage(named: "houyi")
override func viewDidLoad() {
super.viewDidLoad()
resultImageView.image = OpenCV.addWeighted(ake, image2: houyi, alpha: 1, gamma: 1)
}
}

- addWeighted方法要求src1和src2的尺寸和通道數(shù)相同,若是不同如何混合?
實現(xiàn)這個需要先熟悉圖像處理另一個重要的概念--ROI。
四、感興趣區(qū)域ROI(Region Of Interest)
在圖像處理領(lǐng)域,我們常常需要設(shè)置ROI,來專注或者簡化我們的工作過程 。也就是從圖像中選擇的一個圖像區(qū)域,這個區(qū)域是我們圖像分析所關(guān)注的重點。我們?nèi)Χㄟ@個區(qū)域,以便進行進一步處理。而且,使用ROI指定我們想讀入的目標(biāo),可以減少處理時間,增加精度,給圖像處理來帶不小的便利。
定義ROI區(qū)域有兩種方法,第一種是使用cv:Rect指定矩形區(qū)域
Mat imageROI=image(Rect(500,250,logo.cols,logo.rows));
另一種定義ROI的方式是指定感興趣行或列的范圍(Range)
Mat imageROI=srcImage3(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));
下面我們來看一個實例,展示如何利用ROI將一幅圖加到另一幅圖的指定位置。
鎧與Logo

+ (UIImage *)add:(UIImage *)image alphaExist:(BOOL)alphaExist on:(UIImage *)anotherImage atPosition:(CGPoint)position alpha:(CGFloat)alpha beta:(CGFloat)beta gamma:(CGFloat)gamma {
Mat src;
Mat dst;
UIImageToMat(anotherImage, src);
UIImageToMat(image, dst, alphaExist);
//ROI
short x = position.x;
short y = position.y;
Mat imageRoi;
imageRoi = src(cv::Rect(x, y, dst.cols, dst.rows));
//add image
addWeighted(imageRoi, alpha, dst, beta, gamma, imageRoi);
UIImage* result = MatToUIImage(src);
return result;
}
class ROIViewController: UIViewController {
@IBOutlet weak var resultImageView: UIImageView!
private let logo = #imageLiteral(resourceName: "logo")
private let sayHello = #imageLiteral(resourceName: "sayHello")
override func viewDidLoad() {
super.viewDidLoad()
resultImageView.image = OpenCV.add(#imageLiteral(resourceName: "logo"), alphaExist: true, on: #imageLiteral(resourceName: "sayHello"), atPosition: CGPoint(x: 330, y: 0), alpha: 1.0, beta: 1.0, gamma: 0)
}
@IBAction func onSliderValueChanged(_ sender: UISlider) {
resultImageView.image = OpenCV.add(#imageLiteral(resourceName: "logo"), alphaExist: true, on: #imageLiteral(resourceName: "sayHello"), atPosition: CGPoint(x: 330, y: 0), alpha: 1.0, beta: CGFloat(sender.value), gamma: 0)
}
}
注:
- 在UIImage轉(zhuǎn)換成Mat的時候,若有透明度記得將最后一個參數(shù)alphaExist設(shè)置為true
UIImageToMat(image, dst, alphaExist);
- 在addWeighted的時候alpha表示第一個Mat的權(quán)重,beta表示第二個Mat的權(quán)重。在實例中,Logo圖片其實是第二個Mat,所以對應(yīng)的參數(shù)是beta。
Mat imageRoi;
imageRoi = src(cv::Rect(x, y, dst.cols, dst.rows));
//add image
addWeighted(imageRoi, alpha, dst, beta, gamma, imageRoi);
五、小結(jié)
本篇主要介紹了圖像混合的概念,并通過例子講解了線性混合的實現(xiàn)和ROI的應(yīng)用。在今后的學(xué)習(xí)中ROI的使用度較高,需要好好掌握。ROI其實是“分而治之”思想在圖像處理中的應(yīng)用。好了,今天就到這了。有疑問的朋友可以給我留言,see you later!