【OpenCV影象處理】八、影象的掩碼操作
本篇內容參考 朱偉 主編 OpenCV影象處理程式設計例項
影象的掩碼操作是指通過掩碼核算子重新計算影象中各個畫素的值,掩碼核子刻畫鄰域畫素點對新畫素值的影響程度,同時根據掩碼運算元中權重因子對原畫素點進行加權平均。影象掩碼操作常常用於影象的平滑,邊緣檢測和特徵分析等不同的領域。
在OpenCV中常用的計算影象掩碼的操作有下面兩種。
1.基於畫素鄰域遍歷
對於原影象資料f(x,y),卷積核算子為3x3,計算原影象四鄰域均值掩碼可以通過下面的式子得到:
f(x,y) = (f(x-1,y)+f(x+1,y),f(x,y-1),f(x,y+1))/4
對於影象矩陣來說,上式可以用下面的式子代替:
f(x,y) = f(x,y) * Mask
其中,Mask為下面的矩陣
所以說,基於影象的鄰域遍歷就是通過對源資料矩陣進行操作,利用上面的公式以當前畫素點為計算中心目標點,逐畫素移動掩碼核算子模板,對原影象資料進行遍歷,進而更新新影象對應的每個畫素點值。具體程式碼將會在下面進行實現。
2.filter2D函式
在OpenCV中提供了filter2D函式用來專門應用於計算影象卷積的操作,首先簡單介紹一下這個函式:
這個函式基本上是用來實現影象的卷積操作,前兩個引數分別表示輸入影象和輸出影象,第三個引數ddepth表示的是影象的深度,如果這個值設定為負數,則這個影象的深度與輸入的源影象的深度相同,否則就需要根據源影象的深度進行相關的設定void filter2D( InputArray src, OutputArray dst, int ddepth,InputArray kernel, Point anchor=Point(-1,-1),double delta=0, int borderType=BORDER_DEFAULT );
例如,若src.depth()=CV_8U,則ddepth=-1 / CV_16S / CV_32F / CV_64F,若src.depth() = CV_16U/CV_16S,則ddepth = -1 / CV_32F / CV_64F,若src.depth() =CV_32F,則ddepth= -1 / CV_32F / CV_64F,若src.depth() = CV_64F,則ddepth = -1 / CV_64F.
第四個引數kernel是卷積核算子,為單通道浮點矩陣,如果對多通道應用不同的卷子核算子計算,需要首先分離成為單通道後在進行單通道上的操作。引數anchor是卷積核錨點,預設值是(-1,-1)表示的是卷積核中心。引數delta是平滑係數,目標影象生成前可已通過設定這個值用於目標影象的平滑操作。最後一個引數表示的是邊界型別,有預設值BORDER_DEFAULT)
需要說明的是,這個函式常常應用於線性濾波技術中,當使用卷積核算子計算的影象目標點在影象外部時,需要對指定邊界進行插值運算。這個函式實際上計算的是影象的相關性,而非卷積操作,它的計算公式如下:
其中0<= x' < kernel.cols,0<= y' < kernel.rows
相關的程式如下:
//影象掩碼操作的兩種實現
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace std;
using namespace cv;
Mat Myfilter2D(Mat srcImage);
Mat filter2D_(Mat srcImage);
int main()
{
Mat srcImage = imread("2345.jpg");
if (!srcImage.data)
{
cout << "讀入圖片失敗" << endl;
return -1;
}
Mat srcGray;
cvtColor(srcImage, srcGray, CV_BGR2GRAY);
imshow("srcGray", srcGray);
Mat resultImage = Myfilter2D(srcGray);
imshow("resultImage1", resultImage);
resultImage = filter2D_(srcGray);
imshow("resultImage2", resultImage);
waitKey();
return 0;
}
//基於畫素鄰域的掩碼操作
Mat Myfilter2D(Mat srcImage)
{
const int nChannels = srcImage.channels();
Mat resultImage(srcImage.size(), srcImage.type());
for (int j = 1; j < srcImage.rows - 1; j++)
{
//獲取鄰域指標
const uchar* previous = srcImage.ptr<uchar>(j - 1);
const uchar* current = srcImage.ptr<uchar>(j);
const uchar* next = srcImage.ptr<uchar>(j + 1);
uchar * output = resultImage.ptr<uchar>(j);
for (int i = nChannels; i < nChannels*(srcImage.cols - 1); ++i)
{
//進行4-鄰域掩碼操作
*output++ = saturate_cast<uchar>(current[i - nChannels] + current[i + nChannels]
+ previous[i] + next[i]) / 4;
}
}
//進行邊界處理
resultImage.row(0).setTo(Scalar(0));
resultImage.row(resultImage.rows - 1).setTo(Scalar(0));
resultImage.col(0).setTo(Scalar(0));
resultImage.col(resultImage.cols - 1).setTo(Scalar(0));
return resultImage;
}
//使用自帶掩碼庫進行操作
Mat filter2D_(Mat srcImage)
{
Mat resultImage(srcImage.size(), srcImage.type());
//構造核函式因子
Mat kern = (Mat_<float>(3, 3) << 0, 1, 0,
1, 0, 1,
0, 1, 0) / (float)(4);
filter2D(srcImage, resultImage, srcImage.depth(), kern);
return resultImage;
}