1. 程式人生 > >【OpenCV影象處理】八、影象的掩碼操作

【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函式用來專門應用於計算影象卷積的操作,首先簡單介紹一下這個函式:

void filter2D( InputArray src, OutputArray dst, int ddepth,InputArray kernel, 
              Point anchor=Point(-1,-1),double delta=0, int borderType=BORDER_DEFAULT );
這個函式基本上是用來實現影象的卷積操作,前兩個引數分別表示輸入影象和輸出影象,第三個引數ddepth表示的是影象的深度,如果這個值設定為負數,則這個影象的深度與輸入的源影象的深度相同,否則就需要根據源影象的深度進行相關的設定

例如,若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;
}