OpenCV4学习笔记(16)——图像锐化(USM锐化算法)

本次要整理的内容是关于图像的锐化。
图像锐化,本质上是对图像高通滤波后的结果和原图进行像素叠加后的输出。图像的高通滤波,就是一幅图像通过滤波器后,使其细节、边缘部分都保留下来,而其他的主体内容则过滤掉。所以图像进行高通滤波后的输出结果就是图像的细节部分,再将这些细节部分以一定的权重叠加到原图像中去,就使得原图像中的细节部分更为突出,视觉感受上更加的立体。
下面是图像锐化的代码实现:

	Mat sharpen_image, blur_image, sub_image;

	//GaussianBlur(image, blur_image, Size(), 5, 5);
	fastNlMeansDenoisingColored(image, blur_image, 10, 10, 7, 21);			
	
	subtract(image, blur_image, sub_image, Mat(), CV_16S);
	
	add(sub_image, image, sharpen_image, Mat(), CV_16S);
	convertScaleAbs(sharpen_image, sharpen_image);
	imshow("锐化", sharpen_image);

首先通过滤波算法对输入图像去噪,同时模糊掉其边缘和细节部分,可以使用非局部均值滤波算法来进行去噪,因为非局部均值滤波算法对高斯噪声的抑制效果比较好。然后通过用原图像减去模糊后的图像,得到本来被模糊掉的边缘和细节部分,注意两个CV_8UC类型相减,可能出现负值,所以相减后的输出图像用CV_16S或者更大范围的类型定义。最后将相减得到的边缘和细节部分,合成到原图中,再通过convertScaleAbs(sharpen_image, sharpen_image)这个API将其他类型的图像转换为CV_8UC类型,并输出图像。下面展示一下利用这种方式进行图像锐化的效果图:

在这里插入图片描述
上面的锐化实现方式,是将提取到的细节边缘信息完全地合成到原图像中,这样 形成的锐化图像虽然也能达到效果,但是可能是因为将细节过于突出的原因,导致看起来会给人很粗糙、不自然的感觉。尤其是在上面的效果图中,经过这样锐化的图像观感上很生硬,甚至显得有些毛刺,和原图对比起来显得有些不舒服。我们可以通过使用USM(UnSharpen Mask)锐化算法来解决这个问题。USM锐化算法的思路是,先对原图像进行高斯模糊达到降噪、抹除细节边缘的效果,并获得处理后的图像,再将经过高斯模糊后的图像与原图像按照权重进行加权求和。
可以认为:输出图像 = (1 + weight)x 原图像 + (-weight)x 高斯模糊图像
这样处理后,通过一个权重来平衡了叠加的高频信息在输出图像中的比例,可以让图像进行锐化后的显示过渡仍然比较流畅、平滑,避免过多的高频信息影响图像的显示效果。下面是USM算法是代码实现:

	//USM(UnSharpen Mask)锐化算法
	Mat gaussian_image, USM_image;
	GaussianBlur(image, gaussian_image, Size(), 10, 10);
	addWeighted(image, 1.5, gaussian_image, -0.5, 0, USM_image, CV_16S);
	convertScaleAbs(USM_image, USM_image);
	imshow("USM_image", USM_image);

在这里使用了addWeighted(image, 1.5, gaussian_image, -0.5, 0, USM_image, CV_16S)这个API,它的作用是将输入的两幅图像按照各自的权重进行相加,并注意要以比[0,255]范围更大的类型(如CV_16S)返回。
下面是USM算法的效果图:
在这里插入图片描述
从我的主观感受上来看,USM算法是明显优于普通锐化算法的,无论是在细节、边缘、纹理、图像内容之间的过渡等等方面,都有比较好的表现。
但是无论是普通的锐化算法,或者是USM算法,都有一个问题就是:没有考虑到轮廓边缘与物体内部纹理的区别。在上述的两个图像锐化算法中,图像中的所有高频信息都得到了一样的加强,以效果图中的猫和其他物体为例:猫作为主体和其他物体之间的边缘是需要被加强的,这样可以显得图像更立体化,增强视觉效果,但是其他物体表面的那些纹理是不需要被同等强度地加强的,否则会削弱图像主体的显示效果,甚至是被其他的纹理所充斥。所以在图像锐化处理中,最好是能够对主体边缘进行比较大的增强,而对其他纹理则进行比较小的增强,这样才能达到比较好的视觉效果。
动态USM锐化算法就实现了这样的功能,下面给出我自定义的一个USM动态算法:

