NLM全名non-Local Means——非局部均值,通过寻找图片中与当前像素相似的区域,计算出权重,再求和,即当前图像的去噪结果是图像中相似块的融合
原理非常简洁,以下图为例:
假设含噪声图像为
v
v
v,去噪图像为
u
u
u
那么输入输出的关系为:
其中 y y y的取值范围是整幅图像, w w w是代表了图像块 x x x, y y y的相似度,是一种权重,每个输出点都要对整个图像求权重,最后求整幅图像的加权和,其计算复杂度非常之高,想要实用就必须被简化,这里先介绍原始方法。
w
w
w的公式为:
h
h
h是控制去噪程度的系数,越大则去噪程度越高,图像也更模糊,具体取值应当以图像中的噪声水平为依据
V
(
x
)
V(x)
V(x)是输入像素点
x
x
x的窗
V
(
y
)
V(y)
V(y)是当前遍历到的像素点
y
y
y的窗
∣
∣
V
(
y
)
−
V
(
x
)
∣
∣
||V(y) - V(x)||
∣∣V(y)−V(x)∣∣就是两个窗内像素的均方误差
而
z
z
z代表归一化:
本文的代码限制了搜索的范围,不然实在是太慢了:
import cv2
import numpy as np
import math
img = cv2.imread('F:/hh/2.jpg')
img = cv2.resize(img,(500,500))
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
gray= gray.astype('float32')/255
size = img.shape # h w c
r = 1
search_range = 10
h = 0.05
sr = search_range
h2 = h*h
dest = gray.copy() #python中的变量更像指针,不可直接赋值
div = -1 * (2*r+1)*(2*r+1) * h2;
for y in range(r , size[0] - r):
print(y)
for x in range(r , size[1] - r):
srcblock = gray[y-r:y+r+1,x-r:x+r+1] #是y,x
#限制了搜索范围,不然实在太慢了,做个试验而已
y_start = max(y-search_range,r)
x_start = max(x-search_range,r)
y_end = min(y + search_range,size[0]- r - 1)
x_end = min(x + search_range,size[1]- r - 1)
w = np.zeros([y_end - y_start + 1,x_end - x_start + 1])
for yi in range(y_start , y_end + 1):
for xi in range(x_start , x_end + 1):
#运动估计简化计算?
refblock = gray[yi-r:yi+r+1,xi-r:xi+r+1]
delta = np.sum(np.square(srcblock-refblock))
#print(delta)
w[yi-y_start,xi-x_start] = math.exp(delta/div)
#print(yi,xi)
#time.sleep(1)
dest[y,x] = np.sum(w * gray[y_start:y_end + 1,x_start:x_end + 1])/np.sum(w)
cv2.imshow('result',dest)
结果如下: