Learning OpenCV with iOS:圖像混合與ROI

一、前言

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

鎧與Logo

二、圖片相加

要疊加兩張圖片,可以將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)
    }
}
α和β都等于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

鎧與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!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容