1. 2D卷积(图像滤波)

像一维信号一样,图像也可以用各种高通道滤波器、地通道滤波器来进行滤波。低通道滤波器可以用来去除噪音,平滑图像;高通道滤波器可以用来寻找边缘。
OpenCV提供了一个函数cv2.filter2D()用卷积核对图像执行卷积操作。

void cv::filter2D    (    
InputArray     src, //输入图
OutputArray     dst, //输出图
int     ddepth, //目标图期望深度(即输出mat的dtype,如uint8、uint16、32f、64f等,为-1则与输入图相同)
InputArray     kernel, //卷积核(只能为单通道,奇数维度,若要对各通道分别滤波,则需要cv2.split通道后分别滤波)
Point     anchor = Point(-1,-1), //卷积核中心点,默认(-1,-1)代表此卷积核处理的像素位于卷积核中心
double     delta = 0, //滤波结果被写到实际像素前的,会加上此偏移值
int     borderType = BORDER_DEFAULT //对于边缘像素的处理,详见文档描述
)    

例如,使用一个5*5的均值滤波器对图像进行滤波:

import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('test.jpg')
kernel = np.ones((5,5),np.float32)/25
dst = cv2.filter2D(img,-1,kernel)
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(dst),plt.title('Averaging')
plt.xticks([]), plt.yticks([])
plt.show()

image.png

2. 图像模糊(平滑)

图像模糊是通过一个低通道卷积核对图像滤波来实现的,对消除图像噪点有很好的作用。此操作实际上是删除了图像中的高频内容(例如:噪声、边缘),因此边缘会有点模糊(有一些模糊技术也不会模糊边缘)。OpenCV主要提供了四种类型的模糊技术。

2.1 均值模糊

均值模糊是通过使用标准化的箱式过滤器对图像滤波实现的。它只取内核区域下所有像素的平均值,并替换中心元素,通过函数cv2.fuzzy()或cv2.boxFilter()提供此功能。一个3x3的标准箱式过滤器如下所示:
image.png

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

与cv::blur相比,cv::boxFilter不一定要使用标准化的箱式过滤器,其计算结果也可以用于一些其它的计算

void cv::boxFilter    
(    
InputArray     src,
OutputArray     dst,
int     ddepth,
Size     ksize,
Point     anchor = Point(-1,-1),
bool     normalize = true,
int     borderType = BORDER_DEFAULT 
)    

示例代码:

blur = cv2.blur(img, (5, 5))
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(blur), plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()

image.png

2.2. 高斯模糊

cv2.GaussianBlur(),使用高斯核来代替箱形滤波器,指定内核的宽度和高度来执行滤波,这些宽度和高度应该是正的奇数。还可以分别指定X和Y方向、SigmaX和SigmaY方向的标准差。如果仅指定SigmaX,SigmaY将被视为与SigmaX相同。如果两者都以零的形式给出,则它们根据内核大小计算。高斯模糊是去除图像中噪点的有效方法。

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

如果不想用cv::GaussianBlur函数,也可以自己通过参数来 构造一个高斯卷积核

Mat cv::getGaussianKernel    (    
int     ksize,
double     sigma,
int     ktype = CV_64F 
)    

高斯模糊示例代码:

dst = cv2.GaussianBlur(img,(5,5),0)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(dst), plt.title('GaussianBlur')
plt.xticks([]), plt.yticks([])
plt.show()

image.png

2.3 中值模糊

中值模糊取内核区域下所有像素的中间值来替代中心元素的值,对去除椒盐噪声非常有效。在前面的滤镜中,中心元素是一个新计算的值,而在中值模糊中,图像中的中心元素总是被某个像素值所替代,有效地降低了噪声。它的内核大小应该是一个正的奇数。

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

示例代码:

img = cv2.imread('test2.jpg')
dst = cv2.medianBlur(img,5)
plt.subplot(121), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(dst), plt.title('Median')
plt.xticks([]), plt.yticks([])
plt.show()

image.png

2.4 双边滤波

cv::bilateralFilter()函数在有效去除噪点的同时,保持边缘的锐利。但与其他过滤器相比,操作速度较慢。高斯滤波器取像素周围的像素,并求出其高斯加权平均值。这种高斯滤波器是空间的函数,在滤波时要考虑附近的像素。它不考虑像素是否具有几乎相同的亮度,也不考虑像素是否是边缘像素。所以它也模糊了边缘,这是我们不想做的。

双边滤波器在空间上也采用高斯滤波器,但多一个作用域像素亮度上的高斯滤波器。空间上高斯函数保证只邻近像素点,而亮度上的高斯函数只考虑与中心像素点相似的像素点。因此,它保留了边缘,因为边缘部分的像素将有很大的亮度变化。

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

示例代码:

img = cv2.imread('test3.jpg')
dst = cv2.bilateralFilter(img,9,100,100)
dst2 = cv2.GaussianBlur(img, (5, 5), 0)
plt.subplot(131), plt.imshow(img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(dst2), plt.title('GaussianBlur')
plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(dst), plt.title('Bilateral')
plt.xticks([]), plt.yticks([])
plt.show()

image.png

可以看到与高斯模糊相比,双边滤波去除了一定的噪点,且边缘细节保存比较完好

☞ 参与评论