OpenCV4入门教程054:USM锐化

索引地址:系列索引

图像锐化原理

图像锐化,是使图像边缘更加清晰的一种图像处理方法,说白了就是让图像的边缘看起来更加清晰明了。其原理主要就是利用图像的高频分量来实现的,将原图像的高频分量提取出来,再和原图像按一定规则叠加起来,最终得到的图像就是锐化后的图像。提取高频分量可以有如下两种方式:

1.直接使用高通滤波器来提取图像高频分量,如Sobel算子、Laplace算子、梯度Prewitt算子等。其中Sobel提取出来的边缘比较粗糙,Laplace提取出来的边缘更加细腻,因此也更加适合用于做锐化滤波器;

2.先使用低通滤波器对图像进行滤波处理,得到图像中的低频分量,再用原图像减去滤波得到的低频分量图像得到高频分量,最后将高频分量和原图像叠加得到锐化后的图像。这种方法也称为非锐化掩模(USM),我们常使用低通滤波器(高斯、双边)对图像进行滤波,这种方法的滤波器很好控制(包括大小和强弱),从而可以灵活控制高频分量的强弱。

USM锐化增强算法

数字图像处理中关于图像增强算法有两种常用的技术:非锐化掩蔽和高提升滤波

g=f+km. k>1高提升滤波=1非锐化掩蔽<1不强调非锐化模板的贡献g=f+k \cdot m. \ k \begin{aligned} \gt 1 & \text{高提升滤波} \\ = 1 & \text{非锐化掩蔽} \\ < 1 & \text{不强调非锐化模板的贡献} \end{aligned}

g表示锐化后的图像,f表示原图像,k表示锐化系数,m为高频分量图像。

非锐化掩模(USM)

其原理如上面所说,步骤如下:

1、首先使用低通滤波器平滑原图像得到低频分量图像:s = f(低通滤波);
2、原图像减去低频分量图像,产生的差值,高频分量图像称为模板:m = f - s;
3、将模板图像乘以一个系数,加到原图像中,得到锐化后的图像:g = f + k × m

传统的锐化算法通过获取高频分量然后与原图像叠加做锐化,这种方法虽然效果很明显,但是容易出现噪声增强的现象,而且很容易在图像纹理的边缘处产生明亮的伪边缘。

可以看出,在lena的帽子处和头发边缘均有一定的白色伪边缘产生,而且锐化后的图像还出现了许多噪声,使得图像的整体锐化效果变得很差。

改进的非锐化掩模(USM)

USM锐化算法得到的图像容易出现噪声及伪边缘,那么,如果能够做到只针对图像中的边缘部分做锐化处理,是不是就可以在保证锐化效果的前提下最大程度的保留原图中的非噪声信息呢?要实现这个功能,我们需要设置一个阈值,将原图像与滤波后得到的高频分量图像做差值,再与阈值比较,即可得到想要的效果。具体步骤如下:

1.先对原图像src做高斯滤波得到滤波图像BlurImg,src(高斯模糊) -> BlurImg;
2.原图像src与滤波图像做差值,再与阈值Threshold比较,结果存放在掩模Mask中,Mask = abs( src - BlurImg ) < Threshold ? 1 : 0;
3.将src与高频分量图像按一定系数叠加,得到锐化图像,SharpenImg = src + k × ( src - BlurImg );
4.将src中Mask对应的非0部分复制到SharpenImg 中;

经过以上几步简单操作就可以得到优化后的USM锐化图像了。

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include "opencv2/opencv.hpp"
#include <iostream>

using namespace std;
using namespace cv;

#define SharpenFactor 1 //锐化系数
#define SharpenThs 30 //锐化阈值

void ImproveUSM(Mat src, Mat BlurImg, int Threshold, float Factor) {
Mat DiffMask, dst;
DiffMask = Mat::zeros(src.rows, src.cols, src.type());
if (src.channels() == 1) //灰度单通道
{
for (int i = 0; i < src.rows; i++) {
for (int j = 0; j < src.cols; j++) {
int Value_diff = abs(src.at<uchar>(i, j) - BlurImg.at<uchar>(i, j));
if (Value_diff < Threshold) //小于阈值说明非边缘,不需要锐化
DiffMask.at<uchar>(i, j) = 1;
else
DiffMask.at<uchar>(i, j) = 0;
}
}
} else if (src.channels() == 3) //三通道BGR
{
int Value_diff[ 3 ] = {0};
for (int i = 0; i < src.rows; i++) {
for (int j = 0; j < src.cols; j++) {
for (int k = 0; k < 3; k++) {
Value_diff[ k ] = abs(src.at<Vec3b>(i, j)[ k ] - BlurImg.at<Vec3b>(i, j)[ k ]);

if (Value_diff[ k ] < Threshold) //小于阈值说明非边缘,不需要锐化
DiffMask.at<Vec3b>(i, j)[ k ] = 1;
else
DiffMask.at<uchar>(i, j) = 0;
}
}
}
}
addWeighted(src, 1 + Factor, BlurImg, -Factor, 0,
dst); //将两幅图按权重系数融合,dst = src + Factor(src - BlurImg)
src.copyTo(dst, DiffMask); //将src中DiffMask对应的非0部分复制到dst中
imshow("ImproveUSM", dst);
}

int main() {
int Ths = SharpenThs;
float Fac = SharpenFactor;
Mat src = imread("lena.jpg");
if (src.empty()) {
printf("could not load image...\n");
return -1;
}
namedWindow("input", WINDOW_AUTOSIZE);
imshow("input", src);

Mat blur_img, USMsharpen_img, sharpen_img, sharpen_kernel;
sharpen_kernel = (Mat_<float>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
filter2D(src, sharpen_img, CV_32F, sharpen_kernel);
convertScaleAbs(sharpen_img, sharpen_img);
imshow("sharpen", sharpen_img);
GaussianBlur(src, blur_img, Size(9, 9), 0, 0);
addWeighted(src, 1 + Fac, blur_img, -Fac, 0, USMsharpen_img);
imshow("USMsharpen", USMsharpen_img);
//使用自己写的改进版本的USM锐化
ImproveUSM(src, blur_img, Ths, Fac);

waitKey(0);
return 0;
}

usm

右下角的图像即为优化后的USM锐化图像,对比未优化的USM锐化图像可以看出,在提高锐化效果的基础上尽可能的降低了噪声和伪边缘现象。


OpenCV4入门教程054:USM锐化
https://feater.top/opencv/opencv-usm-filter/
作者
JackeyLea
发布于
2020年10月6日
许可协议