OpenCV4学习笔记(12)——图像基本模糊操作

本次要记录的是图像的三种基本模糊操作:均值模糊、高斯模糊和中值模糊。

  • 均值模糊
    均值模糊的原理是通过一个卷积核,在图像上进行移动,对每次覆盖的像素范围进行求平均值,然后将求得的平均值赋值给当前卷积核中心位置处的像素点。经过卷积核对一整幅图像的多次平移,就得到了一幅均值模糊过后的图像,该图像上的每个像素点的值,都是其邻域内所有像素点的平均值。均值模糊能够抹除图像的边缘和细节,对噪声也有一定的抑制作用,但是它作为一种最最最基础的模糊操作,实现简单的同时,其效果也是麻麻嘚。下面是均值模糊的自定义[3x3]卷积操作实现,代码如下:
	int height = image.rows;
	int width = image.cols;
	Mat blur_image = image.clone();
	for (int row = 1; row < height - 1; row++)
	{
		for (int col = 1; col < width - 1; col++)
		{
			Vec3b p1 = image.at<Vec3b>(row - 1, col - 1);
			Vec3b p2 = image.at<Vec3b>(row - 1, col);
			Vec3b p3 = image.at<Vec3b>(row - 1, col + 1);
			Vec3b p4 = image.at<Vec3b>(row, col - 1);
			Vec3b p5 = image.at<Vec3b>(row, col);
			Vec3b p6 = image.at<Vec3b>(row, col + 1);
			Vec3b p7 = image.at<Vec3b>(row + 1, col - 1);
			Vec3b p8 = image.at<Vec3b>(row + 1, col);
			Vec3b p9 = image.at<Vec3b>(row + 1, col + 1);

			int b = p1[0] + p2[0] + p3[0] + p4[0] + p5[0] + p6[0] + p7[0] + p8[0] + p9[0];
			int g = p1[1] + p2[1] + p3[1] + p4[1] + p5[1] + p6[1] + p7[1] + p8[1] + p9[1];
			int r = p1[2] + p2[2] + p3[2] + p4[2] + p5[2] + p6[2] + p7[2] + p8[2] + p9[2];

			blur_image.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b / 9);
			blur_image.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g / 9);
			blur_image.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r / 9);
		}
	}

通过两个for循环对所有像素点进行遍历,并获取某点及其邻域内所有像素点的像素值,然后求和再取平均,就得到了该点的新的像素值。最后将新的像素值赋给输出图像上该坐标的像素点,遍历完成后,就得到了对原图像进行均值模糊操作后的图像。
注意的是,for (int row = 1; row < height - 1; row++)for (int col = 1; col < width - 1; col++)这两个循环的起始值,是从1开始,而终止值是对应的行列数-1。这是因为对于大小为ksize的卷积核来说,对图像进行处理时中心位置无法放置到边缘的行列上,所以输出图像的边缘行列上的像素值是不会被修改的,修改的行数为height-ksize+1,列数为width-ksize+1。
在OpenCV中也有一个简单的API来实现均值模糊,代码如下:

	Mat blur_image;
	blur(image, blur_image, Size(3, 3), Point(-1, -1), 4);
	//Point(-1, -1): 默认锚点 ;    borderType=4:  默认边缘填充方式  
	imshow("blue", blur_image);

其中的blur(image, blur_image, Size(3, 3), Point(-1, -1), 4)就实现了一个均值模糊操作,第一个参数是输入的需要均值模糊的图像,第二个参数是输出图像,第三个参数卷积核的尺寸,可以控制模糊的程度,第四个参数是锚点坐标,使用默认值(-1,-1)就是默认中心位置为锚点,第五个参数边缘的填充方式,也就是上面所说卷积操作时对边缘无法进行,所以要进行填充,使用默认填充方式即可。
下面可以看一下均值模糊后的效果:
在这里插入图片描述
经过对比可以看出,均值模糊操作后的图像在细节上减少了许多,多了一些朦胧感。

  • 高斯模糊
    高斯模糊也是一种基本的模糊操作,和均值模糊不同的是,高斯模糊是对某一像素点的邻域内所有像素点进行加权求和,能够比较好的保留中心像素点对图像的影响。高斯模糊的效果相比起均值模糊,要更好一些,而且在抑制噪声方面的能力也更强,尤其是对于高斯噪声的抑制效果比较好。下面是高斯模糊的实现,代码如下:
	Mat GaussianBlur_imagex, GaussianBlur_imagey;
	GaussianBlur(image, GaussianBlur_imagex, Size(), 1, 10, 4);
	imshow("GaussianBlur_imagex", GaussianBlur_imagex);
	GaussianBlur(image, GaussianBlur_imagey, Size(), 10, 1, 4);
	imshow("GaussianBlur_imagey", GaussianBlur_imagey);
	imshow("image", image);

