0%

图像去噪

为了操作方便,我们将原始输入的彩色图片转为黑白

1
2
3
4
5
6
7
8
9
10
11
12
def rgb2gray(adr):
img=cv2.imread(adr)
width = img.shape[0]
height = img.shape[1]

grayimg = np.zeros([width,height],np.uint8)
for i in range(width):
for j in range(height):
grayimg[i,j] = 0.299 * img[i,j][0] + 0.587 * img[i,j][1]
+ 0.114 * img[i,j][2]
cv2.imshow('grayimage', grayimg)
return grayimg

原始图像

alt text

灰度图像

alt text

添加椒盐噪声

1
2
3
4
5
6
7
8
9
10
11
12
13
def 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到图像长度之间的一个随机整数
Y=random.randint(0,img2.shape[1]-1)#因为是闭区间所以-1
if random.randint(0,1) ==0: #黑白色概率55开
img2[X,Y] = (255)#白色
else:
img2[X,Y] =(0)#黑色
#cv2.imshow("shabi_pepper_and_salt",img2)
return img2

加噪后图像

alt text

基于像素的均值、中值滤波的去噪

均值滤波

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def mean_fliter(img,size):
edge=int((size-1)/2)
width = img.shape[0]
height = img.shape[1]
img_fliter = np.zeros([width,height,1],np.uint8)
for i in range(width-size):
for j in range(height-size):
if i <= edge - 1 or i >= height - 1 - edge or
j <= edge - 1 or j >= height - edge - 1:
img_fliter[i, j] = img[i, j]
else:
img_fliter[i, j] = np.mean(img[i - edge:i + edge + 1,
j - edge:j + edge + 1])
cv2.imshow("mean fliter",img_fliter)

均值滤波图像

alt text

中值滤波

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def median_fliter(img,size):
edge=int((size-1)/2)
width = img.shape[0]
height = img.shape[1]
img_fliter = np.zeros([width,height,1],np.uint8)
for i in range(width-size):
for j in range(height-size):
if i <= edge - 1 or i >= height - 1
- edge or j <= edge - 1 or j >= height - edge - 1:
img_fliter[i, j] = img[i, j]
else:
img_fliter[i, j] = np.median(img[i - edge:i + edge + 1,
j - edge:j + edge + 1])
cv2.imshow("meadian fliter",img_fliter)

中值滤波图像

alt text

基于卷积的均值滤波

1
2
3
4
kernel = np.array([[1, 2, 1],
[2, 4, 2],
[1, 2, 1]])/16 # 定义卷积核
output = cv2.filter2D(img2,-1,kernel)# 进行卷积操作

卷积图像
alt text

非局部均值去噪

算法原理

非局部均值去噪 (NL-means)
非局部均值 (NL-means) 是近年来提出的一项新型的去噪技术。该方法充分利用了图像中的几余信息,在去噪的同时能最大程度地保持图像的细节特征。基本思想是:当前像素的估计值由图像中与它具有相似令域结构的像素加权平均得到。

理论上,该算法需要在整个图像范围内判断像素间的相似度,也就是说,每处理一个像素点时,都要计算它与图像中所有像素点间的相似度。但是考虑到效率问题,实现的时候,会设定两个固定大小的窗口: 搜索窗口 $(D \times D, D=2 * D s+1)$ 和邻域窗口 $(d \times d, d=2 * d s+1)$ 。邻域窗口在搜索窗口中滑动,根据邻域间的相似性确定像素的权值。

下图是NL-means算法执行过程,大窗口是以目标像素 $x$ 为中心的搜索窗口,两个灰色小窗口分别是以 $x 、 y$ 为中心的邻域窗口。其中以 $y$ 为中心的邻域窗口在搜索窗口中滑动,通过计算两个邻域窗口间的相似程度为 $y$赋以权值$w(x, y)$ 。

alt text

设含噪声图像为 $v$ ,去噪后的图像为 $\tilde{u}$ 。 $\tilde{u}$ 中像素点 $x$ 处的灰度值通过如下方式得到:
$$
\tilde{u}(x)=\sum_{y \in I} w(x, y) * v(y)
$$

其中权值 $w(x, y)$ 表示像素点 $x$ 和 $Y$ 间的相似度,它的值由以 $x, y$ 为中心的矩形邻域 $V(x) 、 V(y)$ 间的距离 $|V(x)-V(y)|^2$ 决定:
$$
w(x, y)=\frac{1}{Z(x)} \exp \left(-\frac{|V(x)-V(y)|^2}{h^2}\right)
$$

具中
$$
\begin{aligned}
& |V(x)-V(y)|^2=\frac{1}{d^2} \sum_{|z|_{\infty} \leq d s}|v(x+z)-v(y+z)|^2 \\
& Z(x)=\sum_y \exp \left(-\frac{|V(x)-V(y)|^2}{h^2}\right)
\end{aligned}
$$
$Z(x)$ 为归一化系数, $h$ 为平滑参数,控制高斯函数的衰减程度。 $h$ 越大高斯函数变化越平缓,去噪水平越高,但同时也会导致图像越模糊。 $h$ 越小,边缘细节成分保持得越多,但会残留过多的噪声点。 $h$ 的具体取值应当以图像中的噪声水平为依据。

具体内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def NLM_fliter(img):
img2 = np.zeros([img.shape[0],img.shape[1]],np.uint8)
for i in range(10,img.shape[0]-10):
for j in range(10,img.shape[1]-10):
search_window=img[i-10:i+11,j-10:j+11]
patch=img[i-3:i+4,j-3:j+4]
img2[i,j]=pixel_estimation(search_window,patch,100/256)
return img2
def pixel_estimation(search_window,patch,h):
window_height=search_window.shape[0]
window_width=search_window.shape[1]
patch_height=patch.shape[0]
patch_width=patch.shape[1]

similarity=np.zeros((window_height-patch_height//2*2,
window_width-patch_width//2*2))
for i in range(patch_height//2,window_height-patch_height//2):
for j in range(patch_width//2,window_width-patch_width//2):
temp=search_window[i-patch_height//2:i+patch_height//2+1,
j-patch_width//2:j+patch_width//2+1]
similarity[i-patch_height//2,j-patch_width//2]=weight_eucledian_distance(patch,temp,h)
Z=np.sum(similarity)
weights=np.zeros(search_window.shape)
for i in range(patch_height//2,window_height-patch_height//2):
for j in range(patch_width//2,window_width-patch_width//2):
weights[i,j]=1/Z*similarity[i-patch_height//2,j-patch_width//2]
NLM_estimation=np.sum(weights*search_window)
return NLM_estimation.astype(np.uint8)
def weight_eucledian_distance(patch1,patch2,h):
return np.exp(-(patch1-patch2)**2/(h*h))

NLM图像
alt text