Python实现自适应中值滤波法去除椒盐噪声
三、常用的图像去噪算法
3.1 灰度图的介绍和获取
在处理一张图片之前,通常需要读取这张图片,得到一个与RGB三维矩阵或者是灰度图的二维矩阵,本次课堂设计所处理的图片为灰度图,Gray Scale Image 或是Grey Scale Image,又称灰阶图。把白色与黑色之间按对数关系分为 若干 等级,称为灰度。灰度分为256阶。
在Python语言里有很多方法可以获取灰度图,PIL库提供了获取灰度图的方法Image.open();同时,opencv库提供了获取图片RGB数据的方法,图片中任何颜色都由红、绿、蓝三基色组成,假如原来某点的颜色为RGB(R,G,B),那么,我们可以平均值法,将其转换为灰度, Gray=(R+G+B)/3;
3.2 常见噪声的介绍
常见的图像噪声可以分为两种,椒盐噪声和高斯噪声。椒盐噪声为在图片中随机产生的灰度值的0或者255的的像素点,本课堂设计中,写了一个函数
def AddNoise(imarray, probility=0.05, method= "salt_pepper" ):
这个函数实现的功能就是给图片添加椒盐噪声。
高斯噪声是指它的 概率密度函数 服从 高斯分布 (即 正态分布 )的一类噪声。常见的高斯噪声包括起伏噪声、宇宙噪声、热噪声和散粒噪声等等。
3.3 滤波算法的介绍
常用的图像去噪的方法有均值滤波,中值滤波,高斯滤波;
均值滤波:有效的消除一些高斯噪声,但是很容易导致图像边的模糊。如果是需要做图像分割,使用该滤波方法会导致分割失败。不易选择均值滤波;均值滤波法的原理如下图,取1个像素点周围了8个像素点的均值,然后让这个像素点的值等于其余8个像素点的平均值。
图一 均值滤波法的原理
中值滤波:以像素为中心,指定的滑窗形状作为邻域,将邻域内的像素排序,将中值结果赋值给该邻域的像素。该方法容易去除一些孤立噪声,也能够保留大部分的边缘信息。但是前提是选择合适的滑窗,否则也容易造成图像模糊;中值滤波法的原理如下图,取1个像素点周围了8个像素点的中值,然后让这个像素点的值等于其余8个像素点的中值。
图一 中值滤波法的原理
高斯滤波:高斯滤波不是单纯的求平均值或者中值,而是调用一个二维离散的高斯函数去除噪声。能够保留更多的边缘细节,图像更为清晰,平滑效果也更加柔和;
高斯滤波的原理如下图:
图三 高斯滤波法的原理
四、程序的运行截图
下面两张图片分别为将RGB原彩色图片,转换后的灰度图。
图四、五 原彩色图和灰度图
加入椒盐噪声后的图片:
图六 加入椒盐噪声后的图片
中值滤波法处理后的图片:
图七 中值滤波后的图片
源代码:
import cv2
import numpy as np
from PIL import Image
cv2.waitKey(0)
#添加椒盐噪声
def AddNoise(imarray, probility=0.05, method="salt_pepper"): # 灰度图像
#获取图片的长和宽
height, width = imarray.shape[:2]
for i in range(height):
for j in range(width):
if np.random.random(1) < probility: # 随机加盐或者加椒
if np.random.random(1) < 0.5:
imarray[i, j] = 0
else:
imarray[i, j] = 255
return imarray
#中值滤波法
def medianBlur(image, ksize=2 ):
中值滤波,去除椒盐噪声
args:
image:输入图片数据,要求为灰度图片
ksize:滤波窗口大小
return:
中值滤波之后的图片
rows, cols = image.shape[:2]
# 输入校验
half = ksize // 2
startSearchRow = half
endSearchRow = rows - half - 1
startSearchCol = half
endSearchCol = cols - half - 1
dst = np.zeros((rows, cols), dtype=np.uint8)
# 中值滤波
for y in range(startSearchRow, endSearchRow):
for x in range(startSearchCol, endSearchCol):
window = []
for i in range(y - half, y + half + 1):
for j in range(x - half, x + half + 1):
window.append(image[i][j])
# 取中间值
window = np.sort(window, axis=None)
if len(window) % 2 == 1:
medianValue = window[len(window) // 2]
else:
medianValue = int((window[len(window) // 2] + window[len(window) // 2 + 1]) / 2)
dst[y][x] = medianValue
return dst
#自适应中值滤波法
def amp_medianBlur(img, ksize=2 ):
# 图像边缘扩展
#为保证边缘的像素点可以被采集到,必须对原图进行像素扩展。
#一般设置的最大滤波窗口为7,所以只需要向上下左右各扩展3个像素即可采集到边缘像素。
m,n = img.shape[:2]
Nmax = 3
imgn = np.zeros((m + 2 * Nmax, n + 2 * Nmax),dtype=np.uint8)
imgn[ Nmax : (m + Nmax ) , Nmax : (n + Nmax ) ] = img[:,:,0].copy() # 将原图覆盖在imgn的正中间
# 下面开始向外扩展,即把边缘的像素向外复制
imgn[0: Nmax, Nmax: n + Nmax]=img[0: Nmax, 0 : n,0].copy() # 扩展上边界
imgn[0: m + Nmax, n + Nmax : n + 2 * Nmax]=imgn[0: m+Nmax, n: n + Nmax].copy() # 扩展右边界
imgn[m + Nmax: m + 2 * Nmax, Nmax : n + 2 * Nmax]=imgn[m : m + Nmax,Nmax : n + 2 * Nmax].copy() #扩展下边界
imgn[0: m + 2 * Nmax,0: Nmax]=imgn[0: m + 2 * Nmax,Nmax : 2 * Nmax].copy() # 扩展左边界
re = imgn.copy() # 扩展之后的图像
# 得到不是噪声点的中值
for i in range(Nmax,m+Nmax+1):
for j in range(Nmax,n+Nmax+1):
r = 1 # 初始向外扩张1像素,即滤波窗口大小为3
while r!=Nmax+1: #当滤波窗口小于等于7时(向外扩张元素小于4像素)
W = imgn[i - r-1:i + r,j - r-1: j + r].copy()
Imin,Imax = np.min(W),np.max(W) # 最小灰度值 # 最大灰度值
# 取中间值
window = np.sort(W, axis=None)
if len(window) % 2 == 1:
Imed = window[len(window) // 2]
else:
Imed = int((window[len(window) // 2] + window[len(window) // 2 + 1]) / 2)
if Imin < Imed and Imed < Imax: # 如果当前窗口中值不是噪声点,那么就用此次的中值为替换值
break;
else:
r = r + 1; #否则扩大窗口,继续判断,寻找不是噪声点的中值
# 判断当前窗口内的中心像素是否为噪声,是就用前面得到的中值替换,否则不替换
if Imin < imgn[i, j] and imgn[i, j] < Imax: # 如果当前这个像素不是噪声,原值输出
re[i, j] = imgn[i, j].copy()
else: # 否则输出邻域中值
re[i, j] = Imed
return re
#读取图片
image = cv2.imread("sgs.png")
width = image.shape[0]
height = image.shape[1]
grayimg = np.zeros([width,height,1],np.uint8)
for i in range(height):
for j in range(width):
grayimg[i,j] = 0.299 * image[i,j][0] + 0.587 * image[i,j][1] + 0.114 * image[i,j][2]
cv2.imshow('srcImage', image)
cv2.imshow('grayImage', grayimg)
img_addnoise = AddNoise(grayimg)
cv2.imshow('addnoise_Image', img_addnoise)
#remig2 = amp_medianBlur(img_addnoise)