均值滤波与中值滤波是最常见的两种平滑的方式,尤其是中值滤波能起到强大的降噪效果。
本文内容分为三部分:
1.实现添加图片的椒盐噪声
2.实现调用内置函数进行均值和中值滤波
3.自编函数深刻理解均值和中值滤波
1.何为椒盐噪声?如何添加椒盐噪声?
首先我们知道在图像当中的噪声实际上就是在图像中捣乱的像素点。
懂了之后椒盐噪声就很好理解了。
其实椒盐噪声(pepper and salt noise),就是字面意思。好像是黑胡椒和白盐的噪声. 具体来讲如下图所示:
左侧为原图 右侧为添加了4%椒盐噪声后的图片。
放大了看上面有黑色和白色的小点。我们只要在原有的图像中把相应的像素点随机改为黑色和白色的就好了.实现方法如下:
import cv2import randomdef pepper_and_salt(img,percentage):num=int(percentage*img.shape[0]*img.shape[1])# 椒盐噪声点数量random.randint(0, img.shape[0])img2=img.copy()for i in range(num):X=random.randint(0,img2.shape[0]-1)#从0到图像长度之间的一个随机整数,因为是闭区间所以-1Y=random.randint(0,img2.shape[1]-1)if random.randint(0,1) ==0: #黑白色概率55开img2[X,Y] = (255,255,255)#白色else:img2[X,Y] =(0,0,0)#黑色return img2img=cv2.imread("lena.tiff")img2 = pepper_and_salt(img,0.04)#百分之4的椒盐噪音cv2.imshow("lena",img)cv2.imshow("lena_pepper_and_salt",img2)cv2.waitKey(0)
2.实现图像均值滤波和中值滤波
二者的原理都十分简单,常见的方法中
均值就是把一个像素点九宫格(取决于你的输入)内所有点的rgb值加在一起再除以9,也就是取九宫格九个格子的平均值,来覆盖原有的rgb值。而中值滤波则就是取九个格子的中位数来覆盖原有的rgb值。
当然不一定是九宫格,这取决于你的输入,反正是一个你输入数字的n*n宫格的平均值(均值滤波) 或 中位数 (中值)
实现代码非常简单,如下所示:
img_averaging=cv2.blur(img2,(3,3)) #均值滤波img_median = cv2.medianBlur(img2,3) #中值滤波cv2.imshow("lena_averaging",img_averaging)cv2.imshow("lena_median",img_median)cv2.waitKey(0)
这里面要注意的就是中值滤波中的输入不能为偶数,你想你一个偶数取中位数不就有小数点了吗?或者是去前面和后面,就不准确了。所有只能是奇数
效果如下
你可以明显的看出来,均值滤波的效果不是十分明显,而中值滤波简直神了,几乎还原了原图。
到了这里,题目的要求就已经完成了。
3、自行实现两者
首先来说,一般来说如果有内置的函数尽量使用内置的函数,这样运行速度快,相对来讲兼容性要好而且方便。但是这里只是提供一个大致的编程思路。
话不多说了,因为逻辑上十分简单,所以实现起来也不困难。
直接上代码:
1.均值滤波
def averaging_filter(img,tuple_reform):img2=img.copy()X=img2.shape[0]Y=img2.shape[1]shift=int((tuple_reform[0]+tuple_reform[1]-2)/2)for x in range(shift,X-shift):for y in range(shift,Y-shift):pixel_r=0pixel_g=0pixel_b=0for i in range(tuple_reform[0]): #方位上取前3个的维度的for j in range(tuple_reform[1]):pixel_r+=int(img[x+i-shift,y+j-shift,0])pixel_g+=int(img[x+i-shift,y+j-shift,1])pixel_b+=int(img[x+i-shift,y+j-shift,2])num=tuple_reform[0]*tuple_reform[1]img2[x,y,0] = int(pixel_r/num)img2[x,y,1] = int(pixel_g/num)img2[x,y,2] = int(pixel_b/num)#该像素点等于9宫格内所有相加取平均而成,return img2
2.中值滤波
def median_filter(img,median):img2 = img.copy()X = img2.shape[0]Y = img2.shape[1]edge = int((median) -1/ 2) # 每条边上做这么多次shift = int(edge / 2) # 计算位置的偏移量edge+=1for x in range(shift, X - shift):for y in range(shift, Y - shift):List_r=[] #收集五个点的rgb值List_g=[]List_b=[]for e in range(2): #两种情况为0对x加减,为1对y加减for i in range(edge): # 方位上取前3个的维度的if e == 0:List_r.append(int(img2[x + i - shift, y, 0]))List_g.append(int(img2[x + i - shift, y, 1]))List_b.append(int(img2[x + i - shift, y, 2]))else:List_r.append(int(img2[x, y + i - shift, 0]))List_g.append(int(img2[x, y + i - shift, 1]))List_b.append(int(img2[x, y + i - shift, 2]))List_r.remove(img2[x,y,0]) #消除重复元素List_g.remove(img2[x, y, 1])List_b.remove(img2[x, y, 2])img2[x, y, 0] = np.median(List_r)img2[x, y, 1] = np.median(List_g)img2[x, y, 2] = np.median(List_b)return img2
提示:我所实现的中值滤波方式与正常的九宫格式不同,而是一个十字型阵列,以原来的那个像素点为十字交叉点,同样也是取中值。
且我实现的方法从算法的角度上不好,因为时间复杂度很高,再加上实现中值滤波时采用了每个像素点都要创建列表进行收集。
效果如下:
其实刚才有的小伙伴就以及想到了,我那样子去进行九宫格似的取中位数和平均那周围的点怎么办呢?
常见的处理办法有三种:
1.不做处理。就像我这里一样,因为边界很小,不影响大局,但是边上还是会存在一些噪声
2.边上的rgb值取0.这个方法会使得边上出现一个黑框
3.滤波后,边框上的点的rgb值等于 距离边框上每个像素点最近的像素点的rgb值.应该就是内置函数的操作,边上就不会有噪声