文章目录
前言
图像模糊是图像处理中非常常见的操作之一,用于减少图像中的噪声和细节,使图像看起来更加平滑。OpenCV 提供了多种模糊技术,如均值模糊、高斯模糊、中值模糊和双边滤波等。本篇博客将介绍几种常用的图像模糊方法,并通过代码示例演示如何在 OpenCV 中实现这些方法,最后附上完整代码以便学习使用。
1.图像卷积
卷积是图像处理中用来实现模糊、锐化等效果的基础操作。它的工作原理是将一个小的核(通常称为滤波器)在图像上滑动,通过核与图像的局部区域进行元素级的乘积和求和,从而计算出新的像素值。卷积操作可以用公式表达为:
G
(
x
,
y
)
=
∑
i
=
−
k
k
∑
j
=
−
k
k
K
(
i
,
j
)
⋅
I
(
x
+
i
,
y
+
j
)
G(x, y) = \sum_{i=-k}^k \sum_{j=-k}^k K(i, j) \cdot I(x+i, y+j)
G(x,y)=i=−k∑kj=−k∑kK(i,j)⋅I(x+i,y+j)
其中:
- G(x, y) 是输出图像的像素值。
- K(i, j) 是卷积核的值。
- I(x+i, y+j) 是输入图像的像素值。
下面这张图演示了一个 3x3 的卷积核应用于图像不同位置的过程:
①左图:卷积核覆盖的区域以红色和黄色高亮显示。红色部分是当前像素,黄色部分是卷积核作用的周围像素。
②中图:卷积核向下移动一行,覆盖新区域。
③右图:卷积核继续移动,覆盖新的区域。
通过卷积,图像中的每个像素都会根据其周围像素的值重新计算,从而实现模糊效果。
2.图像读取
读取图像是下面图像模糊处理的基础,本实验使用了3张图片,有需要可以下载使用。
这一部分不太了解建议从头开始学习[OpenCV] 数字图像处理 C++ 学习——01图像的读取、加载和保存附完整代码(小白入门篇)
图片链接图片1 sherlock.jpg
图片2 椒盐噪声.png
图片3 Bilateral.jpg
cv::Mat image;
image = imread("sherlock.jpg");
if (image.empty()) {
printf("could not find the image...\n");
return;
}
namedWindow("input image", cv::WINDOW_AUTOSIZE);
cv::imshow("input image", image);
cv::Mat gege;
gege = imread("椒盐噪声.png");
if (gege.empty()) {
printf("could not find the image...\n");
return;
}
cv::imshow("gege", gege);
cv::Mat Bilateral;
Bilateral = imread("Bilateral.jpg");
if (Bilateral.empty()) {
printf("could not find the image...\n");
return;
}
cv::imshow("Bilateral", Bilateral);
3.均值模糊 (Mean Blurring)
均值模糊是一种简单的图像平滑技术,通过取窗口内所有像素的平均值来替换中心像素的值。它能有效去除图像中的噪声,但也会使边缘变得模糊。均值模糊可以使用 cv::blur()
函数实现。
G
(
x
,
y
)
=
1
(
2
k
+
1
)
2
∑
i
=
−
k
k
∑
j
=
−
k
k
I
(
x
+
i
,
y
+
j
)
G(x, y) = \frac{1}{(2k+1)^2} \sum_{i=-k}^k \sum_{j=-k}^k I(x+i, y+j)
G(x,y)=(2k+1)21i=−k∑kj=−k∑kI(x+i,y+j)
$G(x, y) $是输出图像在位置 (x, y)的像素值。
I ( x + i , y + j ) I(x+i, y+j) I(x+i,y+j)是输入图像在位置 ( x + i , y + j ) (x+i, y+j) (x+i,y+j)的像素值。
k 是卷积核的半径,卷积核大小为 ( 2 k + 1 ) × ( 2 k + 1 ) (2k+1)×(2k+1) (2k+1)×(2k+1)。
例如一个3*3的模板:
均值模糊代码实现(cv::blur())
cv::blur()
函数来实现均值模糊,使用一个 5x5 的内核,对图像进行简单的平滑处理,适用于一般的噪声去除。
//均值模糊
cv::Mat meanBlurredImage;
cv::blur(image, meanBlurredImage, cv::Size(5, 5)); // 使用 5x5 的内核进行均值模糊
cv::imshow("meanBlurredImage", meanBlurredImage);
结果
4.高斯模糊 (Gaussian Blurring)
高斯模糊是通过高斯核函数对图像进行卷积来实现的。它能够有效地平滑图像,同时减少边缘的模糊程度。高斯模糊是通过在模糊过程中给像素分配不同的权重来实现的。OpenCV 提供的 cv::GaussianBlur()
函数可以用来实现高斯模糊。
①左侧图像展示了 1D 高斯分布函数,其中 G(x)表示一个标准的高斯曲线。曲线的高度由标准差 σ控制。标准差越大,曲线越平缓,模糊效果越明显;标准差越小,曲线越尖锐,模糊效果越弱。
②右侧图像展示了 2D 高斯分布的形状,它是 1D 高斯函数在二维空间中的扩展。这里 G(x,y) 描述了一个高斯核的形状,核的中心有最高的权重(离中心越近的像素对结果影响越大),四周逐渐减少。高斯核通过与图像进行卷积操作,使图像达到平滑的效果。
高斯模糊代码实现 (cv::GaussianBlur())
高斯模糊采用高斯函数计算加权平均值,更强调中心像素的影响,从而有效地减少噪声并保留一定的边缘特性。
//高斯模糊
cv::Mat gaussianBlurredImage;
cv::GaussianBlur(image, gaussianBlurredImage, cv::Size(5, 5), 0.5); // 使用 5x5 的高斯内核和标准差为 0.5 进行高斯模糊
cv::imshow("gaussianBlurredImage", gaussianBlurredImage);
结果
左图为标准差0.5高斯模糊,右图为标准差3.0高斯模糊,右侧图像比左侧图像更模糊,所以标准差越大,曲线越平缓,模糊效果越明显。
5.中值模糊 (Median Blurring)
中值模糊通过在窗口内取像素值的中值来替换中心像素值,适合去除“椒盐噪声”。与均值模糊相比,中值模糊能更好地保留图像的边缘信息。使用 cv::medianBlur()
函数来实现中值模糊。
G
(
x
,
y
)
=
median
{
I
(
x
+
i
,
y
+
j
)
∣
−
k
≤
i
,
j
≤
k
}
G(x, y) = \text{median} \{ I(x+i, y+j) \mid -k \leq i, j \leq k \}
G(x,y)=median{I(x+i,y+j)∣−k≤i,j≤k}
$median $表示在卷积核范围内取所有像素值的中值。
k是卷积核的半径。
中值模糊代码实现 (cv::medianBlur())
//中值模糊
cv::Mat medianBlurredImage;
cv::medianBlur(gege, medianBlurredImage, 5); // 使用 5x5 的内核进行中值模糊
cv::imshow("medianBlurredImage", medianBlurredImage);
结果:
左图显示了未处理的图像,其中存在明显的“椒盐噪声”;右图经过中值模糊处理,噪声被有效去除,“哥哥”明显变得更加平滑和清晰。
6.双边滤波 (Bilateral Filtering)
双边滤波是一种高级的图像模糊技术,它不仅考虑像素的空间距离,还考虑像素的颜色差异,从而在平滑图像的同时保持边缘锐利。OpenCV 提供的 cv::bilateralFilter()
函数可以用来实现双边滤波。
G
(
x
,
y
)
=
1
W
p
∑
i
=
−
k
k
∑
j
=
−
k
k
I
(
x
+
i
,
y
+
j
)
⋅
e
−
i
2
+
j
2
2
σ
s
2
⋅
e
−
(
I
(
x
+
i
,
y
+
j
)
−
I
(
x
,
y
)
)
2
2
σ
r
2
G(x, y) = \frac{1}{W_p} \sum_{i=-k}^k \sum_{j=-k}^k I(x+i, y+j) \cdot e^{-\frac{i^2 + j^2}{2\sigma_s^2}} \cdot e^{-\frac{(I(x+i, y+j) - I(x, y))^2}{2\sigma_r^2}}
G(x,y)=Wp1i=−k∑kj=−k∑kI(x+i,y+j)⋅e−2σs2i2+j2⋅e−2σr2(I(x+i,y+j)−I(x,y))2
W
p
Wp
Wp 是归一化因子,确保权重总和为1。
σ s σs σs 是空间标准差,控制空间距离的权重。
σ r σr σr是颜色标准差,控制像素值相似性的权重。
①在平缓区域
在图像变化平缓的区域,邻域中的像素值$ I(x+i,y+j)$与中心像素 $I(x,y) $相差不大,因此颜色相似性项
e
−
(
I
(
x
+
i
,
y
+
j
)
−
I
(
x
,
y
)
)
2
2
σ
r
2
e^{-\frac{(I(x+i, y+j) - I(x, y))^2}{2\sigma_r^2}}
e−2σr2(I(x+i,y+j)−I(x,y))2 接近于 1。此时,双边滤波公式中的空间距离项$ e{-\frac{i2 + j2}{2\sigma_s2}}$ 起主要作用,双边滤波的效果类似于高斯滤波,对图像进行平滑处理。
②在剧烈变化区域(如边缘)
在图像的边缘区域,邻域中的像素值$ I(x+i,y+j) 与中心像素 与中心像素 与中心像素I(x,y) $ 之间的差异较大。这时,颜色相似性项 e − ( I ( x + i , y + j ) − I ( x , y ) ) 2 2 σ r 2 e^{-\frac{(I(x+i, y+j) - I(x, y))^2}{2\sigma_r^2}} e−2σr2(I(x+i,y+j)−I(x,y))2 会趋近于 0。由于这个权重很小,这些颜色差异大的像素对输出$ G(x,y)$的影响也非常小。因此,双边滤波在这种情况下能够保持边缘的清晰度,避免了普通高斯滤波导致的边缘模糊。
双边滤波代码实现 (cv::medianBlur())
cv::bilateralFilter()
函数,内核大小为 9,空间和颜色标准差均为 75。双边滤波可以在去除噪声的同时保持图像边缘清晰。
//双边模糊
cv::Mat bilateralFilterImage;
cv::bilateralFilter(Bilateral, bilateralFilterImage, 9, 75, 75); // 使用双边滤波,窗口大小为9,空间和颜色标准差均为75
cv::imshow("bilateralFilterImage", bilateralFilterImage);
结果:
右图经过双边滤波处理,皮肤细节得到了平滑,面部的边缘和特征线条仍然保持清晰。
完整代码
#include<opencv2/opencv.hpp>
#include<highgui.hpp>
using namespace cv;
using namespace std;
void image_blur()
{
cv::Mat image;
image = imread("sherlock.jpg");
if (image.empty()) {
printf("could not find the image...\n");
return;
}
namedWindow("input image", cv::WINDOW_AUTOSIZE);
cv::imshow("input image", image);
cv::Mat gege;
gege = imread("椒盐噪声.png");
if (gege.empty()) {
printf("could not find the image...\n");
return;
}
cv::imshow("gege", gege);
cv::Mat Bilateral;
Bilateral = imread("Bilateral.jpg");
if (Bilateral.empty()) {
printf("could not find the image...\n");
return;
}
cv::imshow("Bilateral", Bilateral);
//均值模糊
cv::Mat meanBlurredImage;
cv::blur(image, meanBlurredImage, cv::Size(5, 5)); // 使用 5x5 的内核进行均值模糊
cv::imshow("meanBlurredImage", meanBlurredImage);
//高斯模糊
cv::Mat gaussianBlurredImage;
cv::GaussianBlur(image, gaussianBlurredImage, cv::Size(5, 5), 0.5); // 使用 5x5 的高斯内核和标准差为 0.5 进行高斯模糊
cv::imshow("gaussianBlurredImage", gaussianBlurredImage);
//中值模糊
cv::Mat medianBlurredImage;
cv::medianBlur(gege, medianBlurredImage, 5); // 使用 5x5 的内核进行中值模糊
cv::imshow("medianBlurredImage", medianBlurredImage);
//双边模糊
cv::Mat bilateralFilterImage;
cv::bilateralFilter(Bilateral, bilateralFilterImage, 9, 75, 75); // 使用双边滤波,窗口大小为9,空间和颜色标准差均为75
cv::imshow("bilateralFilterImage", bilateralFilterImage);
cv::waitKey(0);
}
int main()
{
image_blur(); //图像模糊处理
return 0;
}