/************************************动态USM锐化算法***********************************/
	Mat gaussian_image, USM_image;
	//cvtColor(image, USM_image, COLOR_BGR2GRAY);
	//cvtColor(image, image, COLOR_BGR2GRAY);
	GaussianBlur(image, gaussian_image, Size(), 20, 20);
	int height = image.rows;
	int width = image.cols;
	int channels = image.channels();
	//定义两个阈值标识,将[0,255]分为三个区间
	int threshold_L = 60;
	int threshold_R = 120;
	//分别定义三个像素值区间的权重
	float weight_L = 0.4;	
	float weight_M = 0.6;
	float weight_R = 0.8;	
	//初始化输出图像
	USM_image = image.clone();
	if (channels != 1)			//非单通道图像
	{
		//将原图像、高斯模糊图像、USM输出图像进行三通道分离
		vector<Mat> image_bgr, gaus_image_bgr, USM_bgr;
		split(image, image_bgr);
		split(gaussian_image, gaus_image_bgr);
		split(USM_image, USM_bgr);
		//分别对每个通道进行像素点遍历
		for (int ch = 0; ch < channels; ch++)
		{
			for (int row = 0; row < height; row++)
			{
				for (int col = 0; col < width; col++)
				{
					//计算原图像和高斯模糊图像在该点的像素差异值
					uchar image_value = image_bgr[ch].at<uchar>(row, col);
					uchar gaus_image_value = gaus_image_bgr[ch].at<uchar>(row, col);
					int dif_value = image_value - gaus_image_value;
					if (dif_value < threshold_L)			//差异值位于第一区间[0, 60]
					{
						//将原图像的像素值和高斯模糊图像的像素值以权重相加
						uchar new_value = saturate_cast<uchar>(((1 + weight_L) * image_value - weight_L * gaus_image_value));
						//将新的像素值赋给当前通道的当前位置的像素点
						USM_bgr[ch].at<uchar>(row, col) = new_value;
					}
					else if(dif_value >= threshold_L && dif_value <= threshold_R)			//差异值位于第二区间[60, 120]
					{
						uchar new_value = saturate_cast<uchar>(((1+weight_M) * image_value - weight_M * gaus_image_value));
						USM_bgr[ch].at<uchar>(row, col) = new_value;
					}
					else			//差异值位于第三区间[60, 255]
					{
						char new_value = saturate_cast<uchar>(((1 + weight_R) * image_value - weight_R * gaus_image_value));
						USM_bgr[ch].at<uchar>(row, col) = new_value;
					}
				}
			}
		}
		merge(USM_bgr, USM_image);					//将分别锐化处理后的三通道图像合并
	}
	else			//单通道图像
	{
		for (int row = 0; row < height; row++)
		{
			for (int col = 0; col < width; col++)
			{
				uchar image_value = image.at<uchar>(row, col);
				uchar gaus_image_value = gaussian_image.at<uchar>(row, col);
				int dif_value = image_value - gaus_image_value;
				if (dif_value < threshold_L)
				{
					uchar new_value = saturate_cast<uchar>(((1 + weight_L) * image_value - weight_L * gaus_image_value));
					USM_image.at<uchar>(row, col) = new_value;
				}
				else if (dif_value >= threshold_L && dif_value <= threshold_R)
				{
					uchar new_value = saturate_cast<uchar>(((1 + weight_M) * image_value - weight_M * gaus_image_value));
					USM_image.at<uchar>(row, col) = new_value;
				}
				else
				{
					uchar new_value = saturate_cast<uchar>(((1 + weight_R) * image_value - weight_R * gaus_image_value));
					USM_image.at<uchar>(row, col) = new_value;
				}
			}
		}
	}
	imshow("USM_image", USM_image);
	//imwrite("D:\\opencv_c++\\opencv_tutorial\\data\\images\\USM_flower2.jpg", USM_image);

在上述算法中,对输入图像进行遍历,并将原图像和经过高斯模糊后的图像进行对比,对像素值差异大于阈值的就进行锐化处理。并且将差异值分为三个区间进行处理, 对差异大的区间使用较大的权重,更突出其边缘细节,对差异小的区间则用较小的权重,避免过度增强。如果输入的是三通道图像,则先进行通道分离后对不同通道进行锐化处理,最后再将三通道合并起来。
下面是动态USM算法的效果图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
处理上面的算法实现图像锐化外,还可以通过自定义卷积来实现图像锐化

	//通过自定义卷积核来进行图像锐化,以下为两个常见锐化算子
	Mat sharpen = (Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);	
	//Mat sharpen = (Mat_<int>(3, 3) << -1, -1, -1, -1, 9, -1, -1, -1, -1);	
	filter2D(image, sharpen_image, CV_32F, sharpen);
	convertScaleAbs(sharpen_image, sharpen_image);
	imshow("锐化", sharpen_image);

上面给出了两个常见的锐化算子,是通过卷积计算梯度的方式来实现图像的锐化,但是效果其实并不是很理想,这里就不做展示了。
其中filter2D(image, sharpen_image, CV_32F, sharpen)是用来实现一幅图像和一个卷积核进行卷积操作的API,卷积核需要提前定义好,并且输出的类型是浮点型的(如CV_32F),卷积完成后再将输出图像归一化到 [ 0 , 255 ] 区间中,OpenCV只有像素值是在 [ 0 , 255 ] 区间的图像才能够正常的显示。

好了,这次关于图像锐化的内容就记录到这里,而且也满足了上一篇博客中想换下实验图片的心愿。。。下次再继续把~~~

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

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

猜你喜欢

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