图像平滑处理是对图像中与周围像素点的像素值差异较大的像素点进行处理,将其调整为周围像素点像素值的近似值,而取近似值的方式有很多,主要有均值滤波、方框滤波、中值滤波、高斯滤波、双边滤波、2D 卷积。
均值滤波是指用当前像素点周围 NxN 个像素值的均值来代替当前像素值。使用该方法遍历图像内每一个像素点,便可以完成整幅图像的均值滤波。在进行均值滤波时,首先要考虑对周围多少个像素点取平均值,通常情况是以当前像素点为中心,对行数和列数相等的一块区域内的所有像素点的像素值求平均值。选定的区域一般被称作滤波核或卷积核。
以图像中心点 ‘23’ 为当前像素点,对周围 3x3 区域内的像素值求平均值:
n e w V a l u e = [ ( 148 + 124 + 136 ) + ( 140 + 23 + 133 ) + ( 144 + 151 + 144 ) ] / ( 3 ∗ 3 ) = 127 newValue = [(148+124+136)+(140+23+133)+(144+151+144)]/(3*3)=127 newValue=[(148+124+136)+(140+23+133)+(144+151+144)]/(3∗3)=127,此时 ‘127’ 就成为中心点的像素值。
当然在对图像的第一个像素点 ‘132’ 进行均值滤波操作时,图像的外部并没有像素点,此时有两种计算方式:
(1).只去图像内存在的周围邻域内的像素值的均值
n e w V a l u e = [ ( 132 + 147 ) + ( 151 + 148 ) ] / ( 2 ∗ 2 ) = 127 = 145 newValue = [(132+147)+(151+148)]/(2*2)=127=145 newValue=[(132+147)+(151+148)]/(2∗2)=127=145,此时 ‘145’ 就成为图像的第一个像素点的像素值。
如上图所示,可以将 5x5 的图像扩展为 9x9 大小的图像,根据不同的需求可以选择不同的填充方式在新增的行列内填充像素值,填充完成后便可以按一般情况进行均值求解。
dst = blur(src, ksize[, anchor[, borderType]])
类型 | 说明 |
---|---|
cv2.BORDER_CONSTANT |
iiiiii|abcdefgh|iiiiii,i 为特定值 |
cv2.BORDER_REPLICATE |
aaaaaa|abcdefgh|hhhhhh |
cv2.BORDER_REFLECT |
fedcba|abcdefgh|hgfedcb |
cv2.BORDER_WRAP |
cdefgh|abcdefgh|abcdefg |
cv2.BORDER_REFLECT101 cv2.BORDER_REFLECT_101 cv2.BORDER_DEFAULT |
gfedcb|abcdefgh|gfedcba |
cv2.BORDER_TRANSPARENT |
uvwxyz|abcdefgh|ijklmno |
简写为 dst = blur(src, ksize)
import cv2
src = cv2.imread('photo.jpg')
dst3 = cv2.blur(src,(3,3))
dst15 = cv2.blur(src,(15,15))
dst70 = cv2.blur(src,(70,70))
cv2.imshow('src',src)
cv2.imshow('dst3',dst3)
cv2.imshow('dst15',dst15)
cv2.imshow('dst70',dst70)
cv2.waitKey()
cv2.destroyAllWindows()
可以看到滤波核越大,图像失真情况会越明显,所在在实际中,既要达到去噪效果,又不能让图像过分失真,就要选取合适大小的滤波核。
在均值滤波中,对每一个像素点进行邻域求均值时,当邻域大小为 3x3 时,原图、滤波核与结果图的结果可以表示如下:
当我们不进行均值求解,只计算当前像素点邻域内的像素值之和,作为当前像素点的新像素值时,上图可以变换为下图的模式:
和均值滤波不同的是,方框滤波可以自由选择以上两种方式中的一种方式作为处理方式,其中像素值之和的方式又称为结果归一化方式。由于像素值之和很可能超过像素值的最大范围,被截断处理为最大值后,处理后的图像有可能会为一张白色的图像。
dst = boxFilter(src, ddepth, ksize[, anchor[, normalize[, borderType]]])
import cv2
src = cv2.imread('photo.jpg')
dst0 = cv2.boxFilter(src,-1,(3,3),normalize=0)
dst1 = cv2.boxFilter(src,-1,(3,3),normalize=1)
cv2.imshow('src',src)
cv2.imshow('dst0',dst0)
cv2.imshow('dst1',dst1)
cv2.waitKey()
cv2.destroyAllWindows()
在进行均值滤波和方框滤波时,滤波核内的值都是相同的,即滤波值的权重相同。在高斯滤波中,会将滤波核中心点的权重加大,原理中心点的权重值减小,然后在此基础上计算邻域内的像素值之和。
进一步可表示为:
在实际使用中,高斯滤波核不仅使用的滤波核大小多种多样,而且同一大小滤波核也会使用不同的权重比。
n e w V a l u e = 148 ∗ 0.05 + 124 ∗ 0.1 + . . . . . . + 151 ∗ 0.1 + 144 ∗ 0.05 = 92.6 newValue=148*0.05+124*0.1+......+151*0.1+144*0.05=92.6 newValue=148∗0.05+124∗0.1+......+151∗0.1+144∗0.05=92.6,此时的结果并没有取均值进行归一化,但实际使用时往往需要进行归一化,严格来讲,没有进行归一化的结果往往是错误的。
dst = GaussianBlur(src, ksize, sigmaX[, sigmaY[, borderType]])
在使用时一般将 sigmaX 和 sigmaY 都设置为0,所以函数简化为:
dst = GaussianBlur(src, ksize, 0, 0)
import cv2
src = cv2.imread('photo.jpg')
dst3 = cv2.GaussianBlur(src,(3,3),0,0)
dst5 = cv2.GaussianBlur(src,(5,5),0,0)
cv2.imshow('src',src)
cv2.imshow('dst3',dst3)
cv2.imshow('dst5',dst5)
cv2.waitKey()
cv2.destroyAllWindows()
中值滤波不再采用加权求均值的的方式计算结果,而是采用邻域内所有像素点的中间值来代替当前像素点的像素值。中值滤波会取当前像素点及周围邻域像素点的像素值,将它们排序,然后取中间位置的像素值作为当前点的像素值。
以下图为例,采用 3x3 的邻域,对中心点 ‘23’ 采区中值滤波进行像素替换:
排序前:148,124,136,140,23,133,144,151,144
排序后:23,124,133,136,140,144,144,148,151
取中值 140 作为中心点的新像素值。
dst = medianBlur(src, ksize)
import cv2
src = cv2.imread('photo.jpg')
dst = cv2.medianBlur(src,3)
cv2.imshow('src',src)
cv2.imshow('dst3',dst)
cv2.waitKey()
cv2.destroyAllWindows()
双边滤波是综合考虑了空间信息和色彩信息的滤波方式,既能够有效地去除噪声,又能够较好的保护边缘信息。之前上述的滤波方式基本都只考虑了空间的权重信息,计算起来比较方便,但是在边缘信息的处理上存在较大问题。
双边滤波在处理边缘时,与当前像素点色彩相近(即像素差值较小)的点会被赋予较大的权重,而与当前像素点色彩相差较大(即像素差值较大)的点会被赋予较小的权重,甚至为 0,这样就做到了保护边缘信息的效果。
使用均值滤波、方框滤波、高斯滤波对上图进行平滑处理,可以看到处理后的边缘信息变得很模糊:
dst = bilateralFilter(src, d, sigmaColor, sigmaSpace[, borderType])
import cv2
img = cv2.imread('2.png')
print(img)
dst1 = cv2.blur(img,(3,3))
dst2 = cv2.bilateralFilter(img,5,150,150)
cv2.imshow('src',img)
cv2.imshow('1',dst1)
cv2.imshow('2',dst2)
cv2.waitKey()
cv2.destroyAllWindows()
对同一个图分别进行均值滤波、双边滤波,明显看到双边滤波处理边缘效果更好:
之前介绍的滤波方式中的滤波核都具有一定的灵活性,可以设置的大小和数值,然而它也不能够满足特定需求,有时候我们需要特定的卷积实现卷积操作,这时候就需要Opencv 中的2D卷积。
dst = filter2D(src, ddepth, kernel[, anchor[, delta[, borderType]]])
在使用时一般可选项都设置为默认值,所以函数简化为:
dst = filter2D(src, ddepth, kernel)
自定义一个 7 x 7 的卷积核对图像进行卷积操作,代码与结果如下:
import cv2
import numpy as np
img = cv2.imread('dog.jpg')
kernel = np.ones((7, 7)) / 81.0
dst = cv2.filter2D(img, -1, kernel)
cv2.imshow('src', img)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()