OpenCV提供了GaussianBlur(image, GaussianBlur_image, Size(), 1, 10, 4)这个API来实现高斯模糊,第一个参数是输入图像;第二个参数是输出图像;第三个参数是高斯模糊卷积核的尺寸,当Size不为空时,由Size计算sigma;当Size为空时,由后续sigma_x和sigma_y计算;第四、五个参数分别是x方向和y方向的sigma值;最后一个参数是边缘填充方式。
下面两张图,左边是设置sigmax=10、sigmay=1的效果,右边是设置sigmax=1、sigmay=10的效果:
在这里插入图片描述
可见如果sigmax设置比较大,在纵向的模糊程度比较大,横向的模糊程度比较小;如果sigmay设置比较大,则在横向的模糊程度比较大,纵向的模糊程度比较小。(可以细看两幅图像中上一级台阶的差异)

日常使用中,高斯模糊的使用频率是比较大的,相对于均值滤波来说,经常的再对图像进行预处理时都会使用一下高斯模糊来过滤掉高斯噪声。

  • 中值滤波
    中值滤波也是一种常见的模糊操作,但相比前面两种方式,中值滤波主要是用于针对过滤椒盐噪声的,因为它对椒盐噪声的抑制效果非常的好。其原理是,同样以一个窗口在原图像中移动,然后获取该窗口覆盖下的所有像素值的中值,这便是该窗口中心位置的新的像素值。下面给出3x3中值滤波的实现:
int height = image.rows;
	int width = image.cols;
	Mat median_blur_image = image.clone();
	vector<Mat> bgr;
	split(image, bgr);
	//对三通道图逐一进行中值滤波,最后合并
	for (int ch = 0; ch < 3; ch++)
	{
		for (int row = 1; row < height - 1; row++)
		{
			for (int col = 1; col < width - 1; col++)
			{
				uchar pixel[9];
				pixel[0] = bgr[ch].at<uchar>(row - 1, col - 1);
				pixel[1] = bgr[ch].at<uchar>(row - 1, col);
				pixel[2] = bgr[ch].at<uchar>(row - 1, col + 1);
				pixel[3] = bgr[ch].at<uchar>(row, col - 1);
				pixel[4] = bgr[ch].at<uchar>(row, col);
				pixel[5] = bgr[ch].at<uchar>(row, col + 1);
				pixel[6] = bgr[ch].at<uchar>(row + 1, col - 1);
				pixel[7] = bgr[ch].at<uchar>(row + 1, col);
				pixel[8] = bgr[ch].at<uchar>(row + 1, col + 1);
				uchar temp;
				//对9个像素点的灰度值排序
				for (int i = 0; i < 9-1; i++)
				{
					for (int j = 0; j < 9 - i-1; j++)
					{
						if (pixel[i] < pixel[i+1])
						{
							temp = pixel[i];
							pixel[i] = pixel[i + 1];
							pixel[i + 1] = temp;
						}
					}
					
				}
				//取中值
				bgr[ch].at<uchar>(row, col) = pixel[4];
			}
		}
	}
	merge(bgr, median_blur_image);

上述代码中,首先我将一幅RGB图像分离成三幅单通道图,然后分别对通道、行、列进行for循环。通过遍历,获取每个点邻域内九个像素点的值,用一个数组存放起来,在进行一次冒泡排序,得到一个从大到小排列的数组。最后取出该数组的中值就是该点的新的像素值。循环结束,就完成了对图像的一次中值滤波。

同样在OpenCV中也提供了一个专门的API来实现中值滤波:

	Mat median_blur_image;
	medianBlur(image, median_blur_image, 3);
	//ksize必须为奇数
	imshow("median_blur_image", median_blur_image);

就是APImedianBlur(image, median_blur_image, 3),第一和第二个参数分别是输入和输出图像,第三个参数就是移动窗口的大小,注意这个ksize参数必须为奇数,这是该函数参数规定的,如果传入值不是奇数的话就会报错,应该是为了便于找到窗口的中心位置。
通过这个API,我们就可以简单的实现中值滤波了,可以帮助我们对图像进行预处理,消除图像中的椒盐噪声。当然了,我个人觉得中值滤波最好是自己实现一遍,而不是单纯的调用API,因为在中值滤波的实现过程中,不仅需要对像素进行操作,同时还需要对像素值进行排序,其中就涉及到排序算法的问题,使用好的排序算法对运行速度的影响是非常大的。
中值滤波的效果如下:
在这里插入图片描述
由于原图像并没有椒盐噪声,所以看起来效果就只是略微的模糊而已,而且对比之前的模糊操作,可以看出中值模糊还带有一点点边缘保留的效果。

好了,本次记录到此结束~

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

发布了36 篇原创文章 · 获赞 43 · 访问量 1814

猜你喜欢

转载自blog.csdn.net/weixin_45224869/article/details/104595794