天天看點

opencv學習(六)之掩膜版

可以通過掩模矩陣(通常來講叫核)對圖像的每個圖像像素值重新計算。這個掩模闆能夠調整臨近像素包括目前像素對新像素的影響程度。從數學的角度來講,我們用特殊的值對目前的值做了一個權重平均的操作。舉個例子,設想一個圖像對比度增強的方法,基本上,我們要将下面的公式應用到每一個像素上:

opencv學習(六)之掩膜版

第一個公式是用數學公式,第二個是用一個掩模闆。将掩模闆中心放到你想計算像素上,将像素值累加并乘以與重疊矩陣值想成。對于較大的矩陣來看,後一種表達方式更容易了解。

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;

void Sharpen(const Mat& myImage, Mat& Result);

int main()
{
    Mat srcImage = imread("lena.jpg");

    //判斷圖像是否加載成功
    if(srcImage.data)
        cout << "圖像加載成功!" << endl << endl;
    else
    {
        cout << "圖像加載失敗!" << endl << endl;
        return -;
    }
    namedWindow("srcImage", WINDOW_AUTOSIZE);
    imshow("srcImage", srcImage);

    Mat dstImage;
    dstImage.create(srcImage.size(), srcImage.type());
    Sharpen(srcImage, dstImage);
    namedWindow("dstImage",WINDOW_AUTOSIZE);
    imshow("dstImage",dstImage);

    waitKey();

    return ;
}

void Sharpen(const Mat& myImage, Mat& Result)
{
    CV_Assert(myImage.depth() == CV_8U);        //判斷函數CV_Assert
    const int nChannels = myImage.channels();

    for(int j = ; j < myImage.rows - ; ++j)
    {
        const uchar* precious = myImage.ptr<uchar>(j - );      //目前像素上一行指針
        const uchar* current = myImage.ptr<uchar>(j);           //目前像素行指針
        const uchar* next = myImage.ptr<uchar>(j + );          //目前像素下一行指針

        uchar* output = Result.ptr<uchar>(j);

        //利用公式和上下左右四個像素對目前像素值進行處理
        for(int i = nChannels; i < nChannels * (myImage.cols - ); ++i)
        {
            *output++ = saturate_cast<uchar>( * current[i]
            -current[i-nChannels]-current[i+nChannels]-precious[i]-next[i]);
        }
    }
    Result.row().setTo(Scalar());                 //設定第一行所有元素值為0
    Result.row(Result.rows-).setTo(Scalar());     //設定最後一行所有元素值為0
    Result.col().setTo(Scalar());                 //設定第一列所有元素值為0
    Result.col(Result.cols-).setTo(Scalar());     //設定最後一列所有元素值為0
}
           

運作結果如圖所示:

opencv學習(六)之掩膜版

對上述程式簡單做個分析。

(1). main()函數中加載圖像後一定要判斷圖像是否加載成功,這是良好的程式設計習慣。判斷圖像是否加載成功有兩種方式如下:

//圖像為空即加載失敗
if(srcImage.empty())
{
    //...處理方法...
}
else        //圖像加載成功
{
    //...圖像加載成功...
}

//圖像是否有資料
if(srtImage.data)
{
    //...處理方法...
}
else        //即圖像加載失敗
{
    //...處理方法...
}
           

注意:在使用empty()函數時其帶後面的”()”而使用data判斷時不帶”()”,這是兩者的差別。

(2). 加載圖像成功後使用CV_Assert()函數判斷圖像是否unsigned char 類型。

(3). 通過create()函數建立一個和原圖像尺寸和類型相同的目标圖像。對于create()等函數的用法可以參考opencv學習(一)之Mat類,裡面有具體叙述,在此不過多介紹!

(4). 此程式是利用上述第一個數學公式對圖像像素進行重新計算和處理,通過其上下左右四個鄰域像素和其本身像素值通過計算得到新的像素值。在本程式中利用C語言[]操作符來讀取像素。因為我們需要同僚讀取多行,是以提前擷取目前行,上一行和下一行的指針(previous、current、next).行指定完後還需要定義一個指針存儲計算結果(output).

(5). 對于列的處理先擷取目前圖像的通道數,然後對每個像素的每個通道上的數值進行計算: current[i - nChannels]和current[i + nChannels]兩個為目前像素左右兩個像素相同通道的值,而previous[i]和next[i]為上下兩個像素值相同通道的像素值.

(6). 利用本方法計算時,對于圖像的上下左右四條邊最外面的像素點無法計算,故在最後利用四行代碼對其進行認為指派為0,這樣得到的計算結果與原圖像相比,四周會出現黑線!!!