每一幅图像都包含某种程度的噪声,在大多数情况下,通过平滑技术(也常称为滤波技术)进行抑制或去除,其中具备保持边缘(Edge Preserving)作用的平滑技术得到更多的关注。
文章目录
常用的平滑处理算法包括
- 基于二维离散卷积的高斯平滑、均值平滑
- 基于统计学方法的中值平滑
- 具备保持边缘作用的平滑算法的双边滤波、导向滤波等。
二维离散卷积
二维离散卷积是基于两个矩阵的一种计算方式
如I=(1324),K=(−12−21)
首先将K逆时针旋转180o,即K=(1−22−1)
full卷积
将I扩展平铺为 I=⎝⎜⎜⎛0000013002400000⎠⎟⎟⎞
将K逐个元素与I相乘
得到
C=⎝⎛−1−16−4−511−4−64⎠⎞
K通常称为卷积核或者卷积掩码或者卷积算子。
valid卷积
I 完全覆盖K的值
1×1+2×2+3×(−2)+4×(−1)=−5
得到
C=(−5)
same卷积
为了得到的卷积结果和原图的高、宽相等,通常给K一个"锚点",然后将"锚点"循环至图像矩阵的每个元素处,然后进行卷积。乘数为I,被乘数为K
得到
C=(−511−64)
API
import cv2
import numpy as np
src=np.array([[5,1,7],[1,5,9],[2,6,2]])
dst=cv2.copyMakeBorder(src,2,2,2,2,cv2.BORDER_REFLECT_101)
其中,BORDER_REFLECT_101是默认的扩充方式,也是最理想的一种扩充方式。
python实现 二维离散卷积
利用Scipy提供的函数: convolve2d(in1,in2,mode=‘full’,boundary=‘fill’,fillvalue=0)
import numpy as np
from scipy import signal
if __name__=="__main__":
I=np.array([[1,2],[3,4]],np.float32)
H1,W1=I.shape
K=np.array([[-1,-2],[2,1]],np.float32)
H2,W2=K.shape
c_full=signal.convolve2d(I,K,mode='full')
kr,kc=0,0
c_same=c_full[H2-kr-1:H1+H2-kr-1,W2-kc-1:W1+W2-kc-1]
1 高斯平滑
-
高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。
-
高斯滤波的具体操作是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。
-
高斯滤波后图像被平滑的程度取决于标准差。它的输出是领域像素的加权平均,同时离中心越近的像素权重越高。因此,相对于均值滤波(mean filter)它的平滑效果更柔和,而且边缘保留的也更好。
import cv2
image = cv2.imread(r'C:\Users\h\Desktop\image\lena.jpg', cv2.IMREAD_ANYCOLOR)
dst = cv2.GaussianBlur(image, (9,9), 0.0)
cv2.imshow("image", image)
cv2.imshow("GaussBlur", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.GaussianBlur(src, ksize, sigmaX, dst, sigmaY, borderType)
平滑窗口小,对标准差的变化不敏感,平滑效果差别不大。
平滑窗口大,对标准差的变化很敏感,平滑效果差别很大。
2 中值平滑
中值滤波法是一种非线性平滑算法,它将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值。
中值滤波模板就是用卷积框中像素的中值代替中心值,达到去噪声的目的。
前面的滤波器都是用计算得到的一个新值来取代中心像素的值,而中值滤波是用中心像素周围(也可以使他本身)的值来取代他,卷积核的大小也是个奇数。
这个模板一般用于去除椒盐噪声。
椒盐噪声:也称为脉冲噪声。在图像中,它是一种随机出现的白点或者黑点,可能是亮的区域有黑色像素或是在暗的区域有白色像素(或是两者皆有)。
import cv2
import numpy as np
#加椒盐噪声
def salt(image, number):
rows, cols = image.shape[:2]
saltImage = np.copy(image)
for i in range(number):
ranR = np.random.randint(0, rows) #随机产生整数
ranC = np.random.randint(0, cols)
saltImage[ranR][ranC] = 255
return saltImage
if __name__ == "__main__":
src1 = cv2.imread(r'C:\Users\h\Desktop\image\lena.jpg', cv2.IMREAD_ANYCOLOR)
src = salt(src1, 2500)
dst = cv2.medianBlur(src, 3)
cv2.imshow("image", src)
cv2.imshow("medianBlurImage", dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
3 双边滤波
双边滤波是一种非线性滤波器,它可以达到保持边缘、降噪平滑的效果。
和其他滤波原理一样,双边滤波也是采用加权平均的方法,用周边像素亮度值的加权平均代表某个像素的强度。
所用的加权平均基于高斯分布。
之所以能够达到保边去噪的滤波效果是因为滤波器由两个函数构成:一个函数是由几何空间距离决定滤波器系数,另一个是由像素差值决定滤波器系数。
高斯滤波器只考虑像素之间的空间关系,而不会考虑像素值之间的关系(像素的相似度)。所以这种方法不会考虑一个像素是否位于边界。因此边界也会别模糊掉,而这正不是我们想要 。
双边滤波在同时使用空间高斯权重和灰度值相似性高斯权重。空间高斯函数确保只有邻近区域的像素对中心点有影响,灰度值相似性高斯函数确保只有与中心像素灰度值相近的才会被用来做模糊运算。所以这种方法会确保边界不会被模糊掉,因为边界处的灰度值变化比较大。
import cv2
image = cv2.imread(r'C:\Users\x\Desktop\48.jpg', cv2.IMREAD_ANYCOLOR)
dst = cv2.bilateralFilter(image, 9, 5, 5)
dst1 = cv2.bilateralFilter(image, 9, 100, 100)
dst2 = cv2.bilateralFilter(image, 9, 50, 50)
cv2.imshow("image", image)
cv2.imshow('bilateralFilter', dst)
cv2.imshow('bilateralFilter1', dst1)
cv2.imshow('bilateralFilter2', dst2)
cv2.waitKey(0)
cv2.destroyAllWindows()
4 联合双边滤波
该算法可以使方形条纹消失,对边缘保留也非常好
联合双边滤波与双边滤波唯一不同:
-
双边滤波是根据原图,对于每个位置,通过该位置与其邻域的灰度值的差的指数来估计相似性
-
联合双边滤波是首先对原图进行高斯平滑,根据平滑结果,用当前的位置及其邻域的值的差来估计相似性权重模板。
import cv2
import numpy as np
import math
#空间距离权重模板
def getClosenessWeight(sigma_g, H, W):
r, c = np.mgrid[0:H:1, 0:W:1] #有两部分。[0]是y轴的范围。[1]是x轴的范围
#模板中心
cH = (H - 1) // 2
cW = (W - 1) // 2
r -= cH
c -= cW
closeWeight = np.exp(-0.5*(np.power(r,2)+np.power(c,2))/math.pow(sigma_g,2)) #sigma_g空间距离权重模板标准差
return closeWeight
def jointBLF(I, H, W, sigma_g, sigma_d, borderType=cv2.BORDER_DEFAULT):
#构建空间距离模板
closenessWeight = getClosenessWeight(sigma_g, H, W)
#对I进行平滑
Ig = cv2.GaussianBlur(I, (W,H), sigma_g)
#模板中心位置 ,(H x W) 是模板的大小
cH = (H - 1) // 2
cW = (W - 1) // 2
#对原图和高斯平滑的结果扩充边界
Ip = cv2.copyMakeBorder(I, cH, cH, cW, cW, borderType)
Igp = cv2.copyMakeBorder(Ig, cH, cH, cW, cW, borderType)
#图像矩阵的行、列数
rows, cols = I.shape
i, j = 0, 0
#联合双边滤波的结果
jblf = np.zeros(I.shape, np.float64)
for r in np.arange(cH, cH+rows, 1):
for c in np.arange(cW, cW+cols, 1):
#当前的位置的值
pixel = Igp[r][c]
#当前位置的邻域
rTop, rBottom, cLeft, cRight = r-cH, r+cH, c-cW, c+cW
#从Igp中截取该邻域,用于构建相似性权重模板
region = Igp[rTop:rBottom+1, cLeft:cRight+1]
#用上述邻域,构建该位置的相似性模板
similarityWeight = np.exp(-0.5*np.power(region-pixel,2.0)/math.pow(sigma_d,2.0))
#两个模板相乘
Weigth = closenessWeight * similarityWeight
#将权重模板归一化
Weigth = Weigth / np.sum(Weigth)
#权重模板和邻域相对应的位置相乘并求和
jblf[i][j] = np.sum(Ip[rTop:rBottom+1, cLeft:cRight+1]*Weigth)
j += 1
j = 0
i += 1
return jblf
if __name__ == "__main__":
Image = cv2.imread(r'C:\Users\x\Desktop\OpenCV-Pic\5\img7.jpg', cv2.IMREAD_GRAYSCALE)
#将8位图转为浮点型
fImage = Image.astype(np.float64)
#联合双边滤波返回值为浮点型
jblf = jointBLF(fImage, 5, 5, 7, 2)
#再转为8位图
jblf = np.round(jblf)
jblf = jblf.astype(np.uint8)
cv2.imshow("image", Image)
cv2.imshow("jblf", jblf)
cv2.waitKey(0)
cv2.destroyAllWindows()
节选自
原文:https://blog.csdn.net/qq_40755643/article/details/84035850