本文主要讲述的是如何去除图像中周期性的线性噪声,尝试过的方法从空域的开关中值滤波到频域的陷波滤波等,在此做个总结,其中陷波滤波的尝试失败,效果并不理想,而开关中止滤波的效果很好。
图1:带周期性线条噪声的图片 |
1.实验过程
首先我是想用openCV自带的cv2.medianBlur()
来去除噪声,但是在测试过几组kernel过后发现:随着线条的消除,图片也会越来越模糊,小的kernel并不能很好的消除噪声,而大的kernel会让图片失真。然后我并没有很聪明的想到我最终的解决方案:开关中值滤波,而是转变了另一个方向:陷波滤波。
2.陷波滤波
先来说说陷波滤波吧。
陷波滤波是一种频域滤波,频域滤波的过程是先对图像进行正交变换(常用的是傅里叶变换),再对正交变换得到的正交变换域系数阵列进行滤波处理,然后逆变换到空域,得到处理结果图像。
傅里叶变换是把图像从空域变换到频域,图像的背景区域和缓慢变化部分对应频谱中的低频部分,而边
缘、细节、跳跃部分以及噪声则对应高频部分。线状和网格状噪声具有准周期性,噪声频谱与图像的频谱是可分离的,因此对于带有此类噪声的图像,可以通过频域滤波的方法来去除噪声。
理想陷波滤波器的传递函数:
H ( u , v ) = { 0 D 1 ( u , v ) < = D 0 0 D 2 ( u , v ) < = D 0 1 e l s e H(u,v)= \begin {cases} 0&D_1(u,v)<=D_0 \\0&D_2(u,v)<=D_0\\1&else \end {cases} H(u,v)=⎩⎪⎨⎪⎧001D1(u,v)<=D0D2(u,v)<=D0else
采用陷波滤波器去除线状和网格状噪声的关键在于如何准确地定位噪声对应的亮点并确定陷波滤波器参数。
对图1所示的叠加了水平线状噪声的图像进行傅里叶变换后,得到如图2所示的傅立叶频谱。
图2:傅里叶频谱 |
以其纵轴上各点的位置为横坐标(自上而下),以各点的频谱值为纵坐标,绘制的频谱曲线如图3所示。
图3: 纵轴频谱值 |
图5中的中心点及其附近点均是亮点,但不是噪声所对应的。这是因为中心点表示图像的平均亮度。因此,将检测范围限制在不包含中心点附近部分的区域。
寻找各点(不包含中心点及其附近)频谱值的最小值和最大值,分别记为 k m i n k_{min} kmin和 k m a x k_{max} kmax,令 T = T m i n + 3 ∗ ( T m a x − T m i n / 4 ) T=T_{min}+3*(T_{max}-T_{min}/4) T=Tmin+3∗(Tmax−Tmin/4), 如果某点的频谱值是局部最大值(即大于与它相邻点 的频谱值)且其值大于等于 T T T,那么该点的位置就是亮
点中心。计算每个亮点中心与其左侧第一个谷底(局部最小值所对应的点)之间的距离,记为 D 1 D_1 D1;与其右侧第一个谷底(局部最小值所对应的点)之间的距离,记为: D 2 D_2 D2。 D 1 D_1 D1和 D 2 D_2 D2:中的最大值作为陷波滤波器的带宽半径,这就是所需要的陷波滤波器参数。
但遗憾的是不知道后续步骤哪里出现了问题导致最终结果并不理想,可能是filter写的比较奇怪吧。。还在检查中。放出来给大家看一下结果:
奇怪的结果 |
3.开关中值滤波
开关中值滤波器是先检测噪声像素。然后只对检测到的噪声像素进行中值滤波。而被判断为非噪声的像素则不再参与中值滤波。
效果那是非常好,话不多说上代码:
def detec(img):(w,h) = img.shapeflt = np.ones((w,h),dtype = int)bright = []for y in range(h):bright.append(np.sum(img[:][y]))x=np.arange(512)plt.plot(x,bright,c='b')plt.ylabel('Brightness')plt.savefig('Brightness.jpg')for i in range(h):if bright[i]>100000:flt[:][i]=0return fltdef medianBlur(img):#只支持3*3的BlockSize(w,h) = img.shapefor i in range(2,w-1):for j in range(2,h-1):n = 0s = 0if img[i][j] == 0:for ii in range(i-1,i+1):for jj in range(j-1,j+1):if img[ii][jj] != 0:n = n + 1s = s + img[ii][jj]if n != 0:img[i][j] = int(s/n)return img
if __name__ == '__main__':fileName = 'line.jpg'gray = loadData(fileName)flt = detec(gray)grayWithoutNoise = gray*fltrow = np.zeros(512)col = np.zeros(514)grayWithoutNoise = np.row_stack((grayWithoutNoise,row))grayWithoutNoise = np.row_stack((row,grayWithoutNoise))grayWithoutNoise = np.column_stack((grayWithoutNoise,col))grayWithoutNoise = np.column_stack((col,grayWithoutNoise))mb = medianBlur(grayWithoutNoise)mb = mb[1:512,1:512] mb = np.array(mb,dtype=np.uint8)cv2.imshow('medianBlurWithDetection',mb)cv2.imwrite('medianBlurWithDetection.jpg',mb)cv2.waitKey(0)cv2.destroyAllWindows()
成功的结果 |