OpenCV(4)-圖像掩碼操作(卷積)--平滑處理


卷積定義

矩陣的掩碼操作即對圖像進行卷積。對圖像卷積操作的意義為:鄰近像素對(包括該像素自身)對新像素的影響;影響大小取決於卷積核對應位置值得大小。

例如:圖像增強可以使用

\[I(i,j)=5*I(i,j)-[I(i-1,j) + I(i+1,j) + I(i, j-1) + I(i, j+1)] \]

用代碼實現

 void Sharpen(const Mat& myImage, Mat& Result)
{
	CV_Assert(myImage.depth() == CV_8U);  // 僅接受uchar圖像
	Result.create(myImage.size(), myImage.type());
	const int nChannels = myImage.channels();

	for (int j = 1; j < myImage.rows - 1; ++j)//從第一行開始,而不是0;到rows-2行結束
	{
		const uchar* previous = myImage.ptr<uchar>(j - 1);//上一行
		const uchar* current = myImage.ptr<uchar>(j);
		const uchar* next = myImage.ptr<uchar>(j + 1);//下一行

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

		for (int i = nChannels; i < nChannels*(myImage.cols - 1); ++i)
		{
			*output++ = saturate_cast<uchar>(5 * current[i]
				- current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]);
		}
	}

	//邊緣處理。邊緣沒有卷積,所以邊緣賦值為原圖像對應像素的值。

	//先處理上和下
	uchar* output_top = Result.ptr<uchar>(0);
	const uchar* origin_top = myImage.ptr<uchar>(0);
	uchar* output_bottom = Result.ptr<uchar>(myImage.rows - 1);
	const uchar* origin_bottom = Result.ptr<uchar>(myImage.rows - 1);
	for (int i = 0; i < nChannels * myImage.cols - 1; ++i){
		*output_top++ = *origin_top++;
		*output_bottom++ = *origin_bottom++;
	}

	//左和右 分別有nChannel
	for (int i = 0; i < myImage.rows; ++i){
		const uchar* origin_left = myImage.ptr<uchar>(i);
		uchar* output_left = Result.ptr<uchar>(i);
		const uchar* origin_right = origin_left + nChannels*(myImage.cols - 1);
		uchar* output_right = output_left + nChannels*(myImage.cols - 1);
		for (int j = 0; j < nChannels; ++j){
			*output_left++ = *origin_left++;
			*output_right++ = *origin_right++;
		}
	}
}

卷積在圖像處理中應用很廣泛,可以使用OpenCV自帶函數

void filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int borderType=BORDER_DEFAULT )

image

濾波器

濾波器中,所有元素和相加后應該等於1。如果大於1,則圖像會變亮,如果小於1,圖像變暗。

平滑濾波器

平滑濾波器的作用是去除圖像中一些不重要的細節,減小噪聲。可以分為線性和非線性2大類:
1、線性平滑濾波器:均值波濾器
2、非線性平滑濾波器:最大值濾波、中值濾波、最小值濾波。

線性平滑濾波

線性平滑濾波可以減小“尖銳”,減小噪聲;因為圖像邊緣是有“尖銳”變換引起的,所以也就模糊了邊緣。

\[ \frac{1}{9}\left[ \begin{matrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{matrix} \right] \tag{3} \]

上面這個濾波器,各個像素的值貢獻相同,是均值濾波,在OpenCV有對應函數。

void blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )

有時各個像素的重要性不同,可以使用:

\[ \frac{1}{16}\left[ \begin{matrix} 1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 2 & 1 \end{matrix} \right] \tag{3} \]

上面的濾波器表面中間和其挨着的像素貢獻大一些。

非線性濾波

線性濾波是對濾波器區域內各個像素點加權求和,結果和原圖像濾波區域有一個線性關系。非線性濾波則不然;基於統計排序的非線性濾波分為:最大值濾波、中值濾波、最小值濾波。顧名思義,上述三種濾波器是分別取卷積核區域內最大像素點值、中值像素點值、最小像素點值。

中值濾波:

中值濾波是對卷積核區域內像素排序,取中位數,只有奇數才有中位數,所有卷積核只能為3、5、7……這樣的奇數。中值濾波在去除噪聲的同時,可以比較好的保留圖像細節(尖銳等),能夠有效去除脈沖噪聲(椒鹽噪聲)。

void medianBlur(InputArray src, OutputArray dst, int ksize)

高斯濾波:卷積核是按照高斯分布的

\[G(x,y)=Ae^{\frac{-(x-\mu_x)^2}{2\sigma_x^2}+\frac{-(x-\mu_y)^2}{2\sigma_y^2}} \]

和均值像素差值大的像素,對結果貢獻較小;離均值“近”的像素,對結果貢獻較大。
函數為:

void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT )

雙邊濾波:

前面提高的濾波都是知識考慮了圖像的空間關系,即卷積核區域內的空域關系,沒有考慮像素間的關系。因此在平滑時,會模糊整幅圖像;雙邊濾波不僅考慮了圖像的空域關系,還考慮了圖像間像素值的差異關系。可以參考這里
OpenCV對應函數

void bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT )

實驗代碼

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

void Sharpen(const Mat& myImage, Mat& Result)
{
	CV_Assert(myImage.depth() == CV_8U);  // 僅接受uchar圖像

	Result.create(myImage.size(), myImage.type());
	const int nChannels = myImage.channels();

	for (int j = 1; j < myImage.rows - 1; ++j)//從第一行開始,而不是0;到rows-2行結束
	{
		const uchar* previous = myImage.ptr<uchar>(j - 1);//上一行
		const uchar* current = myImage.ptr<uchar>(j);
		const uchar* next = myImage.ptr<uchar>(j + 1);//下一行

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

		for (int i = nChannels; i < nChannels*(myImage.cols - 1); ++i)
		{
			*output++ = saturate_cast<uchar>(5 * current[i]
				- current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]);
		}
	}

	//邊緣處理。邊緣沒有卷積,所以邊緣賦值為原圖像對應像素的值。

	//先處理上和下
	uchar* output_top = Result.ptr<uchar>(0);
	const uchar* origin_top = myImage.ptr<uchar>(0);
	uchar* output_bottom = Result.ptr<uchar>(myImage.rows - 1);
	const uchar* origin_bottom = Result.ptr<uchar>(myImage.rows - 1);
	for (int i = 0; i < nChannels * myImage.cols - 1; ++i){
		*output_top++ = *origin_top++;
		*output_bottom++ = *origin_bottom++;
	}

	//左和右 分別有nChannel
	for (int i = 0; i < myImage.rows; ++i){
		const uchar* origin_left = myImage.ptr<uchar>(i);
		uchar* output_left = Result.ptr<uchar>(i);
		const uchar* origin_right = origin_left + nChannels*(myImage.cols - 1);
		uchar* output_right = output_left + nChannels*(myImage.cols - 1);
		for (int j = 0; j < nChannels; ++j){
			*output_left++ = *origin_left++;
			*output_right++ = *origin_right++;
		}
	}
}

int main(int argc, char* argv[]){
	const char* path = "";
	Mat img = imread(path);
	if (img.empty())
	{
		cout << "error";
		return -1;
	}
	imshow("原圖像", img);

	//均值濾波
	Mat blur_mat;
	blur(img, blur_mat, Size(3, 3));
	imshow("均值濾波", blur_mat);

	//線性非均值濾波
	Mat kern = (Mat_<float>(3, 3) << 1, 2, 1,
		2, 4, 2,
		1, 2, 1) / 16;
	Mat blur2_mat;
	filter2D(img, blur2_mat, img.depth(), kern);
	imshow("線性非均值濾波", blur2_mat);

	//中值濾波
	Mat memedian_mat;
	medianBlur(img, memedian_mat, 3);
	imshow("中值濾波", memedian_mat);

	//高斯濾波
	Mat Gaussian_mat;
	GaussianBlur(img, Gaussian_mat, Size(3, 3), 0, 0);
	imshow("高斯濾波", Gaussian_mat);

	//雙邊濾波

	Mat bilateral_mat;
	bilateralFilter(img, bilateral_mat, 25, 25 * 2, 25 / 2);
	imshow("雙邊濾波", bilateral_mat);
	waitKey();
	return 0;

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM