大家都知道,NL-means去噪算法很大的缺陷就是它的运算速度太慢,故,我采取一种快速块匹配对之进行了改进
function [posIdx, weiIdx] = BM_NL_means_test(Img,par)
% Img : 输入图像
% posIdx : 与中心块相似的块的位置索引
% weiIdx : 与中心块相似的块的权重索引
search_r = par.s_r; %search block的半径
similarWinNum = par.nblk; % 相似window的个数
win_r = par.win_r; %similarity window的半径
win=2*win_r+1;%
winSize = win^2; % 每个similarity window的元素个数
hp = par.hp^2; % 高斯参数
step=1;
%在图像四周镜面填充厚度为win_r的像素点,行数和列数增加了win(2*win_r)个
%填充的目的是为了让每一个像素都能被计算到
PaddedImg = padarray(Img,[win_r,win_r],'symmetric','both');
[R , C] = size(PaddedImg);
% R=row+2*win_r C=col+2*win_r
N = R - win + 1; % 最后一个window的起始行坐标(步长1)
M = C - win + 1; % 最后一个window的起始列坐标
winNum = N * M; % window 的总个数
leftUpRow = 1:step:N; % 每个window的行起始坐标
leftUpRow = [leftUpRow leftUpRow(end)+1:N]; % 添加最后一个块,因为可能最后不一定正好取完
leftUpCol = 1:step:M; % 每个window的列起始坐标
leftUpCol = [leftUpCol leftUpCol(end)+1:M]; % 添加最后一个块,因为可能最后不一定正好取完
%分块:按列取一维块中不同位置的数据(连续的地址,速度可比常规提高10倍)!!!!!!
%这一小段是W dong学者BlockMatching中最精华部分!!!!
X = zeros(winSize, M*N, 'single');
k = 0;
for i = 1:win
for j = 1:win
k = k+1;%每次取R*C个window的第k个像素值
blk = PaddedImg(i:end-win+i,j:end-win+j);%这是算法精华所在噢~
X(k,:) = blk(:)';%每列皆是一个window的win*win个像素,共R2*C2列,完成降维
end
end
X = X';
I = reshape(1:winNum, N, M); % 所有块的索引
N1 = length(leftUpRow); % 多少行中心块
M1 = length(leftUpCol); % 多少列中心块中心块
posIdx = zeros(similarWinNum, N1*M1 ); % 每个中心块的相似块索引
weiIdx = zeros(similarWinNum, N1*M1 ); % 每个中心块对应的相似块的权重
%
rmin = bsxfun(@max, bsxfun(@minus, leftUpRow, search_r), 1); % 搜索窗的行最小坐标
rmax = bsxfun(@min, bsxfun(@plus, leftUpRow, search_r), N); % 搜索窗的行最大坐标
cmin = bsxfun(@max, bsxfun(@minus, leftUpCol, search_r), 1); % 搜索窗的列最小坐标
cmax = bsxfun(@min, bsxfun(@plus, leftUpCol, search_r), M); % 搜索窗的列最大坐标
%其实 经过padarry填充以及后来减去win宽度后,此处的N1,N2恰好是初始噪声图的维度一模一样的噢~(R,C)
% step=1是M1/N1是和M/N一样大的
for i = 1 : N1
for j = 1 : M1
offInAllWin = (leftUpCol(j)-1)*N + leftUpRow(i); % 当前中心块在所有块中的索引
offInCentralWin = (j-1)*N1 + i; % 当前中心块在所有中心块中的索引
idx = I(rmin(i):rmax(i), cmin(j):cmax(j)); %在由[rmin:rmax, cmin:cmax]确定的搜索窗中搜索相似块
idx = idx(:);%当前搜索框中所有块的索引
B = X(idx, :);
v = X(offInAllWin, :);
%将搜索窗口的所有window都与当前中心window求它们间的距离
dis = (B(:,1) - v(1)).^2;
for k = 2:winSize
dis = dis + (B(:,k) - v(k)).^2;
end
dis = dis ./ (winSize); % 归一化
similarWinInd = minN(dis, similarWinNum); % 找到距离最小的几个块的索引
posIdx(:,offInCentralWin) = idx(similarWinInd); % 保存相似块索引
wei = exp( -dis(similarWinNum) ./ hp ); % 高斯
weiIdx(:,offInCentralWin) = wei ./ (sum(wei(:))+eps); % 归一化
end
end
function index = minN(data, N)
% 功能 :找到 data 中最小的 N 个数,并返回索引
[~, index] = sort(data);
index = index(1:N);
主函数
par.s_r =5; % search window的半径
par.nblk =35; % 用来加权和去噪的相似块的个数(当然了,也可以是search win所有块)
par.win_r =2; % similarity window的半径
par.step=1;
par.hp = 13.5; % 高斯平滑参数
tic
I=double(imread('taylor.jpg'));
randn('state',0);%保证每次的高斯随机白噪声是一样的随机序列
Img=I+10*randn(size(I));
[row , col] = size(Img);
[posIdx, weiIdx]=BM_NL_means_test(Img,par);
pixel_posIdx = double(Img(posIdx));
Recon_X=sum((pixel_posIdx.*weiIdx),1);
Denoising_Im=reshape(Recon_X,row,col);
toc
% figure,imshow(I/256);%原图
% figure,imshow(Img/256);%加噪声后
% figure,imshow(Denoising_Im/256);%去噪后
参考文献和资料:
1.http://see.xidian.edu.cn/faculty/wsdong/wsdong_Publication.htm 《Sparsity-based Image Denoising via Dictionary Learning and Structural》源码中find_block_fast.m
2.《A non-local algorithm for image denoising》Antoni Buades, Bartomeu Coll等人
3. http://blog.csdn.net/wenxuegeng/article/details/51476980 Non Local Means-块匹配MATLAB和GPU实现
4. http://www.cnblogs.com/luo-peng/p/4785922.html 非局部均值去噪(NL-means)
若有疑问,欢迎邮件交流~