图像的模糊是图像处理中比较常用和简单的处理手段之一。那么使用这种手段的原因就是为了给图像进行去噪,便于后面的处理
根据我上文图像卷积的操作中可以想象,图像的模糊处理就是卷积的计算,拿一个3*3的卷积核在图像上做滑动操作,继而得到新图像
而不同类型的模糊背后其实对应的卷积核也不同
根据原理的不同,模糊的方式也有很多种,比如:均值模糊函数blur()、中值模糊函数medianBlur()、高斯平滑函数GaussianBlur()、双边滤波函数bilateralFilter()
均值模糊从字面意思就是去平均值,也就是卷积核中的系数全是1,与卷积核覆盖下的原图像素值相乘,再除以9(卷积核的大小为3*3),得到平均值,赋值给中心像素
它的缺点是在去噪的同时,不能很好的保留图像细节,因为全用均值代替了
blur(src,ksize,dst=None, anchor=None, borderType=None)
src:输入图像
ksize:卷积核大小,越大越模糊,一般来说是奇数,卷积核一般都是3x3,5x5或者7x7
其余参数可以不理会
相比于均值模糊,高斯模糊更好的保留图像的细节。因为高斯函数的特性,会有一定的权重配比
GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)
src:输入图像
ksize:模糊核大小,但它们必须是正的和奇数的
sigmaX:X方向上的高斯核标准差
字面意思的话,就是取中间的值来代替中心像素。和均值类似,只不过均值是去平均值,而中值取得是中间的那个值。中值可以有效的去除椒盐噪声(比如说你在清水中,撒点盐,这些就是椒盐噪声。对应到图像上,在一副黑色图像上,有很多小白点,这些就是椒盐噪声)
那么它为什么可以去除椒盐噪声呢。因为椒盐噪声像素值要么很小为0,要么很大为255,而取中间值话,就会用替代这些,从而给图像去噪点。
medianBlur(src, ksize, dst=None)
src:输入图像
ksize:孔径线性尺寸;它必须是奇数并且大于1
高斯滤波相对于均值模糊而言保留图像的一些细节,但是它是基于空阈的,就是基于空间分布的。但是它还是没有办法完全避免边缘信息的丢失。而双边滤波则是比高斯滤波多了一个高斯方差sigma-d,所以在边缘附近,离的较远的像素不会太多影响到边缘上的像素值,这样就保证了边缘附近像素值的保存。也就是说它们的像素值在设定落差之外,不给它进行模糊,比便于保留边缘。
bilateralFilter(src, d, sigmaColor, sigmaSpace, dst=None, borderType=None)
src:输入图像
d:用于滤波的每个像素邻域的直径,直径内的所有像素均会被纳入计算。如果它是非正数,则从sigmaSpace计算
sigmaColor:决定多少差值内的像素会被计算,颜色标准差
sigmaSpace:如果d的值大于0则无效,否则会用来计算d的值,空间标准差
代码如下:
# 引入包
import cv2 as cv
def mo_image(src1):
src2 = cv.blur(src1, (5, 5))
cv.namedWindow("blur", cv.WINDOW_NORMAL)
cv.imshow("blur", src2)
src2 = cv.medianBlur(src1, 5)
cv.namedWindow("mdBlur", cv.WINDOW_NORMAL)
cv.imshow("mdBlur", src2)
src2 = cv.GaussianBlur(src1, (5, 5), 2)
cv.namedWindow("GaussBlur", cv.WINDOW_NORMAL)
cv.imshow("GaussBlur", src2)
src2 = cv.bilateralFilter(src1, 5, 5, 2)
cv.namedWindow("bilFilter", cv.WINDOW_NORMAL)
cv.imshow("bilFilter", src2)
src = cv.imread("./static/image/blur.jpg")
cv.namedWindow("image", cv.WINDOW_NORMAL)
cv.imshow("image", src)
mo_image(src)
cv.waitKey(0)
cv.destroyAllWindows()
filter2D():定义为filter2D(src,ddepth,kernel)
1、模糊(blur)
模糊内核消除了相邻像素值之间的差异。内核如下:
kernel = np.ones((5, 5), np.float)/25
# 自定义模糊
kernel1 = np.ones((5, 5), np.float)/25 # 自定义矩阵,并防止数值溢出
src2 = cv.filter2D(src1, -1, kernel1) # 自定义模糊
cv.namedWindow("define", cv.WINDOW_NORMAL)
cv.imshow("define", src2)
2、索贝尔(sobel)
sobel内核用于仅显示特定方向上相邻像素值的差异,分为left sobel、right sobel(检测梯度的水平变化)、top sobel、buttom sobel(检测梯度的垂直变化)。内核如下:
-1 | -2 | -1 |
---|---|---|
0 | 0 | 0 |
1 | 2 | 1 |
3、浮雕(emboss)
通过强调像素的差在给定方向的Givens深度的错觉。在这种情况下,沿着从左上到右下的直线的方向。内核如下:
-2 | -1 | 0 |
---|---|---|
-1 | 1 | 1 |
0 | 1 | 2 |
4、大纲(outline)
一个轮廓内核(也称为“边缘”的内核)用于突出显示的像素值大的差异。具有接近相同强度的相邻像素旁边的像素在新图像中将显示为黑色,而与强烈不同的相邻像素相邻的像素将显示为白色。内核如下:
-1 | -1 | -1 |
---|---|---|
-1 | 8 | -1 |
-1 | -1 | -1 |
5、锐化(sharpen)
一个轮廓内核(也称为“边缘”的内核)用于突出显示的像素值大的差异。具有接近相同强度的相邻像素旁边的像素在新图像中将显示为黑色,而与强烈不同的相邻像素相邻的像素将显示为白色。内核如下:
0 | -1 | 0 |
---|---|---|
-1 | 5 | -1 |
0 | -1 | 0 |
运行效果:
6、拉普拉斯算子(laplacian operator)
拉普拉斯算子可以用于边缘检测,对于检测图像中的模糊也非常有用。内核如下:
0 | 1 | 0 |
---|---|---|
1 | -4 | 1 |
0 | 1 | 0 |
7、分身(identity)
这个非常简单,就是原图(不考虑边界时)。内核如下:
0 | 0 | 0 |
---|---|---|
0 | 1 | 0 |
0 | 0 | 0 |
完整代码:
# 引入包
import cv2 as cv
import numpy as np
def define_image(src1):
# 自定义模糊
kernel1 = np.ones((9, 9), np.float)/80 # 自定义矩阵,并防止数值溢出
src2 = cv.filter2D(src1, -1, kernel1) # 自定义模糊
cv.namedWindow("define", cv.WINDOW_NORMAL)
cv.imshow("define", src2)
# 自定义锐化
kernel2 = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]], np.float32)
src2 = cv.filter2D(src1, -1, kernel2) # 自定义锐化
cv.namedWindow("define2", cv.WINDOW_NORMAL)
cv.imshow("define2", src2)
# 索贝尔(sobel)
kernel3 = np.array([[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]], np.float32)
src2 = cv.filter2D(src1, -1, kernel3)
cv.namedWindow("define2", cv.WINDOW_NORMAL)
cv.imshow("define2", src2)
# 浮雕
kernel4 = np.array([[-2, -1, 0],
[-1, 1, 1],
[0, 1, 2]], np.float32)
src2 = cv.filter2D(src1, -1, kernel4)
cv.namedWindow("define2", cv.WINDOW_NORMAL)
cv.imshow("define2", src2)
# 大纲(outline)
kernel5 = np.array([[-1, -1, -1],
[-1, 8, -1],
[-1, -1, -1]], np.float32)
src2 = cv.filter2D(src1, -1, kernel5)
cv.namedWindow("define2", cv.WINDOW_NORMAL)
cv.imshow("define2", src2)
# 拉普拉斯算子(laplacian operator)
kernel6 = np.array([[0, 1, 0],
[1, -4, 1],
[0, 1, 0]], np.float32)
src2 = cv.filter2D(src1, -1, kernel6)
cv.namedWindow("define2", cv.WINDOW_NORMAL)
cv.imshow("define2", src2)
# 分身(identity)
kernel7 = np.array([[0, 0, 0],
[0, 1, 0],
[0, 0, 0]], np.float32)
src2 = cv.filter2D(src1, -1, kernel7)
cv.namedWindow("define2", cv.WINDOW_NORMAL)
cv.imshow("define2", src2)
src = cv.imread("./static/image/windows.jpg")
cv.namedWindow("image", cv.WINDOW_NORMAL)
cv.imshow("image", src)
define_image(src)
cv.waitKey(0)
cv.destroyAllWindows()
例子参考:
https://blog.csdn.net/qq_42059060/article/details/107660265?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162520738216780271574301%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=162520738216780271574301&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29-2-107660265.pc_search_result_cache&utm_term=cv.filter2d&spm=1018.2226.3001.4187