学习目标:
- 使用低通滤波器平滑图像
- 使用人工滤波器模糊图像(2D卷积)
2D卷积(图像滤波)
正如对一维信号的滤波操作一样,我们可以使用不同的滤波器对图像进行滤波,比如低通滤波器(LPF),高通滤波器(HPF)等。低通滤波器可以移除噪声,进而平滑图像。而高通滤波器可以寻找图像的边界。
OpenCV提供相应的函数,
cv2.filter2D(src,
ddepth,
kernel,
dst=None,
anchor=None,
delta=None,
borderType=None)
函数功能:
该函数将任意的线性滤波器作用于图像。当滤波器一部分不在图像的范围内,那么根据border模式插值不属于图像区域的部分。
参数说明:
src:输入图像
dst:与输入图像的大小和通道一致的输出图像
ddepth:期望的目标图像的深度
kernel:卷积核,通常是浮点型的单通道矩阵。当然也可以使用多通道分别作用与图像的每一个通道。
anchor:锚点,指定卷积核的相对位置的点。默认是(-1,,1)表示卷积核的中心。
delta:对滤波后的像素添加的固定项值。
borderType:边界填充方式。
下面的例子是均值滤波,卷积核为5x5的卷积核,如下:
K
=
1
25
×
[
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
]
K=\frac{1}{25}\times \quad \begin{bmatrix} 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \end{bmatrix} \quad
K=251×⎣⎢⎢⎢⎢⎡1111111111111111111111111⎦⎥⎥⎥⎥⎤
针对上述卷积核,卷积操作如下:对于图像中的每一个像素,该卷积核的中心与图片的像素重合,并将所有落在图像内的像素求和,最后除以25。也即是求5x5窗口内像素的平均值。当窗口滑过图像的所有位置时,得到滤波后的图像。
示例代码如下:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('opencv_logo.png')
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 Blurring)
对图像进行低通卷积核的操作称之为图像模糊。常用于去除噪声,用于去除高频的部分的图像内容,比如噪声或者边界。OpenCV提供了四种类型的图像模糊技术。
1.Averaging
简单说就是对卷积核区域像素求均值,然后用该均值作为该像素未知的输出值。函数如下:
def blur(src, ksize, dst=None, anchor=None, borderType=None):
"""
. @param src input image; it can have any number of channels, which are processed independently, but the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
. @param dst output image of the same size and type as src.
. @param ksize blurring kernel size.
. @param anchor anchor point; default value Point(-1,-1) means that the anchor is at the kernel center.
. @param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes
. @sa boxFilter, bilateralFilter, GaussianBlur, medianBlur
"""
pass
def boxFilter(src, ddepth, ksize, dst=None, anchor=None, normalize=None, borderType=None):
"""
. Unnormalized box filter is useful for computing various integral characteristics over each pixel
. neighborhood, such as covariance matrices of image derivatives (used in dense optical flow
. algorithms, and so on). If you need to compute pixel sums over variable-size windows, use #integral.
.
. @param src input image.
. @param dst output image of the same size and type as src.
. @param ddepth the output image depth (-1 to use src.depth()).
. @param ksize blurring kernel size.
. @param anchor anchor point; default value Point(-1,-1) means that the anchor is at the kernel
. center.
. @param normalize flag, specifying whether the kernel is normalized by its area or not. True or False.
. @param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes
. @sa blur, bilateralFilter, GaussianBlur, medianBlur, integral
"""
示例如下:
img = cv2.imread('opencv_logo.png')
blur = cv2.blur(img,(5,5))
# blur = cv2.boxFilter(img, -1, (5, 5), normalize=True)
# gaussian = cv2.GaussianBlur(img,(5,5),0)
# median = cv2.medianBlur(img,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()
2. Gaussian Filtering(高斯滤波)
与均值滤波不同的是,高斯滤波核中的每个值并不是一样的。函数如下:
blur = cv2.GaussianBlur(img,(5,5),0)
def GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None): # real signature unknown; restored from __doc__
"""
. @brief Blurs an image using a Gaussian filter.
.
. The function convolves the source image with the specified Gaussian kernel. In-place filtering is supported.
. @param src input image; the image can have any number of channels, which are processed independently, but the depth should be CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
. @param dst output image of the same size and type as src.
. @param ksize Gaussian kernel size. ksize.width and ksize.height can differ but they both must be positive and odd(奇数). Or, they can be zero's and then they are computed from sigma.
. @param sigmaX Gaussian kernel standard deviation in X direction.
. @param sigmaY Gaussian kernel standard deviation in Y direction; if sigmaY is zero, it is set to be equal to sigmaX, if both sigmas are zeros, they are computed from ksize.width and ksize.height, respectively (see #getGaussianKernel for details); to fully control the result regardless of possible future modifications of all this semantics, it is recommended to specify all of ksize, sigmaX, and sigmaY.
. @param borderType pixel extrapolation method, see #BorderTypes
. @sa sepFilter2D, filter2D, blur, boxFilter, bilateralFilter, medianBlur
"""
pass
参考上面的代码示例,运行结果如下:
那么高斯卷积核长什么样子呢?应该如何生成高斯卷积核?
二维高斯核的定义:
f ( x ) = 1 2 π ∗ σ ∗ e x p ( x 2 + y 2 2 σ 2 ) f(x)=\frac{1}{\sqrt{2\pi}*\sigma}*exp(\frac{x^2+y^2}{2\sigma^2}) f(x)=2π∗σ1∗exp(2σ2x2+y2)
Python计算高斯核:
def gauss_1(size, sigma_1):
x, y = np.mgrid[-size // 2 + 1:size // 2 + 1, -size // 2 + 1:size // 2 + 1]
g = np.exp(-((x ** 2 + y ** 2) / (2.0 * sigma_1 ** 2)))
return g / g.sum()
s = gauss_1(3, 0.8)
print(s)
# 输出二维高斯卷积核如下:
[[0.05711826 0.12475775 0.05711826]
[0.12475775 0.27249597 0.12475775]
[0.05711826 0.12475775 0.05711826]]
OpenCV计算高斯核:
sigma = 0.8
kx = cv2.getGaussianKernel(3, sigma)
ky = cv2.getGaussianKernel(3, sigma)
a = kx * ky.T
print(a)
# 输出结果
[[0.05711826 0.12475775 0.05711826]
[0.12475775 0.27249597 0.12475775]
[0.05711826 0.12475775 0.05711826]]
3. Median Filtering(中值滤波)
与上面类似,计算窗口区域的中值,并代替当前位置的像素值。主要用去除椒盐噪声(salt-and-pepper noise)。高斯滤波和均值滤波是通过区域内的所有像素加权计算得到的新的像素值,新输出的滤波值可能在原图像中根本不存在。而中值滤波是对窗口内的像素进行排序取中值,所以输出的滤波值一定存在于原图像。核的大小必须是正的奇数。
下面的例子中,我们添加50%的噪声,然后使用中值滤波,代码如下:
median = cv2.medianBlur(img, 5)
结果如下:
4. Bilateral Filtering(双边滤波)
def bilateralFilter(src, d, sigmaColor, sigmaSpace, dst=None, borderType=None): # real signature unknown; restored from __doc__
"""
@brief Applies the bilateral filter to an image.
. bilateralFilter can reduce unwanted noise very well while keeping edges fairly sharp. However, it
. is very slow compared to most filters.
.
. _Sigma values_: For simplicity, you can set the 2 sigma values to be the same. If they are small (\
<10), the filter will not have much effect, whereas if they are large (\> 150), they will have a
very strong effect, making the image look "cartoonish".
. _Filter size_: Large filters (d \> 5) are very slow, so it is recommended to use d=5 for real-time
applications, and perhaps d=9 for offline applications that need heavy noise filtering.
. This filter does not work inplace.
. @param src Source 8-bit or floating-point, 1-channel or 3-channel image.
. @param dst Destination image of the same size and type as src .
. @param d Diameter of each pixel neighborhood that is used during filtering. If it is non-positive,
it is computed from sigmaSpace.
. @param sigmaColor Filter sigma in the color space. A larger value of the parameter means that
farther colors within the pixel neighborhood (see sigmaSpace) will be mixed together, resulting
in larger areas of semi-equal color.
. @param sigmaSpace Filter sigma in the coordinate space. A larger value of the parameter means that
farther pixels will influence each other as long as their colors are close enough (see sigmaColor
). When d\>0, it specifies the neighborhood size regardless of sigmaSpace. Otherwise, d is
proportional to sigmaSpace.
. @param borderType border mode used to extrapolate pixels outside of the image, see #BorderTypes
"""
pass
上面讨论的各种滤波器倾向于模糊边界,双边滤波具有以下特点:
- 去除噪声, 保持边界
- 速度比较慢
img1 = cv2.imread('Selection_028.png')
blur = cv2.bilateralFilter(img1, 9, 75, 75)
# cv2.imshow('blur', blur)
# cv2.waitKey(0)
# opencv读取的是BGR通道,所以要变换通道,否则使用plt会显示异常
# 如左上和右上图(异常),左下和右下是原图和滤波图
x_img = img1[:, :, (2, 1, 0)]
x_blur = blur[:, :, (2, 1, 0)]
plt.subplot(121), plt.imshow(x_img), plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(x_blur), plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()
左下和右下是正常显示的结果: