Repository: weitw/ImageDenoise Branch: master Commit: 94f314358bec Files: 151 Total size: 258.8 KB Directory structure: gitextract_hro3tm_6/ ├── BM3D/ │ ├── BM3D-SAPCA/ │ │ ├── BM3DSAPCA2009.p │ │ ├── README-BM3D-SAPCA.txt │ │ ├── demo_BM3DSAPCA.m │ │ ├── function_AnisLPAICI8.p │ │ ├── function_CreateLPAKernels.m │ │ ├── function_LPAKernelMatrixTheta.m │ │ ├── function_WOSFilters.p │ │ └── function_Window2D.m │ ├── BM3D.m │ ├── BM3DDEB.m │ ├── BM3DSHARP.m │ ├── BM3D_CFA.m │ ├── CBM3D.m │ ├── CVBM3D.m │ ├── ClipComp16b.p │ ├── IDDBM3D/ │ │ ├── BM3DDEB_init.m │ │ ├── BlockMatch.mexw32 │ │ ├── BlockMatch.mexw64 │ │ ├── Demo_IDDBM3D.m │ │ ├── GroupProcessor.mexw32 │ │ ├── GroupProcessor.mexw64 │ │ └── IDDBM3D.p │ ├── LEGAL_NOTICE.txt │ ├── README.txt │ ├── VBM3D.m │ ├── bm3d_CFA_thr.mexa64 │ ├── bm3d_CFA_thr.mexglx │ ├── bm3d_CFA_thr.mexmaci64 │ ├── bm3d_CFA_thr.mexw32 │ ├── bm3d_CFA_thr.mexw64 │ ├── bm3d_CFA_wiener.mexa64 │ ├── bm3d_CFA_wiener.mexglx │ ├── bm3d_CFA_wiener.mexmaci64 │ ├── bm3d_CFA_wiener.mexw32 │ ├── bm3d_CFA_wiener.mexw64 │ ├── bm3d_thr.mexa64 │ ├── bm3d_thr.mexglx │ ├── bm3d_thr.mexmaci │ ├── bm3d_thr.mexmaci64 │ ├── bm3d_thr.mexw32 │ ├── bm3d_thr.mexw64 │ ├── bm3d_thr_color.mexa64 │ ├── bm3d_thr_color.mexglx │ ├── bm3d_thr_color.mexmaci │ ├── bm3d_thr_color.mexmaci64 │ ├── bm3d_thr_color.mexw32 │ ├── bm3d_thr_color.mexw64 │ ├── bm3d_thr_colored_noise.mexa64 │ ├── bm3d_thr_colored_noise.mexglx │ ├── bm3d_thr_colored_noise.mexmaci │ ├── bm3d_thr_colored_noise.mexmaci64 │ ├── bm3d_thr_colored_noise.mexw32 │ ├── bm3d_thr_colored_noise.mexw64 │ ├── bm3d_thr_sharpen_var.mexa64 │ ├── bm3d_thr_sharpen_var.mexglx │ ├── bm3d_thr_sharpen_var.mexmaci │ ├── bm3d_thr_sharpen_var.mexmaci64 │ ├── bm3d_thr_sharpen_var.mexw32 │ ├── bm3d_thr_sharpen_var.mexw64 │ ├── bm3d_thr_video.mexa64 │ ├── bm3d_thr_video.mexglx │ ├── bm3d_thr_video.mexmaci │ ├── bm3d_thr_video.mexmaci64 │ ├── bm3d_thr_video.mexw32 │ ├── bm3d_thr_video.mexw64 │ ├── bm3d_thr_video_c.mexw32 │ ├── bm3d_thr_video_c.mexw64 │ ├── bm3d_wiener.mexa64 │ ├── bm3d_wiener.mexglx │ ├── bm3d_wiener.mexmaci │ ├── bm3d_wiener.mexmaci64 │ ├── bm3d_wiener.mexw32 │ ├── bm3d_wiener.mexw64 │ ├── bm3d_wiener_color.mexa64 │ ├── bm3d_wiener_color.mexglx │ ├── bm3d_wiener_color.mexmaci │ ├── bm3d_wiener_color.mexmaci64 │ ├── bm3d_wiener_color.mexw32 │ ├── bm3d_wiener_color.mexw64 │ ├── bm3d_wiener_colored_noise.mexa64 │ ├── bm3d_wiener_colored_noise.mexglx │ ├── bm3d_wiener_colored_noise.mexmaci │ ├── bm3d_wiener_colored_noise.mexmaci64 │ ├── bm3d_wiener_colored_noise.mexw32 │ ├── bm3d_wiener_colored_noise.mexw64 │ ├── bm3d_wiener_video.mexa64 │ ├── bm3d_wiener_video.mexglx │ ├── bm3d_wiener_video.mexmaci │ ├── bm3d_wiener_video.mexmaci64 │ ├── bm3d_wiener_video.mexw32 │ ├── bm3d_wiener_video.mexw64 │ ├── bm3d_wiener_video_c.mexw32 │ ├── bm3d_wiener_video_c.mexw64 │ └── main.m ├── DnCNN/ │ ├── Demo_FDnCNN_Color.m │ ├── Demo_FDnCNN_Color_Clip.m │ ├── Demo_FDnCNN_Gray.m │ ├── Demo_FDnCNN_Gray_Clip.m │ ├── Demo_test_CDnCNN_Specific.m │ ├── Demo_test_DnCNN.m │ ├── Demo_test_DnCNN3.m │ ├── Demo_test_DnCNN_C.m │ ├── model/ │ │ ├── DnCNN3.mat │ │ ├── FDnCNN_Clip_color.mat │ │ ├── FDnCNN_Clip_gray.mat │ │ ├── FDnCNN_color.mat │ │ ├── FDnCNN_gray.mat │ │ ├── GD_Color_Blind.mat │ │ ├── GD_Gray_Blind.mat │ │ ├── README.txt │ │ ├── specifics/ │ │ │ ├── sigma=10.mat │ │ │ ├── sigma=15.mat │ │ │ ├── sigma=20.mat │ │ │ ├── sigma=25.mat │ │ │ ├── sigma=30.mat │ │ │ ├── sigma=35.mat │ │ │ ├── sigma=40.mat │ │ │ ├── sigma=45.mat │ │ │ ├── sigma=50.mat │ │ │ ├── sigma=55.mat │ │ │ ├── sigma=60.mat │ │ │ ├── sigma=65.mat │ │ │ ├── sigma=70.mat │ │ │ └── sigma=75.mat │ │ └── specifics_color/ │ │ ├── Add (color) specific models.md │ │ ├── color_sigma=05.mat │ │ ├── color_sigma=10.mat │ │ ├── color_sigma=15.mat │ │ ├── color_sigma=25.mat │ │ ├── color_sigma=35.mat │ │ ├── color_sigma=50.mat │ │ ├── model_sigma=00to10.mat │ │ ├── model_sigma=20to30.mat │ │ ├── model_sigma=40to50.mat │ │ ├── model_sigma=60to70.mat │ │ └── model_sigma=80to90.mat │ └── utilities/ │ ├── Cal_PSNRSSIM.m │ ├── Merge_Bnorm_Demo.m │ ├── data_augmentation.m │ ├── modcrop.m │ ├── shave.m │ ├── sigma=25_Bnorm.mat │ ├── simplenn_matlab.m │ ├── vl_ffdnet_concise.m │ ├── vl_ffdnet_matlab.m │ ├── vl_simplenn.m │ └── vl_simplenn_mergebnorm.m ├── README.md ├── avefilter/ │ └── avefilt.m ├── medianfilter/ │ └── medianfilt.m └── nlm-image-denoising/ └── NLmeansfilt.m ================================================ FILE CONTENTS ================================================ ================================================ FILE: BM3D/BM3D-SAPCA/README-BM3D-SAPCA.txt ================================================ -------------------------------------------------------------------- BM3D-SAPCA : BM3D with Shape-Adaptive Principal Component Analysis v1.00, 2009 -------------------------------------------------------------------- Copyright (c) 2009-2011 Tampere University of Technology. All rights reserved. This work should be used for nonprofit purposes only. Author: Alessandro Foi BM3D web page: http://www.cs.tut.fi/~foi/GCF-BM3D BM3D-SAPCA is an algorithm for attenuation of additive white Gaussian noise (AWGN) from grayscale images. This software package reproduces the results from the article: K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "BM3D Image Denoising with Shape-Adaptive Principal Component Analysis", Proc. Workshop on Signal Processing with Adaptive Sparse Structured Representations (SPARS'09), Saint-Malo, France, April 2009. ( PDF available at http://www.cs.tut.fi/~foi/GCF-BM3D ) -------------------------------------------------------------------- This demo package includes routines from both the LASIP 2D demobox (http://www.cs.tut.fi/~lasip/2D/) and the Pointwise SA-DCT demobox (http://www.cs.tut.fi/~foi/SA-DCT/). -------------------------------------------------------------------- -------------------------------------------------------------------- Disclaimer -------------------------------------------------------------------- Any unauthorized use of these routines for industrial or profit- oriented activities is expressively prohibited. By downloading and/or using any of these files, you implicitly agree to all the terms of the TUT limited license, as specified in the document Legal_Notice.txt (included in this package) and online at http://www.cs.tut.fi/~foi/GCF-BM3D/legal_notice.html ================================================ FILE: BM3D/BM3D-SAPCA/demo_BM3DSAPCA.m ================================================ % BM3D-SAPCA : BM3D with Shape-Adaptive Principal Component Analysis (v1.00, 2009) % (demo script) % % BM3D-SAPCA is an algorithm for attenuation of additive white Gaussian noise (AWGN) % from grayscale images. This algorithm reproduces the results from the article: % K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "BM3D Image Denoising with % Shape-Adaptive Principal Component Analysis", Proc. Workshop on Signal Processing % with Adaptive Sparse Structured Representations (SPARS'09), Saint-Malo, France, % April 2009. (PDF available at http://www.cs.tut.fi/~foi/GCF-BM3D ) % % % SYNTAX: % % y_est = BM3DSAPCA2009(z, sigma) % % where z is an image corrupted by AWGN with noise standard deviation sigma % and y_est is an estimate of the noise-free image. % Signals are assumed on the intensity range [0,1]. % % % USAGE EXAMPLE: % % y = im2double(imread('Cameraman256.png')); % sigma=25/255; % z=y+sigma*randn(size(y)); % y_est = BM3DSAPCA2009(z,sigma); % % % % Copyright (c) 2009-2011 Tampere University of Technology. All rights reserved. % This work should only be used for nonprofit purposes. % % author: Alessandro Foi, email: firstname.lastname@tut.fi % %% clear all y = im2double(imread('Cameraman256.png')); % y = im2double(imread('Lena512.png')); randn('seed',0); sigma=25/255; z=y+sigma*randn(size(y)); y_est = BM3DSAPCA2009(z,sigma); PSNR = 10*log10(1/mean((y(:)-y_est(:)).^2)); disp(['PSNR = ',num2str(PSNR)]) if exist('ssim_index') [mssim ssim_map] = ssim_index(y*255, y_est*255); disp(['SSIM = ',num2str(mssim)]) end ================================================ FILE: BM3D/BM3D-SAPCA/function_CreateLPAKernels.m ================================================ % Creates LPA kernels cell array (function_CreateLPAKernels) % % Alessandro Foi - Tampere University of Technology - 2003-2005 % --------------------------------------------------------------- % % Builds kernels cell arrays kernels{direction,size} % and kernels_higher_order{direction,size,1:2} % kernels_higher_order{direction,size,1} is the 3D matrix % of all kernels for that particular direction/size % kernels_higher_order{direction,size,2} is the 2D matrix % containing the orders indices for the kernels % contained in kernels_higher_order{direction,size,1} % % --------------------------------------------------------------------- % % kernels_higher_order{direction,size,1}(:,:,1) is the funcion estimate kernel % kernels_higher_order{direction,size,1}(:,:,2) is a first derivative estimate kernel % % kernels_higher_order{direction,size,1}(:,:,n) is a higher order derivative estimate kernel % whose orders with respect to x and y are specified in % kernels_higher_order{direction,size,2}(n,:)= % =[xorder yorder xorder+yorder] % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function [kernels, kernels_higher_order]=function_createLPAkernels(m,h1,h2,TYPE,window_type,directional_resolution,sig_winds,beta) %-------------------------------------------------------------------------- % LPA ORDER AND KERNELS SIZES %-------------------------------------------------------------------------- % m=[2,0]; % THE VECTOR ORDER OF LPA; % h1=[1 2 3 4 5]; % sizes of the kernel % h2=[1 2 3 4 5]; % row vectors h1 and h2 need to have the same lenght %-------------------------------------------------------------------------- % WINDOWS PARAMETERS %-------------------------------------------------------------------------- % sig_winds=[h1*1 ; h1*1]; % Gaussian parameter % beta=1; % Parameter of window 6 % window_type=1 ; % window_type=1 for uniform, window_type=2 for Gaussian % window_type=6 for exponentions with beta % window_type=8 for Interpolation % TYPE=00; % TYPE IS A SYMMETRY OF THE WINDOW % 00 SYMMETRIC % 10 NONSYMMETRIC ON X1 and SYMMETRIC ON X2 % 11 NONSYMMETRIC ON X1,X2 (Quadrants) % % for rotated directional kernels the method that is used for rotation can be specified by adding % a binary digit in front of these types, as follows: % % 10 % 11 ARE "STANDARD" USING NN (Nearest Neighb.) (you can think of these numbers with a 0 in front) % 00 % % 110 % 111 ARE EXACT SAMPLING OF THE EXACT ROTATED KERNEL % 100 % % 210 % 211 ARE WITH BILINEAR INTERP % 200 % % 310 % 311 ARE WITH BICUBIC INTERP (not reccomended) % 300 %-------------------------------------------------------------------------- % DIRECTIONAL PARAMETERS %-------------------------------------------------------------------------- % directional_resolution=4; % number of directions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%% From this point onwards this file and the create_LPA_kernels.m should be identical %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% lenh=max(length(h1),length(h2)); clear kernels clear kernels_higher_order kernels=cell(directional_resolution,lenh); kernels_higher_order=cell(directional_resolution,lenh,2); THETASTEP=2*pi/directional_resolution; THETA=[0:THETASTEP:2*pi-THETASTEP]; s1=0; for theta=THETA, s1=s1+1; for s=1:lenh, [gh,gh1,gh1degrees]=function_LPAKernelMatrixTheta(ceil(h2(s)),ceil(h1(s)),window_type,[sig_winds(1,s) sig_winds(2,s)],TYPE,theta, m); kernels{s1,s}=gh; % degree=0 kernel kernels_higher_order{s1,s,1}=gh1; % degree>=0 kernels kernels_higher_order{s1,s,2}=gh1degrees; % polynomial indexes matrix end % different lengths loop end % different directions loop ================================================ FILE: BM3D/BM3D-SAPCA/function_LPAKernelMatrixTheta.m ================================================ % Return the discrete kernels for LPA estimation and their degrees matrix % % function [G, G1, index_polynomials]=function_LPAKernelMatrixTheta(h2,h1,window_type,sig_wind,TYPE,theta, m) % % % Outputs: % % G kernel for function estimation % G1 kernels for function and derivative estimation % G1(:,:,j), j=1 for function estimation, j=2 for d/dx, j=3 for d/dy, % contains 0 and all higher order kernels (sorted by degree: % 1 x y y^2 x^3 x^2y xy^2 y^3 etc...) % index_polynomials matrix of degrees first column x powers, second % column y powers, third column total degree % % % Inputs: % % h2, h1 size of the kernel (size of the "asymmetrical portion") % m=[m(1) m(2)] the vector order of the LPA any order combination should work % "theta" is an angle of the directrd window % "TYPE" is a type of the window support % "sig_wind" - vector - sigma parameters of the Gaussian wind % "beta"- parameter of the power in some weights for the window function % (these last 3 parameters are fed into function_Window2D function) % % % Alessandro Foi, 6 march 2004 function [G, G1, index_polynomials]=function_LPAKernelMatrixTheta(h2,h1,window_type,sig_wind,TYPE,theta, m) global beta %G1=0; m(1)=min(h1,m(1)); m(2)=min(h2,m(2)); % builds ordered matrix of the monomes powers number_of_polynomials=(min(m)+1)*(max(m)-min(m)+1)+(min(m)+1)*min(m)/2; % =size(index_polynomials,1) index_polynomials=zeros(number_of_polynomials,2); index3=1; for index1=1:min(m)+1 for index2=1:max(m)+2-index1 index_polynomials(index3,:)=[index1-1,index2-1]; index3=index3+1; end end if m(1)>m(2) index_polynomials=fliplr(index_polynomials); end index_polynomials(:,3)=index_polynomials(:,1)+index_polynomials(:,2); %calculates degrees of polynomials index_polynomials=sortrows(sortrows(index_polynomials,2),3); %sorts polynomials by degree (x first) %===================================================================================================================================== halfH=max(h1,h2); H=-halfH+1:halfH-1; % creates window function and then rotates it % win_fun=zeros(halfH-1,halfH-1); for x1=H for x2=H if TYPE==00|TYPE==200|TYPE==300 % SYMMETRIC WINDOW win_fun1(x2+halfH,x1+halfH)=function_Window2D(x1/h1/(1-1000*eps),x2/h2/(1-1000*eps),window_type,sig_wind,beta,h2/h1); % weight end if TYPE==11|TYPE==211|TYPE==311 % NONSYMMETRIC ON X1,X2 WINDOW win_fun1(x2+halfH,x1+halfH)=(x1>=-0.05)*(x2>=-0.05)*function_Window2D(x1/h1/(1-1000*eps),x2/h2/(1-1000*eps),window_type,sig_wind,beta,h2/h1); % weight end if TYPE==10|TYPE==210|TYPE==310 % NONSYMMETRIC ON X1 WINDOW win_fun1(x2+halfH,x1+halfH)=(x1>=-0.05)*function_Window2D(x1/h1/(1-1000*eps),x2/h2/(1-1000*eps),window_type,sig_wind,beta,h2/h1); % weight end if TYPE==100|TYPE==110|TYPE==111 % exact sampling xt1=x1*cos(-theta)+x2*sin(-theta); xt2=x2*cos(-theta)-x1*sin(-theta); if TYPE==100 % SYMMETRIC WINDOW win_fun1(x2+halfH,x1+halfH)=function_Window2D(xt1/h1/(1-1000*eps),xt2/h2/(1-1000*eps),window_type,sig_wind,beta,h2/h1); % weight end if TYPE==111 % NONSYMMETRIC ON X1,X2 WINDOW win_fun1(x2+halfH,x1+halfH)=(xt1>=-0.05)*(xt2>=-0.05)*function_Window2D(xt1/h1/(1-1000*eps),xt2/h2/(1-1000*eps),window_type,sig_wind,beta,h2/h1); % weight end if TYPE==110 % NONSYMMETRIC ON X1 WINDOW win_fun1(x2+halfH,x1+halfH)=(xt1>=-0.05)*function_Window2D(xt1/h1/(1-1000*eps),xt2/h2/(1-1000*eps),window_type,sig_wind,beta,h2/h1); % weight end end end end win_fun=win_fun1; if (theta~=0)&(TYPE<100) win_fun=imrotate(win_fun1,theta*180/pi,'nearest'); % use 'nearest' or 'bilinear' for different interpolation schemes ('bicubic'...?) end if (theta~=0)&(TYPE>=200)&(TYPE<300) win_fun=imrotate(win_fun1,theta*180/pi,'bilinear'); % use 'nearest' or 'bilinear' for different interpolation schemes ('bicubic'...?) end if (theta~=0)&(TYPE>=300) win_fun=imrotate(win_fun1,theta*180/pi,'bicubic'); % use 'nearest' or 'bilinear' for different interpolation schemes ('bicubic'...?) end % make the weight support a square win_fun2=zeros(max(size(win_fun))); win_fun2((max(size(win_fun))-size(win_fun,1))/2+1:max(size(win_fun))-((max(size(win_fun))-size(win_fun,1))/2),(max(size(win_fun))-size(win_fun,2))/2+1:max(size(win_fun))-((max(size(win_fun))-size(win_fun,2))/2))=win_fun; win_fun=win_fun2; %===================================================================================================================================== %%%% rotated coordinates H=-(size(win_fun,1)-1)/2:(size(win_fun,1)-1)/2; halfH=(size(win_fun,1)+1)/2; h_radious=halfH; Hcos=H*cos(theta); Hsin=H*sin(theta); %%%% Calculation of FI matrix FI=zeros(number_of_polynomials); i1=0; for s1=H i1=i1+1; i2=0; for s2=H i2=i2+1; x1=Hcos(s1+h_radious)-Hsin(s2+h_radious); x2=Hsin(s1+h_radious)+Hcos(s2+h_radious); phi=sqrt(win_fun(s2+halfH,s1+halfH))*(prod(((ones(number_of_polynomials,1)*[x1 x2]).^index_polynomials(:,1:2)),2)./prod(gamma(index_polynomials(:,1:2)+1),2).*(-ones(number_of_polynomials,1)).^index_polynomials(:,3)); FI=FI+phi*phi'; end % end of s2 end % end of s1 %FI_inv=((FI+1*eps*eye(size(FI)))^(-1)); % invert FI matrix FI_inv=pinv(FI); % invert FI matrix (using pseudoinverse) G1=zeros([size(H,2) size(H,2) number_of_polynomials]); %%%% Calculation of mask i1=0; for s1=H i1=i1+1; i2=0; for s2=H i2=i2+1; x1=Hcos(s1+h_radious)-Hsin(s2+h_radious); x2=Hsin(s1+h_radious)+Hcos(s2+h_radious); phi=FI_inv*win_fun(s2+halfH,s1+halfH)*(prod(((ones(number_of_polynomials,1)*[x1 x2]).^index_polynomials(:,1:2)),2)./prod(gamma(index_polynomials(:,1:2)+1),2).*(-ones(number_of_polynomials,1)).^index_polynomials(:,3)); G(i2,i1,1)=phi(1); % Function Est G1(i2,i1,:)=phi(:)'; % Function est & Der est on X Y etc... end % end of s1 end % end of s2 %keyboard ================================================ FILE: BM3D/BM3D-SAPCA/function_Window2D.m ================================================ % Returns a scalar/matrix weights (window function) for the LPA estimates % function w=function_Window2D(X,Y,window,sig_wind, beta); % X,Y scalar/matrix variables % window - type of the window weight % sig_wind - std scaling for the Gaussian ro-weight % beta -parameter of the degree in the weights %---------------------------------------------------------------------------------- % V. Katkovnik & A. Foi - Tampere University of Technology - 2002-2005 function w=function_Window2D(X,Y,window,sig_wind, beta,ratio); if nargin == 5 ratio=1; end IND=(abs(X)<=1)&(abs(Y)<=1); IND2=((X.^2+Y.^2)<=1); IND3=((X.^2+(Y*ratio).^2)<=1); if window==1 % rectangular symmetric window w=IND; end if window==2 %Gaussian X=X/sig_wind(1); Y=Y/sig_wind(2); w = IND.*exp(-(X.^2 + Y.^2)/2); %*(abs(Y)<=0.1*abs(X));%.*IND2; %((X.^2+Y.^2)<=1); end if window==3 % Quadratic window w=(1-(X.^2+Y.^2)).*((X.^2+Y.^2)<=1); end if window==4 % triangular symmetric window w=(1-abs(X)).*(1-abs(Y)).*((X.^2+Y.^2)<=1); end if window==5 % Epanechnikov symmetric window w=(1-X.^2).*(1-Y.^2).*((X.^2+Y.^2)<=1); end if window==6 % Generalized Gaussian X=X/sig_wind; Y=Y/sig_wind; w = exp(-((X.^2 + Y.^2).^beta)/2).*((X.^2+Y.^2)<=1); end if window==7 X=X/sig_wind; Y=Y/sig_wind; w = exp(-abs(X) - abs(Y)).*IND; end if window==8 % Interpolation w=(1./(abs(X).^4+abs(Y).^4+0.0001)).*IND2; end if window==9 % Interpolation NORM=(abs(X)).^2+(abs(Y)).^2+0.0001; w=(1./NORM.*(1-sqrt(NORM)).^2).*(NORM<=1); end if window==10 w=((X.^2+Y.^2)<=1); end if window==11 temp=asin(Y./sqrt(X.^2+Y.^2+eps)); temp=temp*0.6; % Width of Beam temp=(temp>0)*min(temp,1)+(temp<=0)*max(temp,-1); w=max(0,IND.*cos(pi*temp)); end if window==111 temp=asin(Y./sqrt(X.^2+Y.^2+eps)); temp=temp*0.8; % Width of Beam temp=(temp>0)*min(temp,1)+(temp<=0)*max(temp,-1); w=max(0,IND3.*(cos(pi*temp)>0)); % w=((X.^2+Y.^2)<=1); end if window==112 temp=atan(Y/(X+eps)); %temp=temp*0.8; % Width of Beam %temp=(temp>0)*min(temp,1)+(temp<=0)*max(temp,-1); w=max(0,IND3.*((abs(temp))<=pi/4)); % w=((X.^2+Y.^2)<=1); end ================================================ FILE: BM3D/BM3D.m ================================================ function [PSNR, SSIM, y_est] = BM3D(y, z, sigma, profile, print_to_screen) image_name = [ % 'montage.png' 'Cameraman256.png' % 'boat.png' % 'Lena512.png' % 'house.png' % 'barbara.png' % 'peppers256.png' % 'fingerprint.png' % 'couple.png' % 'hill.png' % 'man.png' ]; if (exist('profile') ~= 1) profile = 'np'; %% default profile end if (exist('sigma') ~= 1) sigma = 10; %% default standard deviation of the AWGN end %%%% Following are the parameters for the Normal Profile. %%%% Select transforms ('dct', 'dst', 'hadamard', or anything that is listed by 'help wfilters'): transform_2D_HT_name = 'bior1.5'; %% transform used for the HT filt. of size N1 x N1 transform_2D_Wiener_name = 'dct'; %% transform used for the Wiener filt. of size N1_wiener x N1_wiener transform_3rd_dim_name = 'haar'; %% transform used in the 3-rd dim, the same for HT and Wiener filt. %%%% Hard-thresholding (HT) parameters: N1 = 8; %% N1 x N1 is the block size used for the hard-thresholding (HT) filtering Nstep = 3; %% sliding step to process every next reference block N2 = 16; %% maximum number of similar blocks (maximum size of the 3rd dimension of a 3D array) Ns = 39; %% length of the side of the search neighborhood for full-search block-matching (BM), must be odd tau_match = 3000;%% threshold for the block-distance (d-distance) lambda_thr2D = 0; %% threshold parameter for the coarse initial denoising used in the d-distance measure lambda_thr3D = 2.7; %% threshold parameter for the hard-thresholding in 3D transform domain beta = 2.0; %% parameter of the 2D Kaiser window used in the reconstruction %%%% Wiener filtering parameters: N1_wiener = 8; Nstep_wiener = 3; N2_wiener = 32; Ns_wiener = 39; tau_match_wiener = 400; beta_wiener = 2.0; %%%% Block-matching parameters: stepFS = 1; %% step that forces to switch to full-search BM, "1" implies always full-search smallLN = 'not used in np'; %% if stepFS > 1, then this specifies the size of the small local search neighb. stepFSW = 1; smallLNW = 'not used in np'; thrToIncStep = 8; % if the number of non-zero coefficients after HT is less than thrToIncStep, % than the sliding step to the next reference block is incresed to (nm1-1) if strcmp(profile, 'lc') == 1 Nstep = 6; Ns = 25; Nstep_wiener = 5; N2_wiener = 16; Ns_wiener = 25; thrToIncStep = 3; smallLN = 3; stepFS = 6*Nstep; smallLNW = 2; stepFSW = 5*Nstep_wiener; end if (strcmp(profile, 'vn') == 1) || (sigma > 40) N2 = 32; Nstep = 4; N1_wiener = 11; Nstep_wiener = 6; lambda_thr3D = 2.8; thrToIncStep = 3; tau_match_wiener = 3500; tau_match = 25000; Ns_wiener = 39; end % The 'vn_old' profile corresponds to the original parameters for strong noise proposed in [1]. if (strcmp(profile, 'vn_old') == 1) && (sigma > 40) transform_2D_HT_name = 'dct'; N1 = 12; Nstep = 4; N1_wiener = 11; Nstep_wiener = 6; lambda_thr3D = 2.8; lambda_thr2D = 2.0; thrToIncStep = 3; tau_match_wiener = 3500; tau_match = 5000; Ns_wiener = 39; end decLevel = 0; %% dec. levels of the dyadic wavelet 2D transform for blocks (0 means full decomposition, higher values decrease the dec. number) thr_mask = ones(N1); %% N1xN1 mask of threshold scaling coeff. --- by default there is no scaling, however the use of different thresholds for different wavelet decompoistion subbands can be done with this matrix if strcmp(profile, 'high') == 1 %% this profile is not documented in [1] decLevel = 1; Nstep = 2; Nstep_wiener = 2; lambda_thr3D = 2.5; vMask = ones(N1,1); vMask((end/4+1):end/2)= 1.01; vMask((end/2+1):end) = 1.07; %% this allows to have different threhsolds for the finest and next-to-the-finest subbands thr_mask = vMask * vMask'; beta = 2.5; beta_wiener = 1.5; end %%% Check whether to dump information to the screen or remain silent dump_output_information = 1; if (exist('print_to_screen') == 1) && (print_to_screen == 0) dump_output_information = 0; end %%%% Create transform matrices, etc. %%%% [Tfor, Tinv] = getTransfMatrix(N1, transform_2D_HT_name, decLevel); %% get (normalized) forward and inverse transform matrices [TforW, TinvW] = getTransfMatrix(N1_wiener, transform_2D_Wiener_name, 0); %% get (normalized) forward and inverse transform matrices if (strcmp(transform_3rd_dim_name, 'haar') == 1) || (strcmp(transform_3rd_dim_name(end-2:end), '1.1') == 1) %%% If Haar is used in the 3-rd dimension, then a fast internal transform is used, thus no need to generate transform %%% matrices. hadper_trans_single_den = {}; inverse_hadper_trans_single_den = {}; else %%% Create transform matrices. The transforms are later applied by %%% matrix-vector multiplication for the 1D case. for hpow = 0:ceil(log2(max(N2,N2_wiener))) h = 2^hpow; [Tfor3rd, Tinv3rd] = getTransfMatrix(h, transform_3rd_dim_name, 0); hadper_trans_single_den{h} = single(Tfor3rd); inverse_hadper_trans_single_den{h} = single(Tinv3rd'); end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% 2D Kaiser windows used in the aggregation of block-wise estimates %%%% if beta_wiener==2 && beta==2 && N1_wiener==8 && N1==8 % hardcode the window function so that the signal processing toolbox is not needed by default Wwin2D = [ 0.1924 0.2989 0.3846 0.4325 0.4325 0.3846 0.2989 0.1924; 0.2989 0.4642 0.5974 0.6717 0.6717 0.5974 0.4642 0.2989; 0.3846 0.5974 0.7688 0.8644 0.8644 0.7688 0.5974 0.3846; 0.4325 0.6717 0.8644 0.9718 0.9718 0.8644 0.6717 0.4325; 0.4325 0.6717 0.8644 0.9718 0.9718 0.8644 0.6717 0.4325; 0.3846 0.5974 0.7688 0.8644 0.8644 0.7688 0.5974 0.3846; 0.2989 0.4642 0.5974 0.6717 0.6717 0.5974 0.4642 0.2989; 0.1924 0.2989 0.3846 0.4325 0.4325 0.3846 0.2989 0.1924]; Wwin2D_wiener = Wwin2D; else Wwin2D = kaiser(N1, beta) * kaiser(N1, beta)'; % Kaiser window used in the aggregation of the HT part Wwin2D_wiener = kaiser(N1_wiener, beta_wiener) * kaiser(N1_wiener, beta_wiener)'; % Kaiser window used in the aggregation of the Wiener filt. part end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% If needed, read images, generate noise, or scale the images to the %%%% [0,1] interval %%%% if (exist('y') ~= 1) || (exist('z') ~= 1) y = im2double(imread(image_name)); %% read a noise-free image and put in intensity range [0,1] randn('seed', 0); %% generate seed z = y + (sigma/255)*randn(size(y)); %% create a noisy image else % external images image_name = 'External image'; % convert z to double precision if needed z = double(z); % convert y to double precision if needed y = double(y); % if z's range is [0, 255], then convert to [0, 1] if (max(z(:)) > 10) % a naive check for intensity range z = z / 255; end % if y's range is [0, 255], then convert to [0, 1] if (max(y(:)) > 10) % a naive check for intensity range y = y / 255; end end if (size(z,3) ~= 1) || (size(y,3) ~= 1) error('BM3D accepts only grayscale 2D images.'); end % Check if the true image y is a valid one; if not, then we cannot compute PSNR, etc. y_is_invalid_image = (length(size(z)) ~= length(size(y))) | (size(z,1) ~= size(y,1)) | (size(z,2) ~= size(y,2)); if (y_is_invalid_image) dump_output_information = 0; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Print image information to the screen %%%% if dump_output_information == 1 fprintf('Image: %s (%dx%d), sigma: %.1f\n', image_name, size(z,1), size(z,2), sigma); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Step 1. Produce the basic estimate by HT filtering %%%% tic; y_hat = bm3d_thr(z, hadper_trans_single_den, Nstep, N1, N2, lambda_thr2D,... lambda_thr3D, tau_match*N1*N1/(255*255), (Ns-1)/2, (sigma/255), thrToIncStep, single(Tfor), single(Tinv)', inverse_hadper_trans_single_den, single(thr_mask), Wwin2D, smallLN, stepFS ); estimate_elapsed_time = toc; if dump_output_information == 1 PSNR_INITIAL_ESTIMATE = 10*log10(1/mean((y(:)-double(y_hat(:))).^2)); fprintf('BASIC ESTIMATE, PSNR: %.2f dB\n', PSNR_INITIAL_ESTIMATE); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Step 2. Produce the final estimate by Wiener filtering (using the %%%% hard-thresholding initial estimate) %%%% tic; y_est = bm3d_wiener(z, y_hat, hadper_trans_single_den, Nstep_wiener, N1_wiener, N2_wiener, ... 'unused arg', tau_match_wiener*N1_wiener*N1_wiener/(255*255), (Ns_wiener-1)/2, (sigma/255), 'unused arg', single(TforW), single(TinvW)', inverse_hadper_trans_single_den, Wwin2D_wiener, smallLNW, stepFSW, single(ones(N1_wiener)) ); wiener_elapsed_time = toc; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Calculate the final estimate's PSNR, print it, and show the %%%% denoised image next to the noisy one %%%% y_est = double(y_est); PSNR = 0; %% Remains 0 if the true image y is not available SSIM = 0; if (~y_is_invalid_image) % checks if y is a valid image PSNR = 10*log10(1/mean((y(:)-y_est(:)).^2)); % y is valid SSIM = ssim(y, y_est); end if dump_output_information == 1 fprintf('FINAL ESTIMATE (total time: %.1f sec), PSNR: %.2f dB\n', ... wiener_elapsed_time + estimate_elapsed_time, PSNR); figure, imshow(z); title(sprintf('Noisy %s, PSNR: %.3f dB (sigma: %d)', ... image_name(1:end-4), 10*log10(1/mean((y(:)-z(:)).^2)), sigma)); figure, imshow(y_est); title(sprintf('Denoised %s, PSNR: %.3f dB', ... image_name(1:end-4), PSNR)); end return; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Some auxiliary functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % Create forward and inverse transform matrices, which allow for perfect % reconstruction. The forward transform matrix is normalized so that the % l2-norm of each basis element is 1. % % [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % INPUTS: % % N --> Size of the transform (for wavelets, must be 2^K) % % transform_type --> 'dct', 'dst', 'hadamard', or anything that is % listed by 'help wfilters' (bi-orthogonal wavelets) % 'DCrand' -- an orthonormal transform with a DC and all % the other basis elements of random nature % % dec_levels --> If a wavelet transform is generated, this is the % desired decomposition level. Must be in the % range [0, log2(N)-1], where "0" implies % full decomposition. % % OUTPUTS: % % Tforward --> (N x N) Forward transform matrix % % Tinverse --> (N x N) Inverse transform matrix % if exist('dec_levels') ~= 1 dec_levels = 0; end if N == 1 Tforward = 1; elseif strcmp(transform_type, 'hadamard') == 1 Tforward = hadamard(N); elseif (N == 8) && strcmp(transform_type, 'bior1.5')==1 % hardcoded transform so that the wavelet toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.219417649252501 0.449283757993216 0.449283757993216 0.219417649252501 -0.219417649252501 -0.449283757993216 -0.449283757993216 -0.219417649252501; 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846 -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284; -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846; 0.707106781186547 -0.707106781186547 0 0 0 0 0 0; 0 0 0.707106781186547 -0.707106781186547 0 0 0 0; 0 0 0 0 0.707106781186547 -0.707106781186547 0 0; 0 0 0 0 0 0 0.707106781186547 -0.707106781186547]; elseif (N == 8) && strcmp(transform_type, 'dct')==1 % hardcoded transform so that the signal processing toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.490392640201615 0.415734806151273 0.277785116509801 0.097545161008064 -0.097545161008064 -0.277785116509801 -0.415734806151273 -0.490392640201615; 0.461939766255643 0.191341716182545 -0.191341716182545 -0.461939766255643 -0.461939766255643 -0.191341716182545 0.191341716182545 0.461939766255643; 0.415734806151273 -0.097545161008064 -0.490392640201615 -0.277785116509801 0.277785116509801 0.490392640201615 0.097545161008064 -0.415734806151273; 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274; 0.277785116509801 -0.490392640201615 0.097545161008064 0.415734806151273 -0.415734806151273 -0.097545161008064 0.490392640201615 -0.277785116509801; 0.191341716182545 -0.461939766255643 0.461939766255643 -0.191341716182545 -0.191341716182545 0.461939766255643 -0.461939766255643 0.191341716182545; 0.097545161008064 -0.277785116509801 0.415734806151273 -0.490392640201615 0.490392640201615 -0.415734806151273 0.277785116509801 -0.097545161008064]; elseif (N == 8) && strcmp(transform_type, 'dst')==1 % hardcoded transform so that the PDE toolbox is not needed to generate it Tforward = [ 0.161229841765317 0.303012985114696 0.408248290463863 0.464242826880013 0.464242826880013 0.408248290463863 0.303012985114696 0.161229841765317; 0.303012985114696 0.464242826880013 0.408248290463863 0.161229841765317 -0.161229841765317 -0.408248290463863 -0.464242826880013 -0.303012985114696; 0.408248290463863 0.408248290463863 0 -0.408248290463863 -0.408248290463863 0 0.408248290463863 0.408248290463863; 0.464242826880013 0.161229841765317 -0.408248290463863 -0.303012985114696 0.303012985114696 0.408248290463863 -0.161229841765317 -0.464242826880013; 0.464242826880013 -0.161229841765317 -0.408248290463863 0.303012985114696 0.303012985114696 -0.408248290463863 -0.161229841765317 0.464242826880013; 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863; 0.303012985114696 -0.464242826880013 0.408248290463863 -0.161229841765317 -0.161229841765317 0.408248290463863 -0.464242826880013 0.303012985114696; 0.161229841765317 -0.303012985114696 0.408248290463863 -0.464242826880013 0.464242826880013 -0.408248290463863 0.303012985114696 -0.161229841765317]; elseif strcmp(transform_type, 'dct') == 1 Tforward = dct(eye(N)); elseif strcmp(transform_type, 'dst') == 1 Tforward = dst(eye(N)); elseif strcmp(transform_type, 'DCrand') == 1 x = randn(N); x(1:end,1) = 1; [Q,R] = qr(x); if (Q(1) < 0) Q = -Q; end Tforward = Q'; else %% a wavelet decomposition supported by 'wavedec' %%% Set periodic boundary conditions, to preserve bi-orthogonality dwtmode('per','nodisp'); Tforward = zeros(N,N); for i = 1:N Tforward(:,i)=wavedec(circshift([1 zeros(1,N-1)],[dec_levels i-1]), log2(N), transform_type); %% construct transform matrix end end %%% Normalize the basis elements Tforward = (Tforward' * diag(sqrt(1./sum(Tforward.^2,2))))'; %%% Compute the inverse transform matrix Tinverse = inv(Tforward); return; ================================================ FILE: BM3D/BM3DDEB.m ================================================ function [ISNR, y_hat_RWI] = BM3DDEB(experiment_number, test_image_name) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Copyright (c) 2008-2014 Tampere University of Technology. All rights reserved. % This work should only be used for nonprofit purposes. % % AUTHORS: % Kostadin Dabov % Alessandro Foi email: alessandro.foi _at_ tut.fi %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % This function implements the image deblurring method proposed in: % % [1] K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "Image % restoration by sparse 3D transform-domain collaborative filtering," % Proc SPIE Electronic Imaging, January 2008. % % FUNCTION INTERFACE: % % [PSNR, y_hat_RWI] = BM3DDEB(experiment_number, test_image_name) % % INPUT: % 1) experiment_number: 1 -> PSF 1, sigma^2 = 2 % 2 -> PSF 1, sigma^2 = 8 % 3 -> PSF 2, sigma^2 = 0.308 % 4 -> PSF 3, sigma^2 = 49 % 5 -> PSF 4, sigma^2 = 4 % 6 -> PSF 5, sigma^2 = 64 % % 2) test_image_name: a valid filename of a grayscale test image % % OUTPUT: % 1) ISNR: the output improvement in SNR, dB % 2) y_hat_RWI: the restored image % % ! The function can work without any of the input arguments, % in which case, the internal default ones are used ! % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Fixed regularization parameters (obtained empirically after a rough optimization) Regularization_alpha_RI = 4e-4; Regularization_alpha_RWI = 5e-3; %%%% Experiment number (see below for details, e.g. how the blur is generated, etc.) if (exist('experiment_number') ~= 1) experiment_number = 3; % 1 -- 6 end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Select a single image filename (might contain path) %%%% if (exist('test_image_name') ~= 1) test_image_name = [ % 'Lena512.png' 'Cameraman256.png' % 'barbara.png' % 'house.png' ]; end %%%% Select 2D transforms ('dct', 'dst', 'hadamard', or anything that is listed by 'help wfilters'): transform_2D_HT_name = 'dst'; %% 2D transform (of size N1 x N1) used in Step 1 transform_2D_Wiener_name = 'dct'; %% 2D transform (of size N1_wiener x N1_wiener) used in Step 2 transform_3rd_dimage_name = 'haar'; %% 1D tranform used in the 3-rd dim, the same for both steps %%%% Step 1 (BM3D with collaborative hard-thresholding) parameters: N1 = 8; %% N1 x N1 is the block size Nstep = 3; %% sliding step to process every next refernece block N2 = 16; %% maximum number of similar blocks (maximum size of the 3rd dimensiona of a 3D array) Ns = 39; %% length of the side of the search neighborhood for full-search block-matching (BM) tau_match = 6000;%% threshold for the block distance (d-distance) lambda_thr2D = 0; %% threshold for the coarse initial denoising used in the d-distance measure lambda_thr3D = 2.9; %% threshold for the hard-thresholding beta = 0; %% the beta parameter of the 2D Kaiser window used in the reconstruction %%%% Step 2 (BM3D with collaborative Wiener filtering) parameters: N1_wiener = 8; Nstep_wiener = 2; N2_wiener = 16; Ns_wiener = 39; tau_match_wiener = 800; beta_wiener = 0; %%%% Specify whether to print results and display images print_to_screen = 1; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Note: touch below this point only if you know what you are doing! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Make parameters compatible with the interface of the mex-functions %%%% [Tfor, Tinv] = getTransfMatrix(N1, transform_2D_HT_name, 0); %% get (normalized) forward and inverse transform matrices [TforW, TinvW] = getTransfMatrix(N1_wiener, transform_2D_Wiener_name, 0); %% get (normalized) forward and inverse transform matrices if (strcmp(transform_3rd_dimage_name, 'haar') == 1), %%% Fast internal transform is used, no need to generate transform %%% matrices. hadper_trans_single_den = {}; inverse_hadper_trans_single_den = {}; else %%% Create transform matrices. The transforms are later applied by %%% vector-matrix multiplications for hpow = 0:ceil(log2(max(N2,N2_wiener))), h = 2^hpow; [Tfor3rd, Tinv3rd] = getTransfMatrix(h, transform_3rd_dimage_name, 0); hadper_trans_single_den{h} = single(Tfor3rd); inverse_hadper_trans_single_den{h} = single(Tinv3rd'); end end if beta == 0 & beta_wiener == 0 Wwin2D = ones(N1,N1); Wwin2D_wiener = ones(N1_wiener,N1_wiener); else Wwin2D = kaiser(N1, beta) * kaiser(N1, beta)'; % Kaiser window used in the hard-thresholding part Wwin2D_wiener = kaiser(N1_wiener, beta_wiener) * kaiser(N1_wiener, beta_wiener)'; % Kaiser window used in the Wiener filtering part end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Read an image and generate a blurred and noisy image %%%% y = im2double(imread(test_image_name)); if experiment_number==1 sigma=sqrt(2)/255; for x1=-7:7; for x2=-7:7; v(x1+8,x2+8)=1/(x1^2+x2^2+1); end, end; v=v./sum(v(:)); end if experiment_number==2 sigma=sqrt(8)/255; s1=0; for a1=-7:7; s1=s1+1; s2=0; for a2=-7:7; s2=s2+1; v(s1,s2)=1/(a1^2+a2^2+1); end, end; v=v./sum(v(:)); end if experiment_number==3 BSNR=40; sigma=-1; % if "sigma=-1", then the value of sigma depends on the BSNR v=ones(9); v=v./sum(v(:)); end if experiment_number==4 sigma=7/255; v=[1 4 6 4 1]'*[1 4 6 4 1]; v=v./sum(v(:)); % PSF end if experiment_number==5 sigma=2/255; v=fspecial('gaussian', 25, 1.6); end if experiment_number==6 sigma=8/255; v=fspecial('gaussian', 25, .4); end [Xv, Xh] = size(y); [ghy,ghx] = size(v); big_v = zeros(Xv,Xh); big_v(1:ghy,1:ghx)=v; big_v=circshift(big_v,-round([(ghy-1)/2 (ghx-1)/2])); % pad PSF with zeros to whole image domain, and center it V = fft2(big_v); % frequency response of the PSF y_blur = imfilter(y, v(end:-1:1,end:-1:1), 'circular'); % performs blurring (by circular convolution) randn('seed',0); %%% fix seed for the random number generator if sigma == -1; %% check whether to use BSNR in order to define value of sigma sigma=sqrt(norm(y_blur(:)-mean(y_blur(:)),2)^2 /(Xh*Xv*10^(BSNR/10))); % compute sigma from the desired BSNR end %%%% Create a blurred and noisy observation z = y_blur + sigma*randn(Xv,Xh); tic; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Step 1: Final estimate by Regularized Inversion (RI) followed by %%%% BM3D with collaborative hard-thresholding %%%% %%%% Step 1.1. Regularized Inversion RI= conj(V)./( (abs(V).^2) + Regularization_alpha_RI * Xv*Xh*sigma^2); % Transfer Matrix for RI %% Standard Tikhonov Regularization zRI=real(ifft2( fft2(z).* RI )); % Regularized Inverse Estimate (RI OBSERVATION) stdRI = zeros(N1, N1); for ii = 1:N1, for jj = 1:N1, UnitMatrix = zeros(N1,N1); UnitMatrix(ii,jj)=1; BasisElementPadded = zeros(Xv, Xh); BasisElementPadded(1:N1,1:N1) = Tinv*UnitMatrix*Tinv'; TransfBasisElementPadded = fft2(BasisElementPadded); stdRI(ii,jj) = sqrt( (1/(Xv*Xh)) * sum(sum(abs(TransfBasisElementPadded.*RI).^2)) )*sigma; end, end %%%% Step 1.2. Colored noise suppression by BM3D with collaborative hard- %%%% thresholding y_hat_RI = bm3d_thr_colored_noise(zRI, hadper_trans_single_den, Nstep, N1, N2, lambda_thr2D,... lambda_thr3D, tau_match*N1*N1/(255*255), (Ns-1)/2, sigma, 0, single(Tfor), single(Tinv)',... inverse_hadper_trans_single_den, single(stdRI'), Wwin2D, 0, 1 ); PSNR_INITIAL_ESTIMATE = 10*log10(1/mean((y(:)-y_hat_RI(:)).^2)); ISNR_INITIAL_ESTIMATE = PSNR_INITIAL_ESTIMATE - 10*log10(1/mean((y(:)-z(:)).^2)); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Step 2: Final estimate by Regularized Wiener Inversion (RWI) followed %%%% by BM3D with collaborative Wiener filtering %%%% %%%% Step 2.1. Regularized Wiener Inversion Wiener_Pilot = abs(fft2(double(y_hat_RI))); %%% Wiener reference estimate RWI = conj(V).*Wiener_Pilot.^2./(Wiener_Pilot.^2.*(abs(V).^2) + Regularization_alpha_RWI*Xv*Xh*sigma^2); % Transfer Matrix for RWI (uses standard regularization 'a-la-Tikhonov') zRWI = real(ifft2(fft2(z).*RWI)); % RWI OBSERVATION stdRWI = zeros(N1_wiener, N1_wiener); for ii = 1:N1_wiener, for jj = 1:N1_wiener, UnitMatrix = zeros(N1_wiener,N1_wiener); UnitMatrix(ii,jj)=1; BasisElementPadded = zeros(Xv, Xh); BasisElementPadded(1:N1_wiener,1:N1_wiener) = TinvW*UnitMatrix*TinvW'; TransfBasisElementPadded = fft2(BasisElementPadded); stdRWI(ii,jj) = sqrt( (1/(Xv*Xh)) * sum(sum(abs(TransfBasisElementPadded.*RWI).^2)) )*sigma; end, end %%%% Step 2.2. Colored noise suppression by BM3D with collaborative Wiener %%%% filtering y_hat_RWI = bm3d_wiener_colored_noise(zRWI, y_hat_RI, hadper_trans_single_den, Nstep_wiener, N1_wiener, N2_wiener, ... 0, tau_match_wiener*N1_wiener*N1_wiener/(255*255), (Ns_wiener-1)/2, 0, single(stdRWI'), single(TforW), single(TinvW)',... inverse_hadper_trans_single_den, Wwin2D_wiener, 0, 1, single(ones(N1_wiener)) ); elapsed_time = toc; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Calculate the final estimate's PSNR and ISNR, print them, and show the %%%% restored image %%%% PSNR = 10*log10(1/mean((y(:)-y_hat_RWI(:)).^2)); ISNR = PSNR - 10*log10(1/mean((y(:)-z(:)).^2)); if print_to_screen == 1 fprintf('Image: %s, Exp %d, Time: %.1f sec, PSNR-RI: %.2f dB, PSNR-RWI: %.2f, ISNR-RWI: %.2f dB\n', ... test_image_name, experiment_number, elapsed_time, PSNR_INITIAL_ESTIMATE, PSNR, ISNR); figure,imshow(z); figure,imshow(double(y_hat_RWI)); end return; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Some auxiliary functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % Create forward and inverse transform matrices, which allow for perfect % reconstruction. The forward transform matrix is normalized so that the % l2-norm of each basis element is 1. % % [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % INPUTS: % % N --> Size of the transform (for wavelets, must be 2^K) % % transform_type --> 'dct', 'dst', 'hadamard', or anything that is % listed by 'help wfilters' (bi-orthogonal wavelets) % 'DCrand' -- an orthonormal transform with a DC and all % the other basis elements of random nature % % dec_levels --> If a wavelet transform is generated, this is the % desired decomposition level. Must be in the % range [0, log2(N)-1], where "0" implies % full decomposition. % % OUTPUTS: % % Tforward --> (N x N) Forward transform matrix % % Tinverse --> (N x N) Inverse transform matrix % if exist('dec_levels') ~= 1, dec_levels = 0; end if N == 1, Tforward = 1; elseif strcmp(transform_type, 'hadamard') == 1, Tforward = hadamard(N); elseif (N == 8) & strcmp(transform_type, 'bior1.5')==1 % hardcoded transform so that the wavelet toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.219417649252501 0.449283757993216 0.449283757993216 0.219417649252501 -0.219417649252501 -0.449283757993216 -0.449283757993216 -0.219417649252501; 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846 -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284; -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846; 0.707106781186547 -0.707106781186547 0 0 0 0 0 0; 0 0 0.707106781186547 -0.707106781186547 0 0 0 0; 0 0 0 0 0.707106781186547 -0.707106781186547 0 0; 0 0 0 0 0 0 0.707106781186547 -0.707106781186547]; elseif (N == 8) & strcmp(transform_type, 'dct')==1 % hardcoded transform so that the signal processing toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.490392640201615 0.415734806151273 0.277785116509801 0.097545161008064 -0.097545161008064 -0.277785116509801 -0.415734806151273 -0.490392640201615; 0.461939766255643 0.191341716182545 -0.191341716182545 -0.461939766255643 -0.461939766255643 -0.191341716182545 0.191341716182545 0.461939766255643; 0.415734806151273 -0.097545161008064 -0.490392640201615 -0.277785116509801 0.277785116509801 0.490392640201615 0.097545161008064 -0.415734806151273; 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274; 0.277785116509801 -0.490392640201615 0.097545161008064 0.415734806151273 -0.415734806151273 -0.097545161008064 0.490392640201615 -0.277785116509801; 0.191341716182545 -0.461939766255643 0.461939766255643 -0.191341716182545 -0.191341716182545 0.461939766255643 -0.461939766255643 0.191341716182545; 0.097545161008064 -0.277785116509801 0.415734806151273 -0.490392640201615 0.490392640201615 -0.415734806151273 0.277785116509801 -0.097545161008064]; elseif (N == 8) & strcmp(transform_type, 'dst')==1 % hardcoded transform so that the PDE toolbox is not needed to generate it Tforward = [ 0.161229841765317 0.303012985114696 0.408248290463863 0.464242826880013 0.464242826880013 0.408248290463863 0.303012985114696 0.161229841765317; 0.303012985114696 0.464242826880013 0.408248290463863 0.161229841765317 -0.161229841765317 -0.408248290463863 -0.464242826880013 -0.303012985114696; 0.408248290463863 0.408248290463863 0 -0.408248290463863 -0.408248290463863 0 0.408248290463863 0.408248290463863; 0.464242826880013 0.161229841765317 -0.408248290463863 -0.303012985114696 0.303012985114696 0.408248290463863 -0.161229841765317 -0.464242826880013; 0.464242826880013 -0.161229841765317 -0.408248290463863 0.303012985114696 0.303012985114696 -0.408248290463863 -0.161229841765317 0.464242826880013; 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863; 0.303012985114696 -0.464242826880013 0.408248290463863 -0.161229841765317 -0.161229841765317 0.408248290463863 -0.464242826880013 0.303012985114696; 0.161229841765317 -0.303012985114696 0.408248290463863 -0.464242826880013 0.464242826880013 -0.408248290463863 0.303012985114696 -0.161229841765317]; elseif strcmp(transform_type, 'dct') == 1, Tforward = dct(eye(N)); elseif strcmp(transform_type, 'dst') == 1, Tforward = dst(eye(N)); elseif strcmp(transform_type, 'DCrand') == 1, x = randn(N); x(1:end,1) = 1; [Q,R] = qr(x); if (Q(1) < 0), Q = -Q; end; Tforward = Q'; else %% a wavelet decomposition supported by 'wavedec' %%% Set periodic boundary conditions, to preserve bi-orthogonality dwtmode('per','nodisp'); Tforward = zeros(N,N); for i = 1:N Tforward(:,i)=wavedec(circshift([1 zeros(1,N-1)],[dec_levels i-1]), log2(N), transform_type); %% construct transform matrix end end %%% Normalize the basis elements Tforward = (Tforward' * diag(sqrt(1./sum(Tforward.^2,2))))'; %%% Compute the inverse transform matrix Tinverse = inv(Tforward); return; ================================================ FILE: BM3D/BM3DSHARP.m ================================================ function [y_hat] = BM3DSHARP(z, sigma, alpha_sharp, profile, print_to_screen) % % Joint sharpening and denoising with BM3D. This is implementation of the % BM3D-SH3D sharpening method that is developed in: % % [1] K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "Joint image % sharpening and denoising by 3D transform-domain collaborative filtering," % Proc. 2007 Int. TICSP Workshop Spectral Meth. Multirate Signal Process., % SMMSP 2007, Moscow, Russia, September 2007. % % FUNCTION INTERFACE: % % [ysharp] = BM3DSHARP(z, sigma, alpha_sharp, profile, print_to_screen) % % The function can work without any of the input arguments, hence they are % optional! % % INPUTS (OPTIONAL): % % 1) z (matrix, size MxN) : Input image (noisy and with poor contrast) % 2) sigma (double) : Noise (IF ANY noise) standard deviation (signal assumed % in the range [0, 255]) % 3) alpha_sharp (double) : Sharpening parameter (default: 1.5): % (1,inf) -> sharpen % 1 -> no sharpening % (0,1) -> de-sharpen % 4) profile (char vector) : 'lc' --> fast % 'np' --> normal (default) % 5) print_to_screen (boolean) : 0 --> do not print output % information (and do not plot figures) % 1 --> print figures (default) % % OUTPUTS: % 1) ysharp (matrix, size MxN) : Sharpened image (in the range [0,1]) % % BASIC USAGE EXAMPLES: % % sigma = 10; % z = im2double(imread('cameraman.tif')); % z = z + (sigma/255)*randn(size(z)); % alpha_sharp = 1.3; % [ysharp] = BM3DSHARP(z, sigma, alpha_sharp); % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Copyright 2007 Tampere University of Technology. All rights reserved. % This work should only be used for nonprofit purposes. % % AUTHORS: % Kostadin Dabov (2007), email: kostadin.dabov _at_ tut.fi % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% In case, an input image z is not provided, then use the filename %%%% below to read an original image (might contain path also). Later, %%%% artificial AWGN noise is added and this noisy image is processed %%%% by the BM3D-SH3D. %%%% if (exist('image_name') ~= 1) image_name = [ % %%%% Grayscale images % 'barco.png' % 'pentagon.tif' 'Cameraman256.png' % 'boat.png' % 'Lena512.png' % 'house.png' % 'barbara.png' ]; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Quality/complexity trade-off profile selection %%%% %%%% 'np' --> Normal Profile (balanced quality) %%%% 'lc' --> Low Complexity Profile (fast, lower quality) %%%% %%%% 'high' --> High Profile (high quality, not documented in [1]) %%%% if (exist('profile') ~= 1) profile = 'np'; %% default profile end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Specify the std. dev. of the corrupting noise %%%% if (exist('sigma') ~= 1), if (exist('z') ~= 1) sigma = 20; %% default standard deviation of the AWGN else fprintf('Please specify value for the s.t.d. "sigma"\n'); y_hat = 0; return; end end if (exist('alpha_sharp') ~= 1) alpha_sharp = 3/2; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Following are the parameters for the Normal Profile. %%%% %%%% Select transforms ('dct', 'dst', 'hadamard', or anything that is listed by 'help wfilters'): transform_2D_HT_name = 'bior1.5'; %% transform used for the HT filt. of size N1 x N1 transform_3rd_dim_name = 'haar'; %% transform used in the 3-rd dim, the same for HT and Wiener filt. %%%% Hard-thresholding (HT) parameters: N1 = 8; %% N1 x N1 is the block size used for the hard-thresholding (HT) filtering Nstep = 3; %% sliding step to process every next reference block N2 = 16; %% maximum number of similar blocks (maximum size of the 3rd dimension of a 3D array) Ns = 39; %% length of the side of the search neighborhood for full-search block-matching (BM), must be odd tau_match = 3000;%% threshold for the block-distance (d-distance) lambda_thr2D = 0; %% threshold parameter for the coarse initial denoising used in the d-distance measure lambda_thr3D = 2.7; %% threshold parameter for the hard-thresholding in 3D transform domain beta = 2.0; %% parameter of the 2D Kaiser window used in the reconstruction %%%% Block-matching parameters: stepFS = 1; %% step that forces to switch to full-search BM, "1" implies always full-search smallLN = 'not used in np'; %% if stepFS > 1, then this specifies the size of the small local search neighb. thrToIncStep = 8; %% used in the HT filtering to increase the sliding step in uniform regions if strcmp(profile, 'lc') == 1, Nstep = 6; Ns = 25; thrToIncStep = 3; smallLN = 3; stepFS = 6*Nstep; end if (strcmp(profile, 'vn') == 1) | (sigma > 40), transform_2D_HT_name = 'dct'; N1 = 12; Nstep = 4; lambda_thr3D = 2.8; lambda_thr2D = 2.0; thrToIncStep = 3; tau_match = 5000; end decLevel = 0; %% dec. levels of the dyadic wavelet 2D transform for blocks (0 means full decomposition, higher values decrease the dec. number) thr_mask = ones(N1); %% N1xN1 mask of threshold scaling coeff. --- by default there is no scaling, however the use of different thresholds for different wavelet decompoistion subbands can be done with this matrix if strcmp(profile, 'high') == 1, %% this profile is not documented in [1] decLevel = 1; Nstep = 2; lambda_thr3D = 2.5; vMask = ones(N1,1); vMask((end/4+1):end/2)= 1.01; vMask((end/2+1):end) = 1.07; %% this allows to have different threhsolds for the finest and next-to-the-finest subbands thr_mask = vMask * vMask'; beta = 2.5; beta_wiener = 1.5; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Note: touch below this point only if you know what you are doing! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Check whether to dump information to the screen or remain silent dump_output_information = 1; if (exist('print_to_screen') == 1) & (print_to_screen == 0), dump_output_information = 0; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Create transform matrices, etc. %%%% [Tfor, Tinv] = getTransfMatrix(N1, transform_2D_HT_name, decLevel); %% get (normalized) forward and inverse transform matrices if (strcmp(transform_3rd_dim_name, 'haar') == 1) | (strcmp(transform_3rd_dim_name(end-2:end), '1.1') == 1), %%% If Haar is used in the 3-rd dimension, then a fast internal transform is used, thus no need to generate transform %%% matrices. hadper_trans_single_den = {}; inverse_hadper_trans_single_den = {}; else %%% Create transform matrices. The transforms are later applied by %%% matrix-vector multiplication for the 1D case. for hpow = 0:ceil(log2(max(N2,N2_wiener))), h = 2^hpow; [Tfor3rd, Tinv3rd] = getTransfMatrix(h, transform_3rd_dim_name, 0); hadper_trans_single_den{h} = single(Tfor3rd); inverse_hadper_trans_single_den{h} = single(Tinv3rd'); end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% 2D Kaiser windows used in the aggregation of block-wise estimates %%%% if beta==2 & N1==8 % hardcode the window function so that the signal processing toolbox is not needed by default Wwin2D = [ 0.1924 0.2989 0.3846 0.4325 0.4325 0.3846 0.2989 0.1924; 0.2989 0.4642 0.5974 0.6717 0.6717 0.5974 0.4642 0.2989; 0.3846 0.5974 0.7688 0.8644 0.8644 0.7688 0.5974 0.3846; 0.4325 0.6717 0.8644 0.9718 0.9718 0.8644 0.6717 0.4325; 0.4325 0.6717 0.8644 0.9718 0.9718 0.8644 0.6717 0.4325; 0.3846 0.5974 0.7688 0.8644 0.8644 0.7688 0.5974 0.3846; 0.2989 0.4642 0.5974 0.6717 0.6717 0.5974 0.4642 0.2989; 0.1924 0.2989 0.3846 0.4325 0.4325 0.3846 0.2989 0.1924]; else Wwin2D = kaiser(N1, beta) * kaiser(N1, beta)'; % Kaiser window used in the aggregation of the HT part end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% If needed, read images, generate noise, or scale the images to the %%%% [0,1] interval %%%% if (exist('z') ~= 1) y = im2double(imread(image_name)); %% read a noise-free image and put in intensity range [0,1] randn('seed', 0); %% generate seed z = y + (sigma/255)*randn(size(y)); %% create a noisy image else % external images image_name = 'External image'; % convert z to double precision if needed z = double(z); % if z's range is [0, 255], then convert to [0, 1] if (max(z(:)) > 10), % a naive check for intensity range z = z / 255; end end if (size(z,3) ~= 1), error('BM3D-SH3D accepts only grayscale 2D images.'); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Print image information to the screen %%%% if dump_output_information == 1, fprintf('Image: %s (%dx%d), sigma: %.1f\n', image_name, size(z,1), size(z,2), sigma); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Apply the filtering MEX-subroutine %%%% tic; y_hat = bm3d_thr_sharpen_var(z, hadper_trans_single_den, Nstep, N1, N2, lambda_thr2D,... lambda_thr3D, tau_match*N1*N1/(255*255), (Ns-1)/2, (sigma/255), thrToIncStep, single(Tfor), single(Tinv)', inverse_hadper_trans_single_den, single(thr_mask), Wwin2D, smallLN, stepFS, 1/alpha_sharp ); estimate_elapsed_time = toc; if dump_output_information == 1, fprintf('SHARPENING COMPLETED (total time: %.1f sec)\n', ... estimate_elapsed_time); imshow(z); figure, imshow(double(y_hat)); end return; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Some auxiliary functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % Create forward and inverse transform matrices, which allow for perfect % reconstruction. The forward transform matrix is normalized so that the % l2-norm of each basis element is 1. % % [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % INPUTS: % % N --> Size of the transform (for wavelets, must be 2^K) % % transform_type --> 'dct', 'dst', 'hadamard', or anything that is % listed by 'help wfilters' (bi-orthogonal wavelets) % 'DCrand' -- an orthonormal transform with a DC and all % the other basis elements of random nature % % dec_levels --> If a wavelet transform is generated, this is the % desired decomposition level. Must be in the % range [0, log2(N)-1], where "0" implies % full decomposition. % % OUTPUTS: % % Tforward --> (N x N) Forward transform matrix % % Tinverse --> (N x N) Inverse transform matrix % if exist('dec_levels') ~= 1, dec_levels = 0; end if N == 1, Tforward = 1; elseif strcmp(transform_type, 'hadamard') == 1, Tforward = hadamard(N); elseif (N == 8) & strcmp(transform_type, 'bior1.5')==1 % hardcoded transform so that the wavelet toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.219417649252501 0.449283757993216 0.449283757993216 0.219417649252501 -0.219417649252501 -0.449283757993216 -0.449283757993216 -0.219417649252501; 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846 -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284; -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846; 0.707106781186547 -0.707106781186547 0 0 0 0 0 0; 0 0 0.707106781186547 -0.707106781186547 0 0 0 0; 0 0 0 0 0.707106781186547 -0.707106781186547 0 0; 0 0 0 0 0 0 0.707106781186547 -0.707106781186547]; elseif (N == 8) & strcmp(transform_type, 'dct')==1 % hardcoded transform so that the signal processing toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.490392640201615 0.415734806151273 0.277785116509801 0.097545161008064 -0.097545161008064 -0.277785116509801 -0.415734806151273 -0.490392640201615; 0.461939766255643 0.191341716182545 -0.191341716182545 -0.461939766255643 -0.461939766255643 -0.191341716182545 0.191341716182545 0.461939766255643; 0.415734806151273 -0.097545161008064 -0.490392640201615 -0.277785116509801 0.277785116509801 0.490392640201615 0.097545161008064 -0.415734806151273; 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274; 0.277785116509801 -0.490392640201615 0.097545161008064 0.415734806151273 -0.415734806151273 -0.097545161008064 0.490392640201615 -0.277785116509801; 0.191341716182545 -0.461939766255643 0.461939766255643 -0.191341716182545 -0.191341716182545 0.461939766255643 -0.461939766255643 0.191341716182545; 0.097545161008064 -0.277785116509801 0.415734806151273 -0.490392640201615 0.490392640201615 -0.415734806151273 0.277785116509801 -0.097545161008064]; elseif (N == 8) & strcmp(transform_type, 'dst')==1 % hardcoded transform so that the PDE toolbox is not needed to generate it Tforward = [ 0.161229841765317 0.303012985114696 0.408248290463863 0.464242826880013 0.464242826880013 0.408248290463863 0.303012985114696 0.161229841765317; 0.303012985114696 0.464242826880013 0.408248290463863 0.161229841765317 -0.161229841765317 -0.408248290463863 -0.464242826880013 -0.303012985114696; 0.408248290463863 0.408248290463863 0 -0.408248290463863 -0.408248290463863 0 0.408248290463863 0.408248290463863; 0.464242826880013 0.161229841765317 -0.408248290463863 -0.303012985114696 0.303012985114696 0.408248290463863 -0.161229841765317 -0.464242826880013; 0.464242826880013 -0.161229841765317 -0.408248290463863 0.303012985114696 0.303012985114696 -0.408248290463863 -0.161229841765317 0.464242826880013; 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863; 0.303012985114696 -0.464242826880013 0.408248290463863 -0.161229841765317 -0.161229841765317 0.408248290463863 -0.464242826880013 0.303012985114696; 0.161229841765317 -0.303012985114696 0.408248290463863 -0.464242826880013 0.464242826880013 -0.408248290463863 0.303012985114696 -0.161229841765317]; elseif strcmp(transform_type, 'dct') == 1, Tforward = dct(eye(N)); elseif strcmp(transform_type, 'dst') == 1, Tforward = dst(eye(N)); elseif strcmp(transform_type, 'DCrand') == 1, x = randn(N); x(1:end,1) = 1; [Q,R] = qr(x); if (Q(1) < 0), Q = -Q; end; Tforward = Q'; else %% a wavelet decomposition supported by 'wavedec' %%% Set periodic boundary conditions, to preserve bi-orthogonality dwtmode('per','nodisp'); Tforward = zeros(N,N); for i = 1:N Tforward(:,i)=wavedec(circshift([1 zeros(1,N-1)],[dec_levels i-1]), log2(N), transform_type); %% construct transform matrix end end %%% Normalize the basis elements Tforward = (Tforward' * diag(sqrt(1./sum(Tforward.^2,2))))'; %%% Compute the inverse transform matrix Tinverse = inv(Tforward); return; ================================================ FILE: BM3D/BM3D_CFA.m ================================================ function [varargout] = BM3D_CFA(z, sigma) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % BM3D_CFA is the modification of the BM3D algorithm for attenuation of additive white Gaussian noise from % Bayer CFA images. This algorithm reproduces the results from the article: % % [1] A. Danielyan, M. Vehvilinen, A. Foi, V. Katkovnik, and K. Egiazarian, % Cross-color BM3D filtering of noisy raw data, % Proc. Int. Workshop on Local and Non-Local Approx. in Image Process., % LNLA 2009, Tuusula, Finland, pp. 125-129, August 2009. % % FUNCTION INTERFACE: % % [y_wiener, y_ht] = BM3D(z, sigma) % % ! The function can work without any of the input arguments, % in which case, the internal default ones are used ! % INPUT ARGUMENTS (OPTIONAL): % % 2) z (matrix M x N): Noisy image (intensities in range [0,1] or [0,255]) % 3) sigma (double) : Std. dev. of the noise (corresponding to intensities % in range [0,255] even if the range of z is [0,1]) % OUTPUTS: % 1) y_wiener (matrix M x N): Final(wiener) estimate (in the range [0,1]) % 2) y_ht (matrix M x N): Basic (hard-thresholding) estimate (in the range [0,1]) % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Copyright (c) 2009-2014 Tampere University of Technology. % All rights reserved. % This work should only be used for nonprofit purposes. % % AUTHORS: % Aram Danielyan, email: aram dot danielyan _at_ .tut.fi % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% In case, a noisy image z is not provided, then use the filename %%%% below to read an original image (might contain path also). Later, %%%% artificial AWGN noise is added and this noisy image is processed %%%% by the BM3D. %%%% image_name = [ 'kodim07.png' % 'kodim08.png' % 'kodim19.png' % 'kodim23.png' ]; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Quality/complexity trade-off profile selection %%%% %%%% 'np' --> Normal Profile (balanced quality) if ~exist('profile','var') profile = 'np'; %% default profile end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Specify the std. dev. of the corrupting noise %%%% if ~exist('sigma','var') sigma = 25; %% default standard deviation of the AWGN end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Following are the parameters for the Normal Profile. %%%% %%%% Select transforms ('dct', 'dst', 'hadamard', or anything that is listed by 'help wfilters'): transform_2D_HT_name = 'dct'; %% transform used for the HT filt. of size N1 x N1 transform_2D_Wiener_name = 'dct'; transform_3rd_dim_name = 'haar'; %% transform used in the 3-rd dim, the same for HT and Wiener filt. %%%% Hard-thresholding (HT) parameters: N1 = 5; %% N1 x N1 is the block size used for the hard-thresholding (HT) filtering Nstep = 3; %% sliding step to process every next reference block N2 = 16; %% maximum number of similar blocks (maximum size of the 3rd dimension of a 3D array) Ns = 39; %% length of the side of the search neighborhood for full-search block-matching (BM), must be odd lambda_thr2D = 0; tau_match = 3000;%% threshold for the block-distance (d-distance) lambda_thr3D = 2.7; %% threshold parameter for the hard-thresholding in 3D transform domain beta = 2.0; %% parameter of the 2D Kaiser window used in the reconstruction %%%% Step 2: Wiener filtering parameters: N1_wiener = 6; Nstep_wiener = 3; N2_wiener = 32; Ns_wiener = 39; tau_match_wiener = 400; beta_wiener = 2.0; %%%% Block-matching parameters: stepFS = 1; %% step that forces to switch to full-search BM, "1" implies always full-search smallLN = 'not used in np'; %% if stepFS > 1, then this specifies the size of the small local search neighb. stepFSW = 1; smallLNW = 'not used in np'; thrToIncStep = 8; % if the number of non-zero coefficients after HT is less than thrToIncStep, % than the sliding step to the next reference block is incresed to (nm1-1) decLevel = 0; %% dec. levels of the dyadic wavelet 2D transform for blocks (0 means full decomposition, higher values decrease the dec. number) thr_mask = ones(N1); %% N1xN1 mask of threshold scaling coeff. --- by default there is no scaling, however the use of different thresholds for different wavelet decompoistion subbands can be done with this matrix %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Note: touch below this point only if you know what you are doing! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Create transform matrices, etc. %%%% [Tfor, Tinv] = getTransfMatrix(N1, transform_2D_HT_name, decLevel); %% get (normalized) forward and inverse transform matrices [TforW, TinvW] = getTransfMatrix(N1_wiener, transform_2D_Wiener_name, 0); %% get (normalized) forward and inverse transform matrices if (strcmp(transform_3rd_dim_name, 'haar') == 1) | (strcmp(transform_3rd_dim_name(end-2:end), '1.1') == 1), %%% If Haar is used in the 3-rd dimension, then a fast internal transform is used, thus no need to generate transform %%% matrices. hadper_trans_single_den = {}; inverse_hadper_trans_single_den = {}; else %%% Create transform matrices. The transforms are later applied by %%% matrix-vector multiplication for the 1D case. for hpow = 0:ceil(log2(max(N2,N2_wiener))), h = 2^hpow; [Tfor3rd, Tinv3rd] = getTransfMatrix(h, transform_3rd_dim_name, 0); hadper_trans_single_den{h} = single(Tfor3rd); inverse_hadper_trans_single_den{h} = single(Tinv3rd'); end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% 2D Kaiser windows used in the aggregation of block-wise estimates %%%% if beta_wiener==2 & beta==2 & N1_wiener==8 & N1==8 % hardcode the window function so that the signal processing toolbox is not needed by default Wwin2D = [ 0.1924 0.2989 0.3846 0.4325 0.4325 0.3846 0.2989 0.1924; 0.2989 0.4642 0.5974 0.6717 0.6717 0.5974 0.4642 0.2989; 0.3846 0.5974 0.7688 0.8644 0.8644 0.7688 0.5974 0.3846; 0.4325 0.6717 0.8644 0.9718 0.9718 0.8644 0.6717 0.4325; 0.4325 0.6717 0.8644 0.9718 0.9718 0.8644 0.6717 0.4325; 0.3846 0.5974 0.7688 0.8644 0.8644 0.7688 0.5974 0.3846; 0.2989 0.4642 0.5974 0.6717 0.6717 0.5974 0.4642 0.2989; 0.1924 0.2989 0.3846 0.4325 0.4325 0.3846 0.2989 0.1924]; Wwin2D_wiener = Wwin2D; else Wwin2D = kaiser(N1, beta) * kaiser(N1, beta)'; % Kaiser window used in the aggregation of the HT part Wwin2D_wiener = kaiser(N1_wiener, beta_wiener) * kaiser(N1_wiener, beta_wiener)'; % Kaiser window used in the aggregation of the Wiener filt. part end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% If needed, read images, generate noise, or scale the images to the %%%% [0,1] interval %%%% if ~exist('z','var') yRGB = im2double(imread(image_name)); %% read a noise-free image and put in intensity range [0,1] y = zeros(size(yRGB,1), size(yRGB,2)); y(1:2:end,1:2:end) = yRGB(1:2:end,1:2:end,2); y(2:2:end,2:2:end) = yRGB(2:2:end,2:2:end,2); y(1:2:end,2:2:end) = yRGB(1:2:end,2:2:end,1); y(2:2:end,1:2:end) = yRGB(2:2:end,1:2:end,3); randn('seed', 0); %% generate seed z = y + (sigma/255)*randn(size(y)); %% create a noisy image else % external images image_name = 'External image'; % convert z to double precision if needed z = double(z); y= []; end if (size(z,3) ~= 1) error('BM3D accepts only grayscale 2D images.'); end %%% Check whether to dump information to the screen or remain silent if isempty(y) dump_output_information = false; else dump_output_information = true; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Print image information to the screen %%%% if dump_output_information fprintf('Image: %s (%dx%d), sigma: %.1f\n', image_name, size(z,1), size(z,2), sigma); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Step 1. Produce the basic estimate by HT filtering %%%% tic; y_ht = bm3d_CFA_thr(z, hadper_trans_single_den, Nstep, N1, N2, lambda_thr2D,... lambda_thr3D, tau_match*N1*N1/(255*255), (Ns-1)/2, (sigma/255), thrToIncStep, single(Tfor), single(Tinv)', inverse_hadper_trans_single_den, single(thr_mask), Wwin2D, smallLN, stepFS ); estimate_elapsed_time = toc; if dump_output_information PSNR_INITIAL_ESTIMATE = 10*log10(1/mean((y(:)-double(y_ht(:))).^2)); fprintf('BASIC ESTIMATE, PSNR: %.2f dB\n', PSNR_INITIAL_ESTIMATE); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Step 2. Produce the final estimate by Wiener filtering (using the %%%% hard-thresholding initial estimate) %%%% tic; y_wiener = bm3d_CFA_wiener(z, y_ht, hadper_trans_single_den, Nstep_wiener, N1_wiener, N2_wiener, ... 'unused arg', tau_match_wiener*N1_wiener*N1_wiener/(255*255), (Ns_wiener-1)/2, (sigma/255), 'unused arg', single(TforW), single(TinvW)', inverse_hadper_trans_single_den, Wwin2D_wiener, smallLNW, stepFSW, single(ones(N1_wiener)) ); wiener_elapsed_time = toc; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Calculate the final estimate's PSNR, print it, and show the %%%% denoised image next to the noisy one %%%% y_wiener = double(y_wiener); if dump_output_information PSNR = 10*log10(1/mean((y(:)-y_wiener(:)).^2)); % y is valid fprintf('FINAL ESTIMATE (total time: %.1f sec), PSNR: %.2f dB\n', ... wiener_elapsed_time + estimate_elapsed_time, PSNR); figure, imshow(z); title(sprintf('Noisy %s, PSNR: %.3f dB (sigma: %d)', ... image_name(1:end-4), 10*log10(1/mean((y(:)-z(:)).^2)), sigma)); figure, imshow(y_wiener); title(sprintf('Denoised %s, PSNR: %.3f dB', ... image_name(1:end-4), PSNR)); end if nargout==0 varargout={}; else varargout{1}=y_wiener; varargout{2}=y_ht; end return; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Some auxiliary functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % Create forward and inverse transform matrices, which allow for perfect % reconstruction. The forward transform matrix is normalized so that the % l2-norm of each basis element is 1. % % [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % INPUTS: % % N --> Size of the transform (for wavelets, must be 2^K) % % transform_type --> 'dct', 'dst', 'hadamard', or anything that is % listed by 'help wfilters' (bi-orthogonal wavelets) % 'DCrand' -- an orthonormal transform with a DC and all % the other basis elements of random nature % % dec_levels --> If a wavelet transform is generated, this is the % desired decomposition level. Must be in the % range [0, log2(N)-1], where "0" implies % full decomposition. % % OUTPUTS: % % Tforward --> (N x N) Forward transform matrix % % Tinverse --> (N x N) Inverse transform matrix % if exist('dec_levels') ~= 1, dec_levels = 0; end if N == 1, Tforward = 1; elseif strcmp(transform_type, 'hadamard') == 1, Tforward = hadamard(N); elseif (N == 8) & strcmp(transform_type, 'bior1.5')==1 % hardcoded transform so that the wavelet toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.219417649252501 0.449283757993216 0.449283757993216 0.219417649252501 -0.219417649252501 -0.449283757993216 -0.449283757993216 -0.219417649252501; 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846 -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284; -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846; 0.707106781186547 -0.707106781186547 0 0 0 0 0 0; 0 0 0.707106781186547 -0.707106781186547 0 0 0 0; 0 0 0 0 0.707106781186547 -0.707106781186547 0 0; 0 0 0 0 0 0 0.707106781186547 -0.707106781186547]; elseif (N == 8) & strcmp(transform_type, 'dct')==1 % hardcoded transform so that the signal processing toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.490392640201615 0.415734806151273 0.277785116509801 0.097545161008064 -0.097545161008064 -0.277785116509801 -0.415734806151273 -0.490392640201615; 0.461939766255643 0.191341716182545 -0.191341716182545 -0.461939766255643 -0.461939766255643 -0.191341716182545 0.191341716182545 0.461939766255643; 0.415734806151273 -0.097545161008064 -0.490392640201615 -0.277785116509801 0.277785116509801 0.490392640201615 0.097545161008064 -0.415734806151273; 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274; 0.277785116509801 -0.490392640201615 0.097545161008064 0.415734806151273 -0.415734806151273 -0.097545161008064 0.490392640201615 -0.277785116509801; 0.191341716182545 -0.461939766255643 0.461939766255643 -0.191341716182545 -0.191341716182545 0.461939766255643 -0.461939766255643 0.191341716182545; 0.097545161008064 -0.277785116509801 0.415734806151273 -0.490392640201615 0.490392640201615 -0.415734806151273 0.277785116509801 -0.097545161008064]; elseif (N == 8) & strcmp(transform_type, 'dst')==1 % hardcoded transform so that the PDE toolbox is not needed to generate it Tforward = [ 0.161229841765317 0.303012985114696 0.408248290463863 0.464242826880013 0.464242826880013 0.408248290463863 0.303012985114696 0.161229841765317; 0.303012985114696 0.464242826880013 0.408248290463863 0.161229841765317 -0.161229841765317 -0.408248290463863 -0.464242826880013 -0.303012985114696; 0.408248290463863 0.408248290463863 0 -0.408248290463863 -0.408248290463863 0 0.408248290463863 0.408248290463863; 0.464242826880013 0.161229841765317 -0.408248290463863 -0.303012985114696 0.303012985114696 0.408248290463863 -0.161229841765317 -0.464242826880013; 0.464242826880013 -0.161229841765317 -0.408248290463863 0.303012985114696 0.303012985114696 -0.408248290463863 -0.161229841765317 0.464242826880013; 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863; 0.303012985114696 -0.464242826880013 0.408248290463863 -0.161229841765317 -0.161229841765317 0.408248290463863 -0.464242826880013 0.303012985114696; 0.161229841765317 -0.303012985114696 0.408248290463863 -0.464242826880013 0.464242826880013 -0.408248290463863 0.303012985114696 -0.161229841765317]; elseif strcmp(transform_type, 'dct') == 1, Tforward = dct(eye(N)); elseif strcmp(transform_type, 'dst') == 1, Tforward = dst(eye(N)); elseif strcmp(transform_type, 'DCrand') == 1, x = randn(N); x(1:end,1) = 1; [Q,R] = qr(x); if (Q(1) < 0), Q = -Q; end; Tforward = Q'; else %% a wavelet decomposition supported by 'wavedec' %%% Set periodic boundary conditions, to preserve bi-orthogonality dwtmode('per','nodisp'); Tforward = zeros(N,N); for i = 1:N Tforward(:,i)=wavedec(circshift([1 zeros(1,N-1)],[dec_levels i-1]), log2(N), transform_type); %% construct transform matrix end end %%% Normalize the basis elements Tforward = (Tforward' * diag(sqrt(1./sum(Tforward.^2,2))))'; %%% Compute the inverse transform matrix Tinverse = inv(Tforward); return; ================================================ FILE: BM3D/CBM3D.m ================================================ function [PSNR, yRGB_est] = CBM3D(yRGB, zRGB, sigma, profile, print_to_screen, colorspace) % % CBM3D is algorithm for attenuation of additive white Gaussian noise from % color RGB images. This algorithm reproduces the results from the article: % % [1] K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "Color image % denoising via sparse 3D collaborative filtering with grouping constraint in % luminance-chrominance space," submitted to IEEE Int. Conf. Image Process., % January 2007, in review, preprint at http://www.cs.tut.fi/~foi/GCF-BM3D. % % FUNCTION INTERFACE: % % [PSNR, yRGB_est] = CBM3D(yRGB, zRGB, sigma, profile, print_to_screen, colorspace) % % ! The function can work without any of the input arguments, % in which case, the internal default ones are used ! % % BASIC USAGE EXAMPLES: % % Case 1) Using the default parameters (i.e., image name, sigma, etc.) % % [PSNR, yRGB_est] = CBM3D; % % Case 2) Using an external noisy image: % % % Read an RGB image and scale its intensities in range [0,1] % yRGB = im2double(imread('image_House256rgb.png')); % % Generate the same seed used in the experimental results of [1] % randn('seed', 0); % % Standard deviation of the noise --- corresponding to intensity % % range [0,255], despite that the input was scaled in [0,1] % sigma = 25; % % Add the AWGN with zero mean and standard deviation 'sigma' % zRGB = yRGB + (sigma/255)*randn(size(yRGB)); % % Denoise 'zRGB'. The denoised image is 'yRGB_est', and 'NA = 1' % % because the true image was not provided % [NA, yRGB_est] = CBM3D(1, zRGB, sigma); % % Compute the putput PSNR % PSNR = 10*log10(1/mean((yRGB(:)-yRGB_est(:)).^2)) % % show the noisy image 'zRGB' and the denoised 'yRGB_est' % figure; imshow(min(max(zRGB,0),1)); % figure; imshow(min(max(yRGB_est,0),1)); % % Case 3) If the original image yRGB is provided as the first input % argument, then some additional information is printed (PSNRs, % figures, etc.). That is, "[NA, yRGB_est] = BM3D(1, zRGB, sigma);" in the % above code should be replaced with: % % [PSNR, yRGB_est] = CBM3D(yRGB, zRGB, sigma); % % % INPUT ARGUMENTS (OPTIONAL): % 1) yRGB (M x N x 3): Noise-free RGB image (needed for computing PSNR), % replace with the scalar 1 if not available. % 2) zRGB (M x N x 3): Noisy RGBimage (intensities in range [0,1] or [0,255]) % 3) sigma (double) : Std. dev. of the noise (corresponding to intensities % in range [0,255] even if the range of zRGB is [0,1]) % 4) profile (char) : 'np' --> Normal Profile % 'lc' --> Fast Profile % 5) print_to_screen : 0 --> do not print output information (and do % not plot figures) % 1 --> print information and plot figures % 6) colorspace (char): 'opp' --> use opponent colorspace % 'yCbCr' --> use yCbCr colorspace % % OUTPUTS: % 1) PSNR (double) : Output PSNR (dB), only if the original % image is available, otherwise PSNR = 0 % 2) yRGB_est (M x N x 3): Final RGB estimate (in the range [0,1]) % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Copyright (c) 2007-2011 Tampere University of Technology. % All rights reserved. % This work should only be used for nonprofit purposes. % % AUTHORS: % Kostadin Dabov, email: dabov _at_ cs.tut.fi % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% In case, there is no input image (zRGB or yRGB), then use the filename %%%% below to read an original image (might contain path also). Later, %%%% artificial AWGN noise is added and this noisy image is processed %%%% by the CBM3D. %%%% image_name = [ % 'kodim12.png' 'image_Lena512rgb.png' % 'image_House256rgb.png' % 'image_Peppers512rgb.png' % 'image_Baboon512rgb.png' % 'image_F16_512rgb.png' ]; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Quality/complexity trade-off %%%% %%%% 'np' --> Normal Profile (balanced quality) %%%% 'lc' --> Low Complexity Profile (fast, lower quality) %%%% %%%% 'high' --> High Profile (high quality, not documented in [1]) %%%% %%%% 'vn' --> This profile is automatically enabled for high noise %%%% when sigma > 40 %%%% %%%% 'vn_old' --> This is the old 'vn' profile that was used in [1]. %%%% It gives inferior results than 'vn' in most cases. %%%% if (exist('profile') ~= 1) profile = 'np'; %% default profile end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Specify the std. dev. of the corrupting noise %%%% if (exist('sigma') ~= 1), sigma = 50; %% default standard deviation of the AWGN end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Colorspace in which we perform denoising. BM is applied to the first %%%% component and the matching information is re-used for the other two. %%%% if (exist('colorspace') ~= 1), colorspace = 'opp'; %%% (valid colorspaces are: 'yCbCr' and 'opp') end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Following are the parameters for the Normal Profile. %%%% %%%% Select transforms ('dct', 'dst', 'hadamard', or anything that is listed by 'help wfilters'): transform_2D_HT_name = 'bior1.5'; %% transform used for the HT filt. of size N1 x N1 transform_2D_Wiener_name = 'dct'; %% transform used for the Wiener filt. of size N1_wiener x N1_wiener transform_3rd_dim_name = 'haar'; %% transform used in the 3-rd dim, the same for HT and Wiener filt. %%%% Hard-thresholding (HT) parameters: N1 = 8; %% N1 x N1 is the block size used for the hard-thresholding (HT) filtering Nstep = 3; %% sliding step to process every next reference block N2 = 16; %% maximum number of similar blocks (maximum size of the 3rd dimension of a 3D array) Ns = 39; %% length of the side of the search neighborhood for full-search block-matching (BM), must be odd tau_match = 3000;%% threshold for the block-distance (d-distance) lambda_thr2D = 0; %% threshold parameter for the coarse initial denoising used in the d-distance measure lambda_thr3D = 2.7; %% threshold parameter for the hard-thresholding in 3D transform domain beta = 2.0; %% parameter of the 2D Kaiser window used in the reconstruction %%%% Wiener filtering parameters: N1_wiener = 8; Nstep_wiener = 3; N2_wiener = 32; Ns_wiener = 39; tau_match_wiener = 400; beta_wiener = 2.0; %%%% Block-matching parameters: stepFS = 1; %% step that forces to switch to full-search BM, "1" implies always full-search smallLN = 'not used in np'; %% if stepFS > 1, then this specifies the size of the small local search neighb. stepFSW = 1; smallLNW = 'not used in np'; thrToIncStep = 8; %% used in the HT filtering to increase the sliding step in uniform regions if strcmp(profile, 'lc') == 1, Nstep = 6; Ns = 25; Nstep_wiener = 5; N2_wiener = 16; Ns_wiener = 25; thrToIncStep = 3; smallLN = 3; stepFS = 6*Nstep; smallLNW = 2; stepFSW = 5*Nstep_wiener; end % Profile 'vn' was proposed in % Y. Hou, C. Zhao, D. Yang, and Y. Cheng, 'Comment on "Image Denoising by Sparse 3D Transform-Domain % Collaborative Filtering"', accepted for publication, IEEE Trans. on Image Processing, July, 2010. % as a better alternative to that initially proposed in [1] (which is currently in profile 'vn_old') if (strcmp(profile, 'vn') == 1) | (sigma > 40), N2 = 32; Nstep = 4; N1_wiener = 11; Nstep_wiener = 6; lambda_thr3D = 2.8; thrToIncStep = 3; tau_match_wiener = 3500; tau_match = 25000; Ns_wiener = 39; end % The 'vn_old' profile corresponds to the original parameters for strong noise proposed in [1]. if (strcmp(profile, 'vn_old') == 1) & (sigma > 40), transform_2D_HT_name = 'dct'; N1 = 12; Nstep = 4; N1_wiener = 11; Nstep_wiener = 6; lambda_thr3D = 2.8; lambda_thr2D = 2.0; thrToIncStep = 3; tau_match_wiener = 3500; tau_match = 5000; Ns_wiener = 39; end decLevel = 0; %% dec. levels of the dyadic wavelet 2D transform for blocks (0 means full decomposition, higher values decrease the dec. number) thr_mask = ones(N1); %% N1xN1 mask of threshold scaling coeff. --- by default there is no scaling, however the use of different thresholds for different wavelet decompoistion subbands can be done with this matrix if strcmp(profile, 'high') == 1, decLevel = 1; Nstep = 2; Nstep_wiener = 2; lambda_thr3D = 2.5; vMask = ones(N1,1); vMask((end/4+1):end/2)= 1.01; vMask((end/2+1):end) = 1.07; %% this allows to have different threhsolds for the finest and next-to-the-finest subbands thr_mask = vMask * vMask'; beta = 2.5; beta_wiener = 1.5; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Note: touch below this point only if you know what you are doing! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Check whether to dump information to the screen or reamin silent dump_output_information = 1; if (exist('print_to_screen') == 1) & (print_to_screen == 0), dump_output_information = 0; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Create transform matrices, etc. %%%% [Tfor, Tinv] = getTransfMatrix(N1, transform_2D_HT_name, decLevel); %% get (normalized) forward and inverse transform matrices [TforW, TinvW] = getTransfMatrix(N1_wiener, transform_2D_Wiener_name); %% get (normalized) forward and inverse transform matrices if (strcmp(transform_3rd_dim_name, 'haar') == 1) | (strcmp(transform_3rd_dim_name(end-2:end), '1.1') == 1), %%% If Haar is used in the 3-rd dimension, then a fast internal transform is used, thus no need to generate transform %%% matrices. hadper_trans_single_den = {}; inverse_hadper_trans_single_den = {}; else %%% Create transform matrices. The transforms are later applied by %%% matrix-vector multiplication for the 1D case. for hpow = 0:ceil(log2(max(N2,N2_wiener))), h = 2^hpow; [Tfor3rd, Tinv3rd] = getTransfMatrix(h, transform_3rd_dim_name, 0); hadper_trans_single_den{h} = single(Tfor3rd); inverse_hadper_trans_single_den{h} = single(Tinv3rd'); end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% 2D Kaiser windows used in the aggregation of block-wise estimates %%%% if beta_wiener==2 & beta==2 & N1_wiener==8 & N1==8 % hardcode the window function so that the signal processing toolbox is not needed by default Wwin2D = [ 0.1924 0.2989 0.3846 0.4325 0.4325 0.3846 0.2989 0.1924; 0.2989 0.4642 0.5974 0.6717 0.6717 0.5974 0.4642 0.2989; 0.3846 0.5974 0.7688 0.8644 0.8644 0.7688 0.5974 0.3846; 0.4325 0.6717 0.8644 0.9718 0.9718 0.8644 0.6717 0.4325; 0.4325 0.6717 0.8644 0.9718 0.9718 0.8644 0.6717 0.4325; 0.3846 0.5974 0.7688 0.8644 0.8644 0.7688 0.5974 0.3846; 0.2989 0.4642 0.5974 0.6717 0.6717 0.5974 0.4642 0.2989; 0.1924 0.2989 0.3846 0.4325 0.4325 0.3846 0.2989 0.1924]; Wwin2D_wiener = Wwin2D; else Wwin2D = kaiser(N1, beta) * kaiser(N1, beta)'; % Kaiser window used in the aggregation of the HT part Wwin2D_wiener = kaiser(N1_wiener, beta_wiener) * kaiser(N1_wiener, beta_wiener)'; % Kaiser window used in the aggregation of the Wiener filt. part end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% If needed, read images, generate noise, or scale the images to the %%%% [0,1] interval %%%% if (exist('yRGB') ~= 1) | (exist('zRGB') ~= 1) yRGB = im2double(imread(image_name)); %% read a noise-free image randn('seed', 0); %% generate seed zRGB = yRGB + (sigma/255)*randn(size(yRGB)); %% create a noisy image else % external images image_name = 'External image'; % convert zRGB to double precision zRGB = double(zRGB); % convert yRGB to double precision yRGB = double(yRGB); % if zRGB's range is [0, 255], then convert to [0, 1] if (max(zRGB(:)) > 10), % a naive check for intensity range zRGB = zRGB / 255; end % if yRGB's range is [0, 255], then convert to [0, 1] if (max(yRGB(:)) > 10), % a naive check for intensity range yRGB = yRGB / 255; end end if (size(zRGB,3) ~= 3) | (size(zRGB,4) ~= 1), error('CBM3D accepts only input RGB images (i.e. matrices of size M x N x 3).'); end % Check if the true image yRGB is a valid one; if not, then we cannot compute PSNR, etc. yRGB_is_invalid_image = (length(size(zRGB)) ~= length(size(yRGB))) | (size(zRGB,1) ~= size(yRGB,1)) | (size(zRGB,2) ~= size(yRGB,2)) | (size(zRGB,3) ~= size(yRGB,3)); if (yRGB_is_invalid_image), dump_output_information = 0; end [Xv, Xh, numSlices] = size(zRGB); %%% obtain image sizes if numSlices ~= 3 fprintf('Error, an RGB color image is required!\n'); return; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Change colorspace, compute the l2-norms of the new color channels %%%% [zColSpace l2normLumChrom] = function_rgb2LumChrom(zRGB, colorspace); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Print image information to the screen %%%% if dump_output_information == 1, fprintf(sprintf('Image: %s (%dx%dx%d), sigma: %.1f\n', image_name, Xv, Xh, numSlices, sigma)); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Step 1. Basic estimate by collaborative hard-thresholding and using %%%% the grouping constraint on the chrominances. %%%% tic; y_hat = bm3d_thr_color(zColSpace, hadper_trans_single_den, Nstep, N1, N2, lambda_thr2D,... lambda_thr3D, tau_match*N1*N1/(255*255), (Ns-1)/2, sigma/255, thrToIncStep, single(Tfor), single(Tinv)', inverse_hadper_trans_single_den, single(thr_mask), 'unused arg', 'unused arg', l2normLumChrom, Wwin2D, smallLN, stepFS ); estimate_elapsed_time = toc; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Step 2. Final estimate by collaborative Wiener filtering and using %%%% the grouping constraint on the chrominances. %%%% tic; yRGB_est = bm3d_wiener_color(zColSpace, y_hat, hadper_trans_single_den, Nstep_wiener, N1_wiener, N2_wiener, ... 'unused_arg', tau_match_wiener*N1_wiener*N1_wiener/(255*255), (Ns_wiener-1)/2, sigma/255, 'unused arg', single(TforW), single(TinvW)', inverse_hadper_trans_single_den, 'unused arg', 'unused arg', l2normLumChrom, Wwin2D_wiener, smallLNW, stepFSW ); wiener_elapsed_time = toc; yRGB_est = double(yRGB_est); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Convert back to RGB colorspace %%%% yRGB_est = function_LumChrom2rgb(yRGB_est, colorspace); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Calculate final estimate's PSNR and ISNR, print them, and show the %%%% denoised image %%%% PSNR = 0; %% Remains 0 if the true image yRGB is not available if (~yRGB_is_invalid_image), % then we assume yRGB is a valid image PSNR = 10*log10(1/mean((yRGB(:)-yRGB_est(:)).^2)); end if dump_output_information == 1, fprintf(sprintf('FINAL ESTIMATE (total time: %.1f sec), PSNR: %.2f dB\n', ... wiener_elapsed_time + estimate_elapsed_time, PSNR)); figure, imshow(min(max(zRGB,0),1)); title(sprintf('Noisy %s, PSNR: %.3f dB (sigma: %d)', ... image_name(1:end-4), 10*log10(1/mean((yRGB(:)-zRGB(:)).^2)), sigma)); figure, imshow(min(max(yRGB_est,0),1)); title(sprintf('Denoised %s, PSNR: %.3f dB', ... image_name(1:end-4), PSNR)); end return; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Some auxiliary functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % Create forward and inverse transform matrices, which allow for perfect % reconstruction. The forward transform matrix is normalized so that the % l2-norm of each basis element is 1. % % [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % INPUTS: % % N --> Size of the transform (for wavelets, must be 2^K) % % transform_type --> 'dct', 'dst', 'hadamard', or anything that is % listed by 'help wfilters' (bi-orthogonal wavelets) % 'DCrand' -- an orthonormal transform with a DC and all % the other basis elements of random nature % % dec_levels --> If a wavelet transform is generated, this is the % desired decomposition level. Must be in the % range [0, log2(N)-1], where "0" implies % full decomposition. % % OUTPUTS: % % Tforward --> (N x N) Forward transform matrix % % Tinverse --> (N x N) Inverse transform matrix % if exist('dec_levels') ~= 1, dec_levels = 0; end if N == 1, Tforward = 1; elseif strcmp(transform_type, 'hadamard') == 1, Tforward = hadamard(N); elseif (N == 8) & strcmp(transform_type, 'bior1.5')==1 % hardcoded transform so that the wavelet toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.219417649252501 0.449283757993216 0.449283757993216 0.219417649252501 -0.219417649252501 -0.449283757993216 -0.449283757993216 -0.219417649252501; 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846 -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284; -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846; 0.707106781186547 -0.707106781186547 0 0 0 0 0 0; 0 0 0.707106781186547 -0.707106781186547 0 0 0 0; 0 0 0 0 0.707106781186547 -0.707106781186547 0 0; 0 0 0 0 0 0 0.707106781186547 -0.707106781186547]; elseif (N == 8) & strcmp(transform_type, 'dct')==1 % hardcoded transform so that the signal processing toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.490392640201615 0.415734806151273 0.277785116509801 0.097545161008064 -0.097545161008064 -0.277785116509801 -0.415734806151273 -0.490392640201615; 0.461939766255643 0.191341716182545 -0.191341716182545 -0.461939766255643 -0.461939766255643 -0.191341716182545 0.191341716182545 0.461939766255643; 0.415734806151273 -0.097545161008064 -0.490392640201615 -0.277785116509801 0.277785116509801 0.490392640201615 0.097545161008064 -0.415734806151273; 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274; 0.277785116509801 -0.490392640201615 0.097545161008064 0.415734806151273 -0.415734806151273 -0.097545161008064 0.490392640201615 -0.277785116509801; 0.191341716182545 -0.461939766255643 0.461939766255643 -0.191341716182545 -0.191341716182545 0.461939766255643 -0.461939766255643 0.191341716182545; 0.097545161008064 -0.277785116509801 0.415734806151273 -0.490392640201615 0.490392640201615 -0.415734806151273 0.277785116509801 -0.097545161008064]; elseif (N == 8) & strcmp(transform_type, 'dst')==1 % hardcoded transform so that the PDE toolbox is not needed to generate it Tforward = [ 0.161229841765317 0.303012985114696 0.408248290463863 0.464242826880013 0.464242826880013 0.408248290463863 0.303012985114696 0.161229841765317; 0.303012985114696 0.464242826880013 0.408248290463863 0.161229841765317 -0.161229841765317 -0.408248290463863 -0.464242826880013 -0.303012985114696; 0.408248290463863 0.408248290463863 0 -0.408248290463863 -0.408248290463863 0 0.408248290463863 0.408248290463863; 0.464242826880013 0.161229841765317 -0.408248290463863 -0.303012985114696 0.303012985114696 0.408248290463863 -0.161229841765317 -0.464242826880013; 0.464242826880013 -0.161229841765317 -0.408248290463863 0.303012985114696 0.303012985114696 -0.408248290463863 -0.161229841765317 0.464242826880013; 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863; 0.303012985114696 -0.464242826880013 0.408248290463863 -0.161229841765317 -0.161229841765317 0.408248290463863 -0.464242826880013 0.303012985114696; 0.161229841765317 -0.303012985114696 0.408248290463863 -0.464242826880013 0.464242826880013 -0.408248290463863 0.303012985114696 -0.161229841765317]; elseif strcmp(transform_type, 'dct') == 1, Tforward = dct(eye(N)); elseif strcmp(transform_type, 'dst') == 1, Tforward = dst(eye(N)); elseif strcmp(transform_type, 'DCrand') == 1, x = randn(N); x(1:end,1) = 1; [Q,R] = qr(x); if (Q(1) < 0), Q = -Q; end; Tforward = Q'; else %% a wavelet decomposition supported by 'wavedec' %%% Set periodic boundary conditions, to preserve bi-orthogonality dwtmode('per','nodisp'); Tforward = zeros(N,N); for i = 1:N Tforward(:,i)=wavedec(circshift([1 zeros(1,N-1)],[dec_levels i-1]), log2(N), transform_type); %% construct transform matrix end end %%% Normalize the basis elements Tforward = (Tforward' * diag(sqrt(1./sum(Tforward.^2,2))))'; %%% Compute the inverse transform matrix Tinverse = inv(Tforward); return; function [y, A, l2normLumChrom]=function_rgb2LumChrom(xRGB, colormode) % Forward color-space transformation ( inverse transformation is function_LumChrom2rgb.m ) % % Alessandro Foi - Tampere University of Technology - 2005 - 2006 Public release v1.03 (March 2006) % ----------------------------------------------------------------------------------------------------------------------------------------------- % % SYNTAX: % % [y A l2normLumChrom] = function_rgb2LumChrom(xRGB, colormode); % % INPUTS: % xRGB is RGB image with range [0 1]^3 % % colormode = 'opp', 'yCbCr', 'pca', or a custom 3x3 matrix % % 'opp' Opponent color space ('opp' is equirange version) % 'yCbCr' The standard yCbCr (e.g. for JPEG images) % 'pca' Principal components (note that this transformation is renormalized to be equirange) % % OUTPUTS: % y is color-transformed image (with range typically included in or equal to [0 1]^3, depending on the transformation matrix) % % l2normLumChrom (optional) l2-norm of the transformation (useful for noise std calculation) % A transformation matrix (used necessarily if colormode='pca') % % NOTES: - If only two outputs are used, then the second output is l2normLumChrom, unless colormode='pca'; % - 'opp' is used by default if no colormode is specified. % % % USAGE EXAMPLE FOR PCA TRANSFORMATION: % %%%% -- forward color transformation -- % if colormode=='pca' % [zLumChrom colormode] = function_rgb2LumChrom(zRGB,colormode); % 'colormode' is assigned a 3x3 transform matrix % else % zLumChrom = function_rgb2LumChrom(zRGB,colormode); % end % % %%%% [ ... ] Some processing [ ... ] % % %%%% -- inverse color transformation -- % zRGB=function_LumChrom2rgb(zLumChrom,colormode); % if nargin==1 colormode='opp'; end change_output=0; if size(colormode)==[3 3] A=colormode; l2normLumChrom=sqrt(sum(A.^2,2)); else if strcmp(colormode,'opp') A=[1/3 1/3 1/3; 0.5 0 -0.5; 0.25 -0.5 0.25]; end if strcmp(colormode,'yCbCr') A=[0.299 0.587 0.114; -0.16873660714285 -0.33126339285715 0.5; 0.5 -0.4186875 -0.0813125]; end if strcmp(colormode,'pca') A=princomp(reshape(xRGB,[size(xRGB,1)*size(xRGB,2) 3]))'; A=A./repmat(sum(A.*(A>0),2)-sum(A.*(A<0),2),[1 3]); %% ranges are normalized to unitary length; else if nargout==2 change_output=1; end end end %%%% Make sure that each channel's intensity range is [0,1] maxV = sum(A.*(A>0),2); minV = sum(A.*(A<0),2); yNormal = (reshape(xRGB,[size(xRGB,1)*size(xRGB,2) 3]) * A' - repmat(minV, [1 size(xRGB,1)*size(xRGB,2)])') * diag(1./(maxV-minV)); % put in range [0,1] y = reshape(yNormal, [size(xRGB,1) size(xRGB,2) 3]); %%%% The l2-norm of each of the 3 transform basis elements l2normLumChrom = diag(1./(maxV-minV))*sqrt(sum(A.^2,2)); if change_output A=l2normLumChrom; end return; function yRGB=function_LumChrom2rgb(x,colormode) % Inverse color-space transformation ( forward transformation is function_rgb2LumChrom.m ) % % Alessandro Foi - Tampere University of Technology - 2005 - 2006 Public release v1.03 (March 2006) % ----------------------------------------------------------------------------------------------------------------------------------------------- % % SYNTAX: % % yRGB = function_LumChrom2rgb(x,colormode); % % INPUTS: % x is color-transformed image (with range typically included in or equal to [0 1]^3, depending on the transformation matrix) % % colormode = 'opp', 'yCbCr', or a custom 3x3 matrix (e.g. provided by the forward transform when 'pca' is selected) % % 'opp' opponent color space ('opp' is equirange version) % 'yCbCr' standard yCbCr (e.g. for JPEG images) % % OUTPUTS: % x is RGB image (with range [0 1]^3) % % % NOTE: 'opp' is used by default if no colormode is specified % if nargin==1 colormode='opp'; end if size(colormode)==[3 3] A=colormode; B=inv(A); else if strcmp(colormode,'opp') A =[1/3 1/3 1/3; 0.5 0 -0.5; 0.25 -0.5 0.25]; B =[1 1 2/3;1 0 -4/3;1 -1 2/3]; end if strcmp(colormode,'yCbCr') A=[0.299 0.587 0.114; -0.16873660714285 -0.33126339285715 0.5; 0.5 -0.4186875 -0.0813125]; B=inv(A); end end %%%% Make sure that each channel's intensity range is [0,1] maxV = sum(A.*(A>0),2); minV = sum(A.*(A<0),2); xNormal = reshape(x,[size(x,1)*size(x,2) 3]) * diag(maxV-minV) + repmat(minV, [1 size(x,1)*size(x,2)])'; % put in range [0,1] yRGB = reshape(xNormal * B', [ size(x,1) size(x,2) 3]); return; ================================================ FILE: BM3D/CVBM3D.m ================================================ function [Xdenoised] = CVBM3D(Xnoisy, sigma, Xorig) % CVBM3D denoising of RGB videos corrupted with AWGN. % % % [Xdenoised] = CVBM3D(Xnoisy, sigma, Xorig) % % INPUTS: % % 1) Xnoisy --> Either a filename of a noisy AVI RGB uncompressed video (e.g. 'SMg20.avi') % or a 4-D matrix of dimensions (M x N x 3 x NumberOfFrames) % The intensity range is [0,255]! % 2) Sigma --> Noise standard deviation (assumed intensity range is [0,255]) % % 3) Xorig (optional parameter) --> Filename of the original video % % OUTPUT: .avi files are written to the current matlab folder % % 1) Xdenoised --> A 4-D matrix with the denoised RGB-video % % USAGE EXAMPLES: % 1) To denoise a video: % CVBM3D('SMg20.avi', 20) % % 2) To denoise a video and print PSNR: % CVBM3D('SMg20.avi', 20, 'SM.avi') % % 1) To denoise a 4-D matrix representing a noisy RGB video: % CVBM3D(X_4D_matrix, 20) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Copyright 2009 Tampere University of Technology. All rights reserved. % This work should only be used for nonprofit purposes. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % If no input argument is provided, then use the internal ones from below: if exist('sigma', 'var') ~= 1, Xnoisy = 'SMg20.avi'; sigma = 20; ; end % Whether or not to print information to the screen dump_information = 1; % If the input is a 4-D matrix, then save it as AVI file that is used as % input to the denoising if ischar(Xnoisy) == 0; NumberOfFrames = size(Xnoisy,4); if NumberOfFrames <= 1 error('The input RGB video should be a 4-D matrix (M x N x 3 x NumberOfFrames)'); end avi_filename = sprintf('ExternalMatrix_%.6d.avi', round(rand*50000)); if exist(avi_filename, 'file') == 2, delete(avi_filename); end mov = avifile(avi_filename, 'Colormap', gray(256), 'compression', 'None', 'fps', 30); if mean2(Xnoisy) <= 1 fprintf('Possible error: the input RGB-videos should be in range [0,255] and not in [0,1]!\n'); else for ii = [1:NumberOfFrames], mov = addframe(mov, uint8(Xnoisy(:,:,:,ii))); end end mov = close(mov); if dump_information == 1 fprintf('The input 4-D matrix was written to: %s.\n', avi_filename); end clear Xnoisy Xnoisy = avi_filename; end % Read some properties of the noisy RGB video noi_avi_file_info = aviinfo(Xnoisy); NumberOfFrames = noi_avi_file_info.NumFrames; %%% Read Xorig video --- needed if one wants to compute PSNR and ISNR if exist('Xorig', 'var') == 1, if ischar(Xorig) == 1; org_avi_file_info = aviinfo(Xorig); mo = aviread(Xorig); Xorig = zeros([size(mo(1).cdata), NumberOfFrames], 'single'); for cf = 1:NumberOfFrames Xorig(:,:,:,cf) = single(mo(cf).cdata(:,:,:)); end clear mo; if (org_avi_file_info.NumFrames == noi_avi_file_info.NumFrames && org_avi_file_info.FramesPerSecond == noi_avi_file_info.FramesPerSecond && ... org_avi_file_info.Width == noi_avi_file_info.Width && org_avi_file_info.Height == noi_avi_file_info.Height) dump_information = 1; end else Xorig = single(Xorig); if mean2(Xorig) <= 1 fprintf('Possible error: the input RGB-videos should be in range [0,255] and not in [0,1]!\n'); end end end denoiseFrames = min(9, NumberOfFrames); denoiseFramesW = min(9, NumberOfFrames); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Quality/complexity trade-off %%%% %%%% 'np' --> Normal Profile (balanced quality) %%%% 'lc' --> Low Complexity Profile (fast, lower quality) %%%% if (exist('bm3dProfile') ~= 1) bm3dProfile = 'np'; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Following are the parameters for the Normal Profile. %%%% %%%% Select transforms ('dct', 'dst', 'hadamard', or anything that is listed by 'help wfilters'): transform_2D_HT_name = 'bior1.5'; %% transform used for the HT filt. of size N1 x N1 transform_2D_Wiener_name = 'dct'; %% transform used for the Wiener filt. of size N1_wiener x N1_wiener transform_3rd_dim_name = 'haar'; %% tranform used in the 3-rd dim, the same for HT and Wiener filt. %%%% Step 1: Hard-thresholding (HT) parameters: N1 = 8; %% N1 x N1 is the block size used for the hard-thresholding (HT) filtering Nstep = 5; %% sliding step to process every next refernece block N2 = 8; %% maximum number of similar blocks (maximum size of the 3rd dimension of the 3D groups) Ns = 7; %% length of the side of the search neighborhood for full-search block-matching (BM) Npr = 3; %% length of the side of the motion-adaptive search neighborhood, use din the predictive-search BM tau_match = 3000; %% threshold for the block distance (d-distance) lambda_thr3D = 2.7; %% threshold parameter for the hard-thresholding in 3D DFT domain dsub = 13; %% a small value subtracted from the distnce of blocks with the same spatial coordinate as the reference one Nb = 2; %% number of blocks to follow in each next frame, used in the predictive-search BM beta = 2.0; %% the beta parameter of the 2D Kaiser window used in the reconstruction %%%% Step 2: Wiener filtering parameters: N1_wiener = 7; Nstep_wiener = 4; N2_wiener = 8; Ns_wiener = 7; Npr_wiener = 3; tau_match_wiener = 1000; beta_wiener = 2.0; dsub_wiener = 1.5; Nb_wiener = 2; %%%% Block-matching parameters: stepFS = 1; %% step that firces to switch to full-search BM, "1" implies always full-search stepFSW = 1; thrToIncStep = 8; %% used in the HT filtering to increase the sliding step in uniform regions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Following are the parameters for the Low Complexity Profile. %%%% if strcmp(bm3dProfile, 'lc') == 1, lambda_thr3D = 2.8; denoiseFrames = min(5, NumberOfFrames); denoiseFramesW = min(5, NumberOfFrames); N2_wiener = 4; N2 = 4; Ns = 3; Ns_wiener = 3; Nb = 1; Nb_wiener = 1; end if strcmp(bm3dProfile, 'hi') == 1, Nstep = 3; Nstep_wiener = 3; end if sigma > 30, N1_wiener = 8; tau_match = 4500; tau_match_wiener = 3000; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Note: touch below this point only if you know what you are doing! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Create transform matrices, etc. %%%% decLevel = 0; %% dec. levels of the dyadic wavelet 2D transform for blocks (0 means full decomposition, higher values decrease the dec. number) decLevel3 = 0; %% dec. level for the wavelet transform in the 3rd dimension [Tfor, Tinv] = getTransfMatrix(N1, transform_2D_HT_name, decLevel); %% get (normalized) forward and inverse transform matrices [TforW, TinvW] = getTransfMatrix(N1_wiener, transform_2D_Wiener_name); %% get (normalized) forward and inverse transform matrices if (strcmp(transform_3rd_dim_name, 'haar') == 1 || strcmp(transform_3rd_dim_name(end-2:end), '1.1') == 1), %%% Fast internal transform is used, no need to generate transform %%% matrices. hadper_trans_single_den = {}; inverse_hadper_trans_single_den = {}; else %%% Create transform matrices. The transforms are later computed by %%% matrix multiplication with them for hh = [1 2 4 8 16 32]; [Tfor3rd, Tinv3rd] = getTransfMatrix(hh, transform_3rd_dim_name, decLevel3); hadper_trans_single_den{hh} = single(Tfor3rd); inverse_hadper_trans_single_den{hh} = single(Tinv3rd'); end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% 2D Kaiser windows that scale the reconstructed blocks %%%% if beta_wiener==2 & beta==2 & N1_wiener==7 & N1==8 % hardcode the window function so that the signal processing toolbox is not needed by default Wwin2D = [ 0.1924 0.2989 0.3846 0.4325 0.4325 0.3846 0.2989 0.1924; 0.2989 0.4642 0.5974 0.6717 0.6717 0.5974 0.4642 0.2989; 0.3846 0.5974 0.7688 0.8644 0.8644 0.7688 0.5974 0.3846; 0.4325 0.6717 0.8644 0.9718 0.9718 0.8644 0.6717 0.4325; 0.4325 0.6717 0.8644 0.9718 0.9718 0.8644 0.6717 0.4325; 0.3846 0.5974 0.7688 0.8644 0.8644 0.7688 0.5974 0.3846; 0.2989 0.4642 0.5974 0.6717 0.6717 0.5974 0.4642 0.2989; 0.1924 0.2989 0.3846 0.4325 0.4325 0.3846 0.2989 0.1924 ]; Wwin2D_wiener = [ 0.1924 0.3151 0.4055 0.4387 0.4055 0.3151 0.1924; 0.3151 0.5161 0.6640 0.7184 0.6640 0.5161 0.3151; 0.4055 0.6640 0.8544 0.9243 0.8544 0.6640 0.4055; 0.4387 0.7184 0.9243 1.0000 0.9243 0.7184 0.4387; 0.4055 0.6640 0.8544 0.9243 0.8544 0.6640 0.4055; 0.3151 0.5161 0.6640 0.7184 0.6640 0.5161 0.3151; 0.1924 0.3151 0.4055 0.4387 0.4055 0.3151 0.1924 ]; else Wwin2D = kaiser(N1, beta) * kaiser(N1, beta)'; % Kaiser window used in the aggregation of the HT part Wwin2D_wiener = kaiser(N1_wiener, beta_wiener) * kaiser(N1_wiener, beta_wiener)'; % Kaiser window used in the aggregation of the Wiener filt. part end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Read an image, generate noise and add it to the image %%%% if dump_information == 1 fprintf('Input video: %s, sigma: %.1f\n', Xnoisy, sigma); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Determine unique filenames of intermediate avi files %%%% HT_avi_file = sprintf('%s_cvbm3d_step1_0.avi', Xnoisy(1:end-4)); Denoised_avi_file = sprintf('%s_cvbm3d_0.avi', Xnoisy(1:end-4)); i = 1; while (exist(['./' HT_avi_file], 'file') ~= 0) | (exist(['./' Denoised_avi_file],'file') ~= 0) HT_avi_file = sprintf('%s_cvbm3d_step1_%d.avi', Xnoisy(1:end-4),i); Denoised_avi_file = sprintf('%s_cvbm3d_%d.avi', Xnoisy(1:end-4),i); i = i + 1; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Initial estimate by hard-thresholding filtering HT_IO = {which(Xnoisy), HT_avi_file}; tic; bm3d_thr_video_c(HT_IO, hadper_trans_single_den, Nstep, N1, N2, 0,... lambda_thr3D, tau_match*N1*N1/(255*255), (Ns-1)/2, sigma/255, thrToIncStep,... single(Tfor), single(Tinv)', inverse_hadper_trans_single_den, single(ones(N1)),... 'unused arg', dsub*dsub/255 * (sigma^2 / 255), ones(NumberOfFrames,1), Wwin2D,... (Npr-1)/2, stepFS, denoiseFrames, Nb, 0 ); estimate_elapsed_time = toc; if dump_information == 1 % mo = aviread(HT_avi_file); % y_hat = zeros([size(mo(1).cdata(:,:,1)), 3, NumberOfFrames], 'single'); % for cf = 1:NumberOfFrames % y_hat(:,:,:,cf) = single(mo(cf).cdata(:,:,:))/255; % end % clear mo % % PSNR_HT_ESTIMATE = 10*log10(1/mean2((Xorig-y_hat).^2)); % fprintf('HT ESTIMATE, PSNR: %.3f dB\n', PSNR_HT_ESTIMATE); % clear y_hat; fprintf('STEP1 completed!\n'); end %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % %%%% Final estimate by Wiener filtering (using the hard-thresholding % initial estimate) lut_ic = ClipComp16b(sigma/255); WIE_IO = {which(Xnoisy), HT_avi_file, Denoised_avi_file}; tic; bm3d_wiener_video_c(WIE_IO, 'unused', hadper_trans_single_den, Nstep_wiener, N1_wiener, N2_wiener, ... 'unused_arg', tau_match_wiener*N1_wiener*N1_wiener/(255*255), (Ns_wiener-1)/2, sigma/255, 'unused arg',... single(TforW), single(TinvW)', inverse_hadper_trans_single_den, 'unused arg', dsub_wiener*dsub_wiener/255*(sigma^2 / 255),... ones(NumberOfFrames,1), Wwin2D_wiener, (Npr_wiener-1)/2, stepFSW, denoiseFramesW, Nb_wiener, 0, lut_ic); wiener_elapsed_time = toc; if nargout == 1 mo = aviread(Denoised_avi_file); Xdenoised = zeros([size(mo(1).cdata(:,:,1)), 3, NumberOfFrames], 'single'); for cf = 1:NumberOfFrames Xdenoised(:,:,:,cf) = single(mo(cf).cdata(:,:,:)); end clear mo end if dump_information == 1 if nargout ~= 1 mo = aviread(Denoised_avi_file); Xdenoised = zeros([size(mo(1).cdata(:,:,1)), 3, NumberOfFrames], 'single'); for cf = 1:NumberOfFrames Xdenoised(:,:,:,cf) = single(mo(cf).cdata(:,:,:)); end clear mo end PSNR_TEXT=''; if exist('Xorig', 'var') == 1 PSNR = 10*log10(255*255/mean((Xorig(:)-Xdenoised(:)).^2)); PSNR_TEXT=sprintf(' PSNR: %.3f dB,', PSNR); New_Denoised_avi_file = sprintf('%s_PSNR%.2f.avi',Denoised_avi_file(1:end-4),PSNR); movefile(Denoised_avi_file, New_Denoised_avi_file); Denoised_avi_file = New_Denoised_avi_file; end % PSNRs = zeros(NumberOfFrames,1); % for ii = 1:NumberOfFrames, % PSNRs(ii) = 10*log10(1/mean2( (Xorig(:,:,:,ii)-Xdenoised(:,:,:,ii)).^2)); % fprintf('Frame: %d, PSNR: %.2f\n', ii, PSNRs(ii)); % end if nargout == 0 clear Xdenoised end fprintf('FILTERING COMPLETED (frames/sec: %.2f,%s denoised video saved as %s)\n', ... NumberOfFrames/(wiener_elapsed_time + estimate_elapsed_time), PSNR_TEXT, Denoised_avi_file); end return; function [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % Create forward and inverse transform matrices, which allow for perfect % reconstruction. The forward transform matrix is normalized so that the % l2-norm of each basis element is 1. % % [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % INPUTS: % % N --> Size of the transform (for wavelets, must be 2^K) % % transform_type --> 'dct', 'dst', 'hadamard', or anything that is % listed by 'help wfilters' (bi-orthogonal wavelets) % 'DCrand' -- an orthonormal transform with a DC and all % the other basis elements of random nature % % dec_levels --> If a wavelet transform is generated, this is the % desired decomposition level. Must be in the % range [0, log2(N)-1], where "0" implies % full decomposition. % % OUTPUTS: % % Tforward --> (N x N) Forward transform matrix % % Tinverse --> (N x N) Inverse transform matrix % if exist('dec_levels') ~= 1, dec_levels = 0; end if N == 1, Tforward = 1; elseif strcmp(transform_type, 'hadamard') == 1, Tforward = hadamard(N); elseif (N == 8) & strcmp(transform_type, 'bior1.5')==1 % hardcoded transform so that the wavelet toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.219417649252501 0.449283757993216 0.449283757993216 0.219417649252501 -0.219417649252501 -0.449283757993216 -0.449283757993216 -0.219417649252501; 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846 -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284; -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846; 0.707106781186547 -0.707106781186547 0 0 0 0 0 0; 0 0 0.707106781186547 -0.707106781186547 0 0 0 0; 0 0 0 0 0.707106781186547 -0.707106781186547 0 0; 0 0 0 0 0 0 0.707106781186547 -0.707106781186547]; elseif (N == 8) & strcmp(transform_type, 'dct')==1 % hardcoded transform so that the signal processing toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.490392640201615 0.415734806151273 0.277785116509801 0.097545161008064 -0.097545161008064 -0.277785116509801 -0.415734806151273 -0.490392640201615; 0.461939766255643 0.191341716182545 -0.191341716182545 -0.461939766255643 -0.461939766255643 -0.191341716182545 0.191341716182545 0.461939766255643; 0.415734806151273 -0.097545161008064 -0.490392640201615 -0.277785116509801 0.277785116509801 0.490392640201615 0.097545161008064 -0.415734806151273; 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274; 0.277785116509801 -0.490392640201615 0.097545161008064 0.415734806151273 -0.415734806151273 -0.097545161008064 0.490392640201615 -0.277785116509801; 0.191341716182545 -0.461939766255643 0.461939766255643 -0.191341716182545 -0.191341716182545 0.461939766255643 -0.461939766255643 0.191341716182545; 0.097545161008064 -0.277785116509801 0.415734806151273 -0.490392640201615 0.490392640201615 -0.415734806151273 0.277785116509801 -0.097545161008064]; elseif (N == 8) & strcmp(transform_type, 'dst')==1 % hardcoded transform so that the PDE toolbox is not needed to generate it Tforward = [ 0.161229841765317 0.303012985114696 0.408248290463863 0.464242826880013 0.464242826880013 0.408248290463863 0.303012985114696 0.161229841765317; 0.303012985114696 0.464242826880013 0.408248290463863 0.161229841765317 -0.161229841765317 -0.408248290463863 -0.464242826880013 -0.303012985114696; 0.408248290463863 0.408248290463863 0 -0.408248290463863 -0.408248290463863 0 0.408248290463863 0.408248290463863; 0.464242826880013 0.161229841765317 -0.408248290463863 -0.303012985114696 0.303012985114696 0.408248290463863 -0.161229841765317 -0.464242826880013; 0.464242826880013 -0.161229841765317 -0.408248290463863 0.303012985114696 0.303012985114696 -0.408248290463863 -0.161229841765317 0.464242826880013; 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863; 0.303012985114696 -0.464242826880013 0.408248290463863 -0.161229841765317 -0.161229841765317 0.408248290463863 -0.464242826880013 0.303012985114696; 0.161229841765317 -0.303012985114696 0.408248290463863 -0.464242826880013 0.464242826880013 -0.408248290463863 0.303012985114696 -0.161229841765317]; elseif (N == 7) & strcmp(transform_type, 'dct')==1 % hardcoded transform so that the signal processing toolbox is not needed to generate it Tforward =[ 0.377964473009227 0.377964473009227 0.377964473009227 0.377964473009227 0.377964473009227 0.377964473009227 0.377964473009227; 0.521120889169602 0.417906505941275 0.231920613924330 0 -0.231920613924330 -0.417906505941275 -0.521120889169602; 0.481588117120063 0.118942442321354 -0.333269317528993 -0.534522483824849 -0.333269317528993 0.118942442321354 0.481588117120063; 0.417906505941275 -0.231920613924330 -0.521120889169602 0 0.521120889169602 0.231920613924330 -0.417906505941275; 0.333269317528993 -0.481588117120063 -0.118942442321354 0.534522483824849 -0.118942442321354 -0.481588117120063 0.333269317528993; 0.231920613924330 -0.521120889169602 0.417906505941275 0 -0.417906505941275 0.521120889169602 -0.231920613924330; 0.118942442321354 -0.333269317528993 0.481588117120063 -0.534522483824849 0.481588117120063 -0.333269317528993 0.118942442321354]; elseif strcmp(transform_type, 'dct') == 1, Tforward = dct(eye(N)); elseif strcmp(transform_type, 'dst') == 1, Tforward = dst(eye(N)); elseif strcmp(transform_type, 'DCrand') == 1, x = randn(N); x(1:end,1) = 1; [Q,R] = qr(x); if (Q(1) < 0), Q = -Q; end; Tforward = Q'; else %% a wavelet decomposition supported by 'wavedec' %%% Set periodic boundary conditions, to preserve bi-orthogonality dwtmode('per','nodisp'); Tforward = zeros(N,N); for i = 1:N Tforward(:,i)=wavedec(circshift([1 zeros(1,N-1)],[dec_levels i-1]), log2(N), transform_type); %% construct transform matrix end end %%% Normalize the basis elements Tforward = (Tforward' * diag(sqrt(1./sum(Tforward.^2,2))))'; %%% Compute the inverse transform matrix Tinverse = inv(Tforward); return; ================================================ FILE: BM3D/IDDBM3D/BM3DDEB_init.m ================================================ function [ISNR, y_hat_RI,y_hat_RWI,zRI] = BM3DDEB_init(experiment_number, y, z, v, sigma) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Copyright 2008 Tampere University of Technology. All rights reserved. % This work should only be used for nonprofit purposes. % % AUTHORS: % Kostadin Dabov, email: kostadin.dabov _at_ tut.fi % Alessandro Foi %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % This function implements the image deblurring method proposed in: % % [1] K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "Image % restoration by sparse 3D transform-domain collaborative filtering," % Proc SPIE Electronic Imaging, January 2008. % % FUNCTION INTERFACE: % % [PSNR, y_hat_RWI] = BM3DDEB(experiment_number, test_image_name) % % INPUT: % 1) experiment_number: 1 -> PSF 1, sigma^2 = 2 % 2 -> PSF 1, sigma^2 = 8 % 3 -> PSF 2, sigma^2 = 0.308 % 4 -> PSF 3, sigma^2 = 49 % 5 -> PSF 4, sigma^2 = 4 % 6 -> PSF 5, sigma^2 = 64 % % 2) test_image_name: a valid filename of a grayscale test image % % OUTPUT: % 1) ISNR: the output improvement in SNR, dB % 2) y_hat_RWI: the restored image % % ! The function can work without any of the input arguments, % in which case, the internal default ones are used ! % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Fixed regularization parameters (obtained empirically after a rough optimization) Regularization_alpha_RI = 4e-4; Regularization_alpha_RWI = 5e-3; %%%% Experiment number (see below for details, e.g. how the blur is generated, etc.) if (exist('experiment_number') ~= 1) experiment_number = 3; % 1 -- 6 end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Select a single image filename (might contain path) %%%% % if (exist('test_image_name') ~= 1) % test_image_name = [ % % 'Lena512.png' % 'Cameraman256.png' % % 'barbara.png' % % 'house.png' % ]; % end %%%% Select 2D transforms ('dct', 'dst', 'hadamard', or anything that is listed by 'help wfilters'): transform_2D_HT_name = 'dst'; %% 2D transform (of size N1 x N1) used in Step 1 transform_2D_Wiener_name = 'dct'; %% 2D transform (of size N1_wiener x N1_wiener) used in Step 2 transform_3rd_dimage_name = 'haar'; %% 1D tranform used in the 3-rd dim, the same for both steps %%%% Step 1 (BM3D with collaborative hard-thresholding) parameters: N1 = 8; %% N1 x N1 is the block size Nstep = 3; %% sliding step to process every next refernece block N2 = 16; %% maximum number of similar blocks (maximum size of the 3rd dimensiona of a 3D array) Ns = 39; %% length of the side of the search neighborhood for full-search block-matching (BM) tau_match = 6000;%% threshold for the block distance (d-distance) lambda_thr2D = 0; %% threshold for the coarse initial denoising used in the d-distance measure lambda_thr3D = 2.9; %% threshold for the hard-thresholding beta = 0; %% the beta parameter of the 2D Kaiser window used in the reconstruction %%%% Step 2 (BM3D with collaborative Wiener filtering) parameters: N1_wiener = 8; Nstep_wiener = 2; N2_wiener = 16; Ns_wiener = 39; tau_match_wiener = 800; beta_wiener = 0; %%%% Specify whether to print results and display images print_to_screen = 0; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Note: touch below this point only if you know what you are doing! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Make parameters compatible with the interface of the mex-functions %%%% [Tfor, Tinv] = getTransfMatrix(N1, transform_2D_HT_name, 0); %% get (normalized) forward and inverse transform matrices [TforW, TinvW] = getTransfMatrix(N1_wiener, transform_2D_Wiener_name, 0); %% get (normalized) forward and inverse transform matrices if (strcmp(transform_3rd_dimage_name, 'haar') == 1), %%% Fast internal transform is used, no need to generate transform %%% matrices. hadper_trans_single_den = {}; inverse_hadper_trans_single_den = {}; else %%% Create transform matrices. The transforms are later applied by %%% vector-matrix multiplications for hpow = 0:ceil(log2(max(N2,N2_wiener))), h = 2^hpow; [Tfor3rd, Tinv3rd] = getTransfMatrix(h, transform_3rd_dimage_name, 0); hadper_trans_single_den{h} = single(Tfor3rd); inverse_hadper_trans_single_den{h} = single(Tinv3rd'); end end if beta == 0 & beta_wiener == 0 Wwin2D = ones(N1_wiener,N1_wiener); Wwin2D_wiener = ones(N1,N1); else Wwin2D = kaiser(N1, beta) * kaiser(N1, beta)'; % Kaiser window used in the hard-thresholding part Wwin2D_wiener = kaiser(N1_wiener, beta_wiener) * kaiser(N1_wiener, beta_wiener)'; % Kaiser window used in the Wiener filtering part end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % %%%% Read an image and generate a blurred and noisy image % %%%% % y = im2double(imread(test_image_name)); % % if experiment_number==1 % sigma=sqrt(2)/255; % for x1=-7:7; for x2=-7:7; v(x1+8,x2+8)=1/(x1^2+x2^2+1); end, end; v=v./sum(v(:)); % end % if experiment_number==2 % sigma=sqrt(8)/255; % s1=0; for a1=-7:7; s1=s1+1; s2=0; for a2=-7:7; s2=s2+1; v(s1,s2)=1/(a1^2+a2^2+1); end, end; v=v./sum(v(:)); % end % if experiment_number==3 % BSNR=40; sigma=-1; % if "sigma=-1", then the value of sigma depends on the BSNR % v=ones(9); v=v./sum(v(:)); % end % if experiment_number==4 % sigma=7/255; % v=[1 4 6 4 1]'*[1 4 6 4 1]; v=v./sum(v(:)); % PSF % end % if experiment_number==5 % sigma=2/255; % v=fspecial('gaussian', 25, 1.6); % end % if experiment_number==6 % sigma=8/255; % v=fspecial('gaussian', 25, .4); % end % % [Xv, Xh] = size(y); [ghy,ghx] = size(v); big_v = zeros(Xv,Xh); big_v(1:ghy,1:ghx)=v; big_v=circshift(big_v,-round([(ghy-1)/2 (ghx-1)/2])); % pad PSF with zeros to whole image domain, and center it V = fft2(big_v); % frequency response of the PSF % y_blur = imfilter(y, v, 'circular'); % performs blurring (by circular convolution) % % randn('seed',0); %%% fix seed for the random number generator % if sigma == -1; %% check whether to use BSNR in order to define value of sigma % sigma=sqrt(norm(y_blur(:)-mean(y_blur(:)),2)^2 /(Xh*Xv*10^(BSNR/10))); % compute sigma from the desired BSNR % end % % %%%% Create a blurred and noisy observation % z = y_blur + sigma*randn(Xv,Xh); tic; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Step 1: Final estimate by Regularized Inversion (RI) followed by %%%% BM3D with collaborative hard-thresholding %%%% %%%% Step 1.1. Regularized Inversion RI= conj(V)./( (abs(V).^2) + Regularization_alpha_RI * Xv*Xh*sigma^2); % Transfer Matrix for RI %% Standard Tikhonov Regularization zRI=real(ifft2( fft2(z).* RI )); % Regularized Inverse Estimate (RI OBSERVATION) stdRI = zeros(N1, N1); for ii = 1:N1, for jj = 1:N1, UnitMatrix = zeros(N1,N1); UnitMatrix(ii,jj)=1; BasisElementPadded = zeros(Xv, Xh); BasisElementPadded(1:N1,1:N1) = Tinv*UnitMatrix*Tinv'; TransfBasisElementPadded = fft2(BasisElementPadded); stdRI(ii,jj) = sqrt( (1/(Xv*Xh)) * sum(sum(abs(TransfBasisElementPadded.*RI).^2)) )*sigma; end, end %%%% Step 1.2. Colored noise suppression by BM3D with collaborative hard- %%%% thresholding y_hat_RI = bm3d_thr_colored_noise(zRI, hadper_trans_single_den, Nstep, N1, N2, lambda_thr2D,... lambda_thr3D, tau_match*N1*N1/(255*255), (Ns-1)/2, sigma, 0, single(Tfor), single(Tinv)',... inverse_hadper_trans_single_den, single(stdRI'), Wwin2D, 0, 1 ); PSNR_INITIAL_ESTIMATE = 10*log10(1/mean((y(:)-y_hat_RI(:)).^2)); ISNR_INITIAL_ESTIMATE = PSNR_INITIAL_ESTIMATE - 10*log10(1/mean((y(:)-z(:)).^2)); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Step 2: Final estimate by Regularized Wiener Inversion (RWI) followed %%%% by BM3D with collaborative Wiener filtering %%%% %%%% Step 2.1. Regularized Wiener Inversion Wiener_Pilot = abs(fft2(double(y_hat_RI))); %%% Wiener reference estimate RWI = conj(V).*Wiener_Pilot.^2./(Wiener_Pilot.^2.*(abs(V).^2) + Regularization_alpha_RWI*Xv*Xh*sigma^2); % Transfer Matrix for RWI (uses standard regularization 'a-la-Tikhonov') zRWI = real(ifft2(fft2(z).*RWI)); % RWI OBSERVATION stdRWI = zeros(N1_wiener, N1_wiener); for ii = 1:N1, for jj = 1:N1, UnitMatrix = zeros(N1,N1); UnitMatrix(ii,jj)=1; BasisElementPadded = zeros(Xv, Xh); BasisElementPadded(1:N1,1:N1) = idct2(UnitMatrix); TransfBasisElementPadded = fft2(BasisElementPadded); stdRWI(ii,jj) = sqrt( (1/(Xv*Xh)) * sum(sum(abs(TransfBasisElementPadded.*RWI).^2)) )*sigma; end, end %%%% Step 2.2. Colored noise suppression by BM3D with collaborative Wiener %%%% filtering y_hat_RWI = bm3d_wiener_colored_noise(zRWI, y_hat_RI, hadper_trans_single_den, Nstep_wiener, N1_wiener, N2_wiener, ... 0, tau_match_wiener*N1_wiener*N1_wiener/(255*255), (Ns_wiener-1)/2, 0, single(stdRWI'), single(TforW), single(TinvW)',... inverse_hadper_trans_single_den, Wwin2D_wiener, 0, 1, single(ones(N1_wiener)) ); elapsed_time = toc; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Calculate the final estimate's PSNR and ISNR, print them, and show the %%%% restored image %%%% PSNR = 10*log10(1/mean((y(:)-y_hat_RWI(:)).^2)); ISNR = PSNR - 10*log10(1/mean((y(:)-z(:)).^2)); if print_to_screen == 1 fprintf('Image: %s, Exp %d, Time: %.1f sec, PSNR-RI: %.2f dB, PSNR-RWI: %.2f, ISNR-RWI: %.2f dB\n', ... test_image_name, experiment_number, elapsed_time, PSNR_INITIAL_ESTIMATE, PSNR, ISNR); figure,imshow(z); figure,imshow(double(y_hat_RWI)); end return; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Some auxiliary functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % Create forward and inverse transform matrices, which allow for perfect % reconstruction. The forward transform matrix is normalized so that the % l2-norm of each basis element is 1. % % [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % INPUTS: % % N --> Size of the transform (for wavelets, must be 2^K) % % transform_type --> 'dct', 'dst', 'hadamard', or anything that is % listed by 'help wfilters' (bi-orthogonal wavelets) % 'DCrand' -- an orthonormal transform with a DC and all % the other basis elements of random nature % % dec_levels --> If a wavelet transform is generated, this is the % desired decomposition level. Must be in the % range [0, log2(N)-1], where "0" implies % full decomposition. % % OUTPUTS: % % Tforward --> (N x N) Forward transform matrix % % Tinverse --> (N x N) Inverse transform matrix % if exist('dec_levels') ~= 1, dec_levels = 0; end if N == 1, Tforward = 1; elseif strcmp(transform_type, 'hadamard') == 1, Tforward = hadamard(N); elseif (N == 8) & strcmp(transform_type, 'bior1.5')==1 % hardcoded transform so that the wavelet toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.219417649252501 0.449283757993216 0.449283757993216 0.219417649252501 -0.219417649252501 -0.449283757993216 -0.449283757993216 -0.219417649252501; 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846 -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284; -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846; 0.707106781186547 -0.707106781186547 0 0 0 0 0 0; 0 0 0.707106781186547 -0.707106781186547 0 0 0 0; 0 0 0 0 0.707106781186547 -0.707106781186547 0 0; 0 0 0 0 0 0 0.707106781186547 -0.707106781186547]; elseif (N == 8) & strcmp(transform_type, 'dct')==1 % hardcoded transform so that the signal processing toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.490392640201615 0.415734806151273 0.277785116509801 0.097545161008064 -0.097545161008064 -0.277785116509801 -0.415734806151273 -0.490392640201615; 0.461939766255643 0.191341716182545 -0.191341716182545 -0.461939766255643 -0.461939766255643 -0.191341716182545 0.191341716182545 0.461939766255643; 0.415734806151273 -0.097545161008064 -0.490392640201615 -0.277785116509801 0.277785116509801 0.490392640201615 0.097545161008064 -0.415734806151273; 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274; 0.277785116509801 -0.490392640201615 0.097545161008064 0.415734806151273 -0.415734806151273 -0.097545161008064 0.490392640201615 -0.277785116509801; 0.191341716182545 -0.461939766255643 0.461939766255643 -0.191341716182545 -0.191341716182545 0.461939766255643 -0.461939766255643 0.191341716182545; 0.097545161008064 -0.277785116509801 0.415734806151273 -0.490392640201615 0.490392640201615 -0.415734806151273 0.277785116509801 -0.097545161008064]; elseif (N == 8) & strcmp(transform_type, 'dst')==1 % hardcoded transform so that the PDE toolbox is not needed to generate it Tforward = [ 0.161229841765317 0.303012985114696 0.408248290463863 0.464242826880013 0.464242826880013 0.408248290463863 0.303012985114696 0.161229841765317; 0.303012985114696 0.464242826880013 0.408248290463863 0.161229841765317 -0.161229841765317 -0.408248290463863 -0.464242826880013 -0.303012985114696; 0.408248290463863 0.408248290463863 0 -0.408248290463863 -0.408248290463863 0 0.408248290463863 0.408248290463863; 0.464242826880013 0.161229841765317 -0.408248290463863 -0.303012985114696 0.303012985114696 0.408248290463863 -0.161229841765317 -0.464242826880013; 0.464242826880013 -0.161229841765317 -0.408248290463863 0.303012985114696 0.303012985114696 -0.408248290463863 -0.161229841765317 0.464242826880013; 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863; 0.303012985114696 -0.464242826880013 0.408248290463863 -0.161229841765317 -0.161229841765317 0.408248290463863 -0.464242826880013 0.303012985114696; 0.161229841765317 -0.303012985114696 0.408248290463863 -0.464242826880013 0.464242826880013 -0.408248290463863 0.303012985114696 -0.161229841765317]; elseif strcmp(transform_type, 'dct') == 1, Tforward = dct(eye(N)); elseif strcmp(transform_type, 'dst') == 1, Tforward = dst(eye(N)); elseif strcmp(transform_type, 'DCrand') == 1, x = randn(N); x(1:end,1) = 1; [Q,R] = qr(x); if (Q(1) < 0), Q = -Q; end; Tforward = Q'; else %% a wavelet decomposition supported by 'wavedec' %%% Set periodic boundary conditions, to preserve bi-orthogonality dwtmode('per','nodisp'); Tforward = zeros(N,N); for i = 1:N Tforward(:,i)=wavedec(circshift([1 zeros(1,N-1)],[dec_levels i-1]), log2(N), transform_type); %% construct transform matrix end end %%% Normalize the basis elements Tforward = (Tforward' * diag(sqrt(1./sum(Tforward.^2,2))))'; %%% Compute the inverse transform matrix Tinverse = inv(Tforward); return; ================================================ FILE: BM3D/IDDBM3D/Demo_IDDBM3D.m ================================================ function [isnr, y_hat] = Demo_IDDBM3D(experiment_number, test_image_name) % ------------------------------------------------------------------------------------------ % % Demo software for BM3D-frame based image deblurring % Public release ver. 0.8 (beta) (June 03, 2011) % % ------------------------------------------------------------------------------------------ % % This function implements the IDDBM3D image deblurring algorithm proposed in: % % [1] A.Danielyan, V. Katkovnik, and K. Egiazarian, "BM3D frames and % variational image deblurring," submitted to IEEE TIP, May 2011 % % ------------------------------------------------------------------------------------------ % % authors: Aram Danielyan % Vladimir Katkovnik % % web page: http://www.cs.tut.fi/~foi/GCF-BM3D/ % % contact: firstname.lastname@tut.fi % % ------------------------------------------------------------------------------------------ % Copyright (c) 2011 Tampere University of Technology. % All rights reserved. % This work should be used for nonprofit purposes only. % ------------------------------------------------------------------------------------------ % % Disclaimer % ---------- % % Any unauthorized use of these routines for industrial or profit-oriented activities is % expressively prohibited. By downloading and/or using any of these files, you implicitly % agree to all the terms of the TUT limited license (included in the file Legal_Notice.txt). % ------------------------------------------------------------------------------------------ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % FUNCTION INTERFACE: % % [psnr, y_hat] = Demo_IDDBM3D(experiment_number, test_image_name) % % INPUT: % 1) experiment_number: 1 -> PSF 1, sigma^2 = 2 % 2 -> PSF 1, sigma^2 = 8 % 3 -> PSF 2, sigma^2 = 0.308 % 4 -> PSF 3, sigma^2 = 49 % 5 -> PSF 4, sigma^2 = 4 % 6 -> PSF 5, sigma^2 = 64 % 7-13 -> experiments 7-13 are not described in [1]. % see this file for the blur and noise parameters. % 2) test_image_name: a valid filename of a grayscale test image % % OUTPUT: % 1) isnr the output improvement in SNR, dB % 2) y_hat: the restored image % % ! The function can work without any of the input arguments, % in which case, the internal default ones are used ! % % To run this demo functions within the BM3D package should be accessible to Matlab %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% addpath('../') if ~exist('experiment_number','var'), experiment_number=3; end if ~exist('test_image_name','var'), test_image_name='Cameraman256.png'; end filename=test_image_name; if 1 % initType = 'bm3ddeb'; %use output of the BM3DDEB to initialize the algorithm else initType = 'zeros'; %use zero image to initialize the algorithm end matchType = 'bm3ddeb'; %build groups using output of the BM3DDEB algorithm numIt = 200; fprintf('Experiment number: %d\n', experiment_number); fprintf('Image: %s\n', filename); %% ------- Generating bservation --------------------------------------------- disp('--- Generating observation ----'); y=im2double(imread(filename)); [yN,xN]=size(y); switch experiment_number case 1 sigma=sqrt(2)/255; for x1=-7:7; for x2=-7:7; h(x1+8,x2+8)=1/(x1^2+x2^2+1); end, end; h=h./sum(h(:)); case 2 sigma=sqrt(8)/255; s1=0; for a1=-7:7; s1=s1+1; s2=0; for a2=-7:7; s2=s2+1; h(s1,s2)=1/(a1^2+a2^2+1); end, end; h=h./sum(h(:)); case 3 BSNR=40; sigma=-1; % if "sigma=-1", then the value of sigma depends on the BSNR h=ones(9); h=h./sum(h(:)); case 4 sigma=7/255; h=[1 4 6 4 1]'*[1 4 6 4 1]; h=h./sum(h(:)); % PSF case 5 sigma=2/255; h=fspecial('gaussian', 25, 1.6); case 6 sigma=8/255; h=fspecial('gaussian', 25, .4); %extra experiments case 7 BSNR=30; sigma=-1; h=ones(9); h=h./sum(h(:)); case 8 BSNR=20; sigma=-1; h=ones(9); h=h./sum(h(:)); case 9 BSNR=40; sigma=-1; h=fspecial('gaussian', 25, 1.6); case 10 BSNR=20; sigma=-1; h=fspecial('gaussian', 25, 1.6); case 11 BSNR=15; sigma=-1; h=fspecial('gaussian', 25, 1.6); case 12 BSNR=40; sigma=-1; % if "sigma=-1", then the value of sigma depends on the BSNR h=ones(19); h=h./sum(h(:)); case 13 BSNR=25; sigma=-1; % if "sigma=-1", then the value of sigma depends on the BSNR h=ones(19); h=h./sum(h(:)); end y_blur = imfilter(y, h, 'circular'); % performs blurring (by circular convolution) if sigma == -1; %% check whether to use BSNR in order to define value of sigma sigma=sqrt(norm(y_blur(:)-mean(y_blur(:)),2)^2 /(yN*xN*10^(BSNR/10))); % Xv% compute sigma from the desired BSNR end %%%% Create a blurred and noisy observation randn('seed',0); z = y_blur + sigma*randn(yN, xN); bsnr=10*log10(norm(y_blur(:)-mean(y_blur(:)),2)^2 /sigma^2/yN/xN); psnr_z =PSNR(y,z,1,0); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% fprintf('Observation BSNR: %4.2f, PSNR: %4.2f\n', bsnr, psnr_z); %% ----- Computing initial estimate --------------------- disp('--- Computing initial estimate ----'); [dummy, y_hat_RI,y_hat_RWI,zRI] = BM3DDEB_init(experiment_number, y, z, h, sigma); switch lower(initType) case 'zeros' y_hat_init=zeros(size(z)); case 'zri' y_hat_init=zRI; case 'ri' y_hat_init=y_hat_RI; case 'bm3ddeb' y_hat_init=y_hat_RWI; end switch lower(matchType) case 'z' match_im = z; case 'y' match_im = y; case 'zri' match_im = zRI; case 'ri' match_im = y_hat_RI; case 'bm3ddeb' match_im = y_hat_RWI; end psnr_init = PSNR(y, y_hat_init,1,0); fprintf('Initialization method: %s\n', initType); fprintf('Initial estimate ISNR: %4.2f, PSNR: %4.2f\n', psnr_init-psnr_z, psnr_init); %% ------- Core algorithm --------------------- %------ Description of the parameters of the IDDBM3D function ---------- %y - true image (use [] if true image is unavaliable) %z - observed %h - blurring PSF %y_hat_init - initial estimate y_0 %match_im - image used to constuct groups and calculate weights g_r %sigma - standard deviation of the noise %threshType = 'h'; %use 's' for soft thresholding %numIt - number of iterations %gamma - regularization parameter see [1] %tau - regularization parameter see [1] (thresholding level) %xi - regularization parameter see [1], it is always set to 1 in this implementation %showFigure - set to True to display figure with current estimate %-------------------------------------------------------------------- threshType = 'h'; showFigure = true; switch threshType case {'s'} gamma_tau_xi_inits= [ 0.0004509 0.70 1;%1 0.0006803 0.78 1;%2 0.0003485 0.65 1;%3 0.0005259 0.72 1;%4 0.0005327 0.82 1;%5 7.632e-05 0.25 1;%6 0.0005818 0.81 1;%7 0.001149 1.18 1;%8 0.0004155 0.74 1;%9 0.0005591 0.74 1;%10 0.0007989 0.82 1;%11 0.0006702 0.75 1;%12 0.001931 1.83 1;%13 ]; case {'h'} gamma_tau_xi_inits= [ 0.00051 3.13 1;%1 0.0006004 2.75 1;%2 0.0004573 2.91 1;%3 0.0005959 2.82 1;%4 0.0006018 3.63 1;%5 0.0001726 2.24 1;%6 0.00062 2.98 1;%7 0.001047 3.80 1;%8 0.0005125 3.00 1;%9 0.0005685 2.80 1;%10 0.0005716 2.75 1;%11 0.0005938 2.55 1;%12 0.001602 4.16 1;%13 ]; end gamma = gamma_tau_xi_inits(experiment_number,1); tau = gamma_tau_xi_inits(experiment_number,2)/255*2.7; xi = gamma_tau_xi_inits(experiment_number,3); disp('-------- Start ----------'); fprintf('Number of iterations to perform: %d\n', numIt); fprintf('Thresholding type: %s\n', threshType); y_hat = IDDBM3D(y, h, z, y_hat_init, match_im, sigma, threshType, numIt, gamma, tau, xi, showFigure); psnr = PSNR(y,y_hat,1,0); isnr = psnr-psnr_z; disp('-------- Results --------'); fprintf('Final estimate ISNR: %4.2f, PSNR: %4.2f\n', isnr, psnr); return; end function PSNRdb = PSNR(x, y, maxval, borders) if ~exist('borders', 'var'), borders = 0; end if ~exist('maxval', 'var'), maxval = 255; end xx=borders+1:size(x,1)-borders; yy=borders+1:size(x,2)-borders; PSNRdb = zeros(1,size(x,3)); for fr=1:size(x,3) err = x(xx,yy,fr) - y(xx,yy,fr); PSNRdb(fr) = 10 * log10((maxval^2)/mean2(err.^2)); end end ================================================ FILE: BM3D/LEGAL_NOTICE.txt ================================================ Legal Notice By accessing these World Wide Web pages you agree to the following terms. If you do not agree to the following terms, please notice that you are not allowed to use the site. Copyright, author rights, trademarks and other intellectual property rights This website and its contents are protected by copyright, author rights and/or other intellectual property rights which are the property of Tampere University of Technology ("TUT"), its researchers and/or third parties. Reproduction, modification, and use of the materials (or any information incorporated thereto such as but not limited to reports, publications, software, pictures, diagrams, video material) published on this website are hereby authorized provided that: (i) reproduction, use, and modification are for informational and non-commercial or personal use only and will not be copied or posted on any network computer or broadcast in any media; and (ii) any reproduction or modification retains all original notices including proprietary or copyright notices; and (iii) reference to the original authors is given whenever results, which arise from the use of the provided material or any modification of it, are made public. No other use of the materials and of any information incorporated thereto is hereby authorized. In addition, be informed that some names are protected by trademarks which are the property of TUT, its researchers and/or other third parties whether a specific mention in that respect is made or not. Disclaimers The material, which is found on this website, is provided for general information only and should not be relied upon or used as the basis for making any transactions of any kind whatsoever. All the information and any part thereof provided on this website are provided AS IS without warranty of any kind either expressed or implied including, without limitation, warranties of merchantability, fitness for a particular purpose or non infringement of intellectual property rights. TUT makes no representations or warranties as to the accuracy or completeness of any materials and information incorporated thereto and contained on this website. TUT makes no representations or warranties that access to this website will be uninterrupted or error-free, that this website (the materials and/or any information incorporated thereto) will be secure and free of virus or other harmful components. The use of the materials (or any information incorporated thereto), in whole or in part, contained in this website is your sole responsibility. TUT disclaims any liability for any damages whatsoever including without limitation direct, indirect, incidental and/or consequential damages resulting from access to the website and use of the materials provided therein. This website may contain links to third party sites. The links are provided to you only as a convenience and the inclusion of any link do not imply either an endorsement by TUT of the linked sites or any warranty from TUT on said sites. Access to said linked sites is at your own risk. Transmission of user information Any and all information or request for information you may direct to TUT through this website or through e-mail as may be linked to this website is to be considered as not confidential. You may also address your information or request through mail to TUT's registered office for the attention of the department identified in the relevant part of this website. Modifications TUT reserves the right to revise the site or withdraw access to them at any time. ================================================ FILE: BM3D/README.txt ================================================ ------------------------------------------------------------------- BM3D demo software for image/video restoration and enhancement Public release v2.00 (30 January 2014) ------------------------------------------------------------------- Copyright (c) 2006-2014 Tampere University of Technology. All rights reserved. This work should be used for nonprofit purposes only. Authors: Kostadin Dabov Aram Danieyan Alessandro Foi BM3D web page: http://www.cs.tut.fi/~foi/GCF-BM3D ------------------------------------------------------------------- Contents ------------------------------------------------------------------- The package comprises these functions *) BM3D.m : BM3D grayscale-image denoising [1] *) CBM3D.m : CBM3D RGB-image denoising [2] *) VBM3D.m : VBM3D grayscale-video denoising [3] *) CVBM3D.m : CVBM3D RGB-video denoising *) BM3DSHARP.m : BM3D-SHARP grayscale-image sharepening & denoising [4] *) BM3DDEB.m : BM3D-DEB grayscale-image deblurring [5] *) IDDBM3D\Demo_IDDBM3D : IDDBM3D grayscale-image deblurring [8] *) BM3D-SAPCA\BM3DSAPCA2009 : BM3D-SAPCA grayscale-image denoising [9] *) BM3D_CFA.m : BM3D denoising of Bayer data [10] For help on how to use these scripts, you can e.g. use "help BM3D" or "help CBM3D". Each demo calls MEX-functions that allow to change all possible parameters used in the algorithm from within the corresponding M-file. ------------------------------------------------------------------- Installation ------------------------------------------------------------------- Unzip both BM3D.zip (contains codes) and BM3D_images.zip (contains test images) in a folder that is in the MATLAB path. ------------------------------------------------------------------- Requirements ------------------------------------------------------------------- *) MS Windows (32 or 64 bit), Linux (32 bit or 64 bit) or Mac OS X (32 or 64 bit) *) Matlab v.7.1 or later with installed: -- Image Processing Toolbox (for visualization with "imshow") *) CVBM3D currently supports only 32-bit and 64-bit Windows. *) IDDBM3D currently supports only 32-bit and 64-bit Windows and requires Microsoft Visual C++ 2008 SP1 Redistributable Package to be installed. It can be downloaded from: (x86) http://www.microsoft.com/downloads/en/details.aspx?FamilyID=A5C84275-3B97-4AB7-A40D-3802B2AF5FC2 (x64) http://www.microsoft.com/downloads/en/details.aspx?FamilyID=BA9257CA-337F-4B40-8C14-157CFDFFEE4E ------------------------------------------------------------------- Change log ------------------------------------------------------------------- v2.00 (30 January 2014) + Added BM3D_CFA denoising algorithm for Bayer data [10]. ! Various fixes in BM3DDEB main script: now works correctly with asymmetric PSFs; corrected several typos which caused first or second collaborative filtering stages to fail whenever the block sizes and 2-D transforms differed from the default ones. v1.9 (26 August 2011) + Added BM3D-SAPCA denoising algorithm [9]. v1.8 (4 July 2011) + Added IDDBM3D deblurring algorithm [8]. ! Improved float precision of BM3D, CBM3D, and BM3DDEB mex-files. v1.7.6 (4 February 2011) + Added support for Matlab running on Mac OSX 32-bit . Changed the strong-noise parameters ("vn" profile) in CBM3D.m, as proposed in [6]. v1.7.5 (7 July 2010) . Changed the strong-noise parameters ("vn" profile) in BM3D.m, as proposed in [6]. v1.7.4 (3 May 2010) + Added support for Matlab running on Mac OSX 64-bit v1.7.3 (15 March 2010) ! Fixed a problem with writing to AVI files in CVBM3D ! Fixed a problem with VBM3D when the input is a 3-D matrix v1.7.2 (8 Dec 2009) ! Fixed the output of CVBM3D to be in range [0,255] instead of in range [0,1] v1.7.1 (2 Dec 2009) ! Fixed a bug in VBM3D.m introduced in v1.7 that concerns the declipping v1.7 (12 Nov 2009) + Added CVBM3D.m script that performs denoising on RGB-videos with AWGN ! Fixed VBM3D.m to use declipping in the case when noisy AVI file is provided v1.6 (17 June 2009) ! Made few fixes to the "getTransfMatrix" internal function. If used with default parameters, BM3D no longer requires neither Wavelet, PDE, nor Signal Processing toolbox. + Added support for x86_64 Linux v1.5.1 (20 Nov 2008) ! Fixed bugs for older versions of Matlab + Added support for 32-bit Linux + improved the structure of the VBM3D.m script v1.5 (18 Oct 2008) + Added x86_64 version of the MEX-files that run on 64-bit Matlab under Windows + Added a missing function in BM3DDEB.m + Improves some of the comments in the codes ! Fixed a bug in VBM3D when only a input noisy video is provided v1.4.1 (26 Feb 2008) ! Fixed a bug in the grayscale-image deblurring codes and made these codes compatible with Matlab 7 or newer versions. v1.4 (1 Feb 2008) + Added grayscale-image deblurring v1.3 (12 Oct 2007) + Added grayscale-image joint sharpening and denoising v1.2.1 (4 Sept 2007) ! Fixed the output of the VBM3D to be the final Wiener estimate rather than the intermediate basic estimate ! Fixed a problem when the original video is provided as a 3D matrix v1.2 (11 June 2007) + Added grayscale-video denoising files v1.1.3 (4 May 2007) + Added support for Linux x86-compatible platforms v1.1.2 ! Fixed bugs related with Matlab v.6.1 v1.1.1 (8 March 2007) ! Fixed bugs related with Matlab v.6 (e.g., "isfloat" was not available and "imshow" did not work with single precision) + Improved the usage examples shown by executing "help BM3D" or "help CBM3D" MATLAB commands v1.1 (6 March 2007) ! Fixed a bug in comparisons of the image sizes, which was causing problems when executing "CBM3D(1,z,sigma);" ! Fixed a bug that was causing a crash when the input images are of type "uint8" ! Fixed a problem that has caused some versions of imshow to report an error ! Fixed few typos in the comments of the functions . Made the parameters of the BM3D and the C-BM3D the same v1.0 (9 December 2006) + Initial version, based on BM3D-DFT [7] package (November 2005) ------------------------------------------------------------------- References ------------------------------------------------------------------- [1] K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "Image denoising by sparse 3D transform-domain collaborative filtering," IEEE Trans. Image Process., vol. 16, no. 8, August 2007. [2] K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "Color image denoising via sparse 3D collaborative filtering with grouping constraint in luminance-chrominance space," Proc. IEEE Int. Conf. Image Process., ICIP 2007, San Antonio (TX), USA, September 2007. [3] K. Dabov, A. Foi, and K. Egiazarian, "Video denoising by sparse 3D transform-domain collaborative filtering," Proc. European Signal Process. Conf., EUSIPCO 2007, Poznan, Poland, September 2007. [4] K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "Joint image sharpening and denoising by 3D transform-domain collaborative filtering," Proc. 2007 Int. TICSP Workshop Spectral Meth. Multirate Signal Process., SMMSP 2007, Moscow, Russia, September 2007. [5] K. Dabov, A. Foi, and K. Egiazarian, "Image restoration by sparse 3D transform-domain collaborative filtering," Proc. SPIE Electronic Imaging '08, vol. 6812, no. 6812-1D, San Jose (CA), USA, January 2008. [6] Y. Hou, C. Zhao, D. Yang, and Y. Cheng, 'Comment on "Image Denoising by Sparse 3D Transform-Domain Collaborative Filtering"' accepted for publication, IEEE Trans. Image Process., July, 2010. [7] K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "Image denoising with block-matching and 3D filtering," Proc. SPIE Electronic Imaging '06, vol. 6064, no. 6064A-30, San Jose (CA), USA, January 2006. [8] A.Danielyan, V. Katkovnik, and K. Egiazarian, "BM3D frames and variational image deblurring," accepted for publication in IEEE Trans. Image Process. Preprint online at http://www.cs.tut.fi/~foi/GCF-BM3D [9] K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "BM3D Image Denoising with Shape-Adaptive Principal Component Analysis", Proc. Workshop on Signal Processing with Adaptive Sparse Structured Representations (SPARS'09), Saint-Malo, France, April 2009. [10] A. Danielyan, M. Vehvilinen, A. Foi, V. Katkovnik, and K. Egiazarian, "Cross-color BM3D filtering of noisy raw data", Proc. Int. Workshop on Local and Non-Local Approx. in Image Process., LNLA 2009, Tuusula, Finland, pp. 125-129, August 2009. ------------------------------------------------------------------- Disclaimer ------------------------------------------------------------------- Any unauthorized use of these routines for industrial or profit- oriented activities is expressively prohibited. By downloading and/or using any of these files, you implicitly agree to all the terms of the TUT limited license: http://www.cs.tut.fi/~foi/GCF-BM3D/legal_notice.html ------------------------------------------------------------------- Feedback ------------------------------------------------------------------- If you have any comment, suggestion, or question, please do contact Alessandro Foi at firstname.lastname@tut.fi ================================================ FILE: BM3D/VBM3D.m ================================================ function [PSNR_FINAL_ESTIMATE, y_hat_wi] = VBM3D(Xnoisy, sigma, NumberOfFrames, dump_information, Xorig, bm3dProfile) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % VBM3D is a Matlab function for attenuation of additive white Gaussian % noise from grayscale videos. This algorithm reproduces the results from the article: % % [1] K. Dabov, A. Foi, and K. Egiazarian, "Video denoising by sparse 3D % transform-domain collaborative filtering," European Signal Processing % Conference (EUSIPCO-2007), September 2007. (accepted) % % INTERFACE: % % [PSNR, Xest] = VBM3D(Xnoisy, Sigma, NFrames, PrintInfo, Xorig) % % INPUTS: % 1) Xnoisy --> A filename of a noisy .avi video, e.g. Xnoisy = 'gstennisg20.avi' % OR % Xnoisy --> A 3D matrix of a noisy video in a (floating point data in range [0,1], % or in [0,255]) % 2) Sigma --> Noise standard deviation (assumed range is [0,255], no matter what is % the input's range) % % 3) NFrames (optional paremter!) --> Number of frames to process. If set to 0 or % ommited, then process all frames (default: 0). % % 4) PrintInfo (optional paremter!) --> If non-zero, then print to screen and save % the denoised video in .AVI % format. (default: 1) % % 5) Xorig (optional paremter!) --> Original video's filename or 3D matrix % If provided, PSNR, ISNR will be computed. % % NOTE: If Xorig == Xnoisy, then artificial noise is added internally and the % obtained noisy video is denoised. % % OUTPUTS: % % 1) PSNR --> If Xorig is valid video, then this contains the PSNR of the % denoised one % % 1) Xest --> Final video estimate in a 3D matrix (intensities in range [0,1]) % % *) If "PrintInfo" is non-zero, then save the denoised video in the current % MATLAB folder. % % USAGE EXAMPLES: % % 1) Denoise a noisy (clipped in [0,255] range) video sequence, e.g. % 'gsalesmang20.avi' corrupted with AWGN with std. dev. 20: % % Xest = VBM3D('gsalesmang20.avi', 20, 0, 1); % % 2) The same, but also print PSNR, ISNR numbers. % % Xest = VBM3D('gsalesmang20.avi', 20, 0, 1, 'gsalesman.avi'); % % 3) Add artificial noise to a video, then denoise it (without % considering clipping in [0,255]): % % Xest = VBM3D('gsalesman.avi', 20, 0, 1, 'gsalesman.avi'); % % % RESTRICTIONS: % % Since the video sequences are read into memory as 3D matrices, % there apply restrictions on the input video size, which are thus % proportional to the maximum memory allocatable by Matlab. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Copyright 2007 Tampere University of Technology. All rights reserved. % This work should only be used for nonprofit purposes. % % AUTHORS: % Kostadin Dabov, email: dabov _at_ cs.tut.fi % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % If no input argument is provided, then use these internal ones: if exist('sigma', 'var') ~= 1, Xnoisy = 'gsalesmang20.avi'; Xorig = 'gsalesman.avi'; sigma = 20; %Xnoisy = 'gstennisg20.avi'; Xorig = 'gstennis.avi'; sigma = 20; %Xnoisy = 'gflowersg20.avi'; Xorig = 'gflower.avi'; sigma = 20; %Xnoisy = 'gsalesman.avi'; Xorig = Xnoisy; sigma = 20; NumberOfFrames = 0; %% 0 means process ALL frames. end if exist('dump_information', 'var') ~= 1, dump_information = 1; % 1 -> print informaion to the screen and save the processed video as an AVI file end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Obtain infromation about the input noisy video %%%% if (ischar(Xnoisy) == 1), % if the input is a video filename isCharacterName = 1; Xnoisy_name = Xnoisy; videoInfo = aviinfo(Xnoisy); videoHeight = videoInfo.Height; videoWidth = videoInfo.Width; TotalFrames = videoInfo.NumFrames; elseif length(size(Xnoisy)) == 3% the input argument is a 3D video (spatio-temporal) matrix Xnoisy_name = 'Input 3D matrix'; isCharacterName = 0; [videoHeight, videoWidth, TotalFrames] = size(Xnoisy); else fprintf('Oops! The input argument Xnoisy should be either a filename or a 3D matrix!\n'); PSNR_FINAL_ESTIMATE = 0; y_hat_wi = 0; return; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Check if we want to process all frames, and save as 'NumberOfFrames' %%%% the desired number of frames to process %%%% if exist('NumberOfFrames', 'var') == 1, if NumberOfFrames <= 0, NumberOfFrames = TotalFrames; else NumberOfFrames = max(min(NumberOfFrames, TotalFrames), 1); end else NumberOfFrames = TotalFrames; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Quality/complexity trade-off %%%% %%%% 'np' --> Normal Profile (balanced quality) %%%% 'lc' --> Low Complexity Profile (fast, lower quality) %%%% if (exist('bm3dProfile', 'var') ~= 1) bm3dProfile = 'np'; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Parameters for the Normal Profile. %%%% %%%% Select transforms ('dct', 'dst', 'hadamard', or anything that is listed by 'help wfilters'): transform_2D_HT_name = 'bior1.5'; %% transform used for the HT filt. of size N1 x N1 transform_2D_Wiener_name = 'dct'; %% transform used for the Wiener filt. of size N1_wiener x N1_wiener transform_3rd_dim_name = 'haar'; %% tranform used in the 3-rd dim, the same for HT and Wiener filt. %%%% Step 1: Hard-thresholding (HT) parameters: denoiseFrames = min(9, NumberOfFrames); % number of frames in the temporalwindow (should not exceed the total number of frames 'NumberOfFrames') N1 = 8; %% N1 x N1 is the block size used for the hard-thresholding (HT) filtering Nstep = 6; %% sliding step to process every next refernece block N2 = 8; %% maximum number of similar blocks (maximum size of the 3rd dimension of the 3D groups) Ns = 7; %% length of the side of the search neighborhood for full-search block-matching (BM) Npr = 5; %% length of the side of the motion-adaptive search neighborhood, use din the predictive-search BM tau_match = 3000; %% threshold for the block distance (d-distance) lambda_thr3D = 2.7; %% threshold parameter for the hard-thresholding in 3D DFT domain dsub = 7; %% a small value subtracted from the distnce of blocks with the same spatial coordinate as the reference one Nb = 2; %% number of blocks to follow in each next frame, used in the predictive-search BM beta = 2.0; %% the beta parameter of the 2D Kaiser window used in the reconstruction %%%% Step 2: Wiener filtering parameters: denoiseFramesW = min(9, NumberOfFrames); N1_wiener = 7; Nstep_wiener = 4; N2_wiener = 8; Ns_wiener = 7; Npr_wiener = 5; tau_match_wiener = 1500; beta_wiener = 2.0; dsub_wiener = 3; Nb_wiener = 2; %%%% Block-matching parameters: stepFS = 1; %% step that forces to switch to full-search BM, "1" implies always full-search smallLN = 3; %% if stepFS > 1, then this specifies the size of the small local search neighb. stepFSW = 1; smallLNW = 3; thrToIncStep = 8; %% used in the HT filtering to increase the sliding step in uniform regions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Parameters for the Low Complexity Profile. %%%% if strcmp(bm3dProfile, 'lc') == 1, lambda_thr3D = 2.8; smallLN = 2; smallLNW = 2; denoiseFrames = min(5, NumberOfFrames); denoiseFramesW = min(5, NumberOfFrames); N2_wiener = 4; N2 = 4; Ns = 3; Ns_wiener = 3; NB = 1; Nb_wiener = 1; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Parameters for the High Profile. %%%% if strcmp(bm3dProfile, 'hi') == 1, Nstep = 3; Nstep_wiener = 3; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Parameters for the "Very Noisy" Profile. %%%% if sigma > 30, N1 = 8; N1_wiener = 8; Nstep = 6; tau_match = 4500; tau_match_wiener = 3000; end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Note: touch below this point only if you know what you are doing! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Extract the input noisy video and make sure intensities are in [0,1] %%%% interval, using single-precision float if isCharacterName, mno = aviread(Xnoisy_name); z = zeros([videoHeight, videoWidth, NumberOfFrames], 'single'); for cf = 1:NumberOfFrames z(:,:,cf) = single(mno(cf).cdata(:,:,1)) * 0.0039216; % 1/255 = 0.0039216 end clear mno else if isinteger(Xnoisy) == 1, z = single(Xnoisy) * 0.0039216; % 1/255 = 0.0039216 elseif isfloat(Xnoisy) == 0, fprintf('Unknown format of "Xnoisy"! Must be a filename (array of char) or a 3D array of either floating point data (range [0,1]) or integer data (range [0,255]). \n'); return; else z = single(Xnoisy); end end clear Xnoisy; %%%% If the original video is provided, then extract it to 'Xorig' %%%% which is later used to compute PSNR and ISNR if exist('Xorig', 'var') == 1, randn('seed', 0); if ischar(Xorig) == 0, if isinteger(Xorig) == 1, y = single(Xorig) * 0.0039216; % 1/255 = 0.0039216 elseif isfloat(Xorig) == 0, fprintf('Unknown format of "Xorig"! Must be a filename (array of char) or a 3D array of either floating point data (range [0,1]) or integer data (range [0,255]). \n'); return; else y = single(Xorig); end else if strcmp(Xorig, Xnoisy_name) == 1, %% special case, noise is aritifically added y = z; z = z + (sigma/255) * randn(size(z)); else mo = aviread(Xorig); y = zeros([videoHeight, videoWidth, NumberOfFrames], 'single'); for cf = 1:NumberOfFrames y(:,:,cf) = single(mo(cf).cdata(:,:,1)) * 0.0039216; % 1/255 = 0.0039216 end clear mo end end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Create transform matrices, etc. %%%% decLevel = 0; %% dec. levels of the dyadic wavelet 2D transform for blocks (0 means full decomposition, higher values decrease the dec. number) decLevel3 = 0; %% dec. level for the wavelet transform in the 3rd dimension [Tfor, Tinv] = getTransfMatrix(N1, transform_2D_HT_name, decLevel); %% get (normalized) forward and inverse transform matrices [TforW, TinvW] = getTransfMatrix(N1_wiener, transform_2D_Wiener_name); %% get (normalized) forward and inverse transform matrices thr_mask = ones(N1); %% N1xN1 mask of threshold scaling coeff. --- by default there is no scaling, however the use of different thresholds for different wavelet decompoistion subbands can be done with this matrix if (strcmp(transform_3rd_dim_name, 'haar') == 1 || strcmp(transform_3rd_dim_name(end-2:end), '1.1') == 1), %%% Fast internal transform is used, no need to generate transform %%% matrices. hadper_trans_single_den = {}; inverse_hadper_trans_single_den = {}; else %%% Create transform matrices. The transforms are later computed by %%% matrix multiplication with them for hh = [1 2 4 8 16 32]; [Tfor3rd, Tinv3rd] = getTransfMatrix(hh, transform_3rd_dim_name, decLevel3); hadper_trans_single_den{hh} = single(Tfor3rd); inverse_hadper_trans_single_den{hh} = single(Tinv3rd'); end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% 2D Kaiser windows that scale the reconstructed blocks %%%% if beta_wiener==2 & beta==2 & N1_wiener==7 & N1==8 % hardcode the window function so that the signal processing toolbox is not needed by default Wwin2D = [ 0.1924 0.2989 0.3846 0.4325 0.4325 0.3846 0.2989 0.1924; 0.2989 0.4642 0.5974 0.6717 0.6717 0.5974 0.4642 0.2989; 0.3846 0.5974 0.7688 0.8644 0.8644 0.7688 0.5974 0.3846; 0.4325 0.6717 0.8644 0.9718 0.9718 0.8644 0.6717 0.4325; 0.4325 0.6717 0.8644 0.9718 0.9718 0.8644 0.6717 0.4325; 0.3846 0.5974 0.7688 0.8644 0.8644 0.7688 0.5974 0.3846; 0.2989 0.4642 0.5974 0.6717 0.6717 0.5974 0.4642 0.2989; 0.1924 0.2989 0.3846 0.4325 0.4325 0.3846 0.2989 0.1924 ]; Wwin2D_wiener = [ 0.1924 0.3151 0.4055 0.4387 0.4055 0.3151 0.1924; 0.3151 0.5161 0.6640 0.7184 0.6640 0.5161 0.3151; 0.4055 0.6640 0.8544 0.9243 0.8544 0.6640 0.4055; 0.4387 0.7184 0.9243 1.0000 0.9243 0.7184 0.4387; 0.4055 0.6640 0.8544 0.9243 0.8544 0.6640 0.4055; 0.3151 0.5161 0.6640 0.7184 0.6640 0.5161 0.3151; 0.1924 0.3151 0.4055 0.4387 0.4055 0.3151 0.1924 ]; else Wwin2D = kaiser(N1, beta) * kaiser(N1, beta)'; % Kaiser window used in the aggregation of the HT part Wwin2D_wiener = kaiser(N1_wiener, beta_wiener) * kaiser(N1_wiener, beta_wiener)'; % Kaiser window used in the aggregation of the Wiener filt. part end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Read an image, generate noise and add it to the image %%%% l2normLumChrom = ones(NumberOfFrames,1); %%% NumberOfFrames == nSl ! if dump_information == 1, fprintf('Video: %s (%dx%dx%d), sigma: %.1f\n', Xnoisy_name, videoHeight, videoWidth, NumberOfFrames, sigma); end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%% Initial estimate by hard-thresholding filtering tic; y_hat = bm3d_thr_video(z, hadper_trans_single_den, Nstep, N1, N2, 0,... lambda_thr3D, tau_match*N1*N1/(255*255), (Ns-1)/2, sigma/255, thrToIncStep, single(Tfor), single(Tinv)', inverse_hadper_trans_single_den, single(thr_mask), 'unused arg', dsub*dsub/255, l2normLumChrom, Wwin2D, (Npr-1)/2, stepFS, denoiseFrames, Nb ); estimate_elapsed_time = toc; if exist('Xorig', 'var') == 1, PSNR_INITIAL_ESTIMATE = 10*log10(1/mean((double(y(:))-double(y_hat(:))).^2)); PSNR_NOISE = 10*log10(1/mean((double(y(:))-double(z(:))).^2)); ISNR_INITIAL_ESTIMATE = PSNR_INITIAL_ESTIMATE - PSNR_NOISE; if dump_information == 1, fprintf('BASIC ESTIMATE (time: %.1f sec), PSNR: %.3f dB, ISNR: %.3f dB\n', ... estimate_elapsed_time, PSNR_INITIAL_ESTIMATE, ISNR_INITIAL_ESTIMATE); end end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % %%%% Final estimate by Wiener filtering (using the hard-thresholding % initial estimate) tic; y_hat_wi = bm3d_wiener_video(z, y_hat, hadper_trans_single_den, Nstep_wiener, N1_wiener, N2_wiener, ... 'unused_arg', tau_match_wiener*N1_wiener*N1_wiener/(255*255), (Ns_wiener-1)/2, sigma/255, 'unused arg', single(TforW), single(TinvW)', inverse_hadper_trans_single_den, 'unused arg', dsub_wiener*dsub_wiener/255, l2normLumChrom, Wwin2D_wiener, (Npr_wiener-1)/2, stepFSW, denoiseFramesW, Nb_wiener ); % In case the input noisy video is clipped in [0,1], then apply declipping if isCharacterName if exist('Xorig', 'var') == 1 if ~strcmp(Xorig, Xnoisy_name) [y_hat_wi] = ClipComp16b(sigma/255, y_hat_wi); end else [y_hat_wi] = ClipComp16b(sigma/255, y_hat_wi); end end wiener_elapsed_time = toc; PSNR_FINAL_ESTIMATE = 0; if exist('Xorig', 'var') == 1, PSNR_FINAL_ESTIMATE = 10*log10(1/mean((double(y(:))-double(y_hat_wi(:))).^2)); ISNR_FINAL_ESTIMATE = PSNR_FINAL_ESTIMATE - 10*log10(1/mean((double(y(:))-double(z(:))).^2)); end if dump_information == 1, text_psnr = ''; if exist('Xorig', 'var') == 1 %%%% Un-comment the following to print the PSNR of each frame % % PSNRs = zeros(NumberOfFrames,1); % for ii = [1:NumberOfFrames], % PSNRs(ii) = 10*log10(1/mean2((y(:,:,ii)-y_hat_wi(:,:,ii)).^2)); % fprintf(['Frame: ' sprintf('%d',ii) ', PSNR: ' sprintf('%.2f',PSNRs(ii)) '\n']); % end % fprintf('FINAL ESTIMATE, PSNR: %.3f dB, ISNR: %.3f dB\n', ... PSNR_FINAL_ESTIMATE, ISNR_FINAL_ESTIMATE); figure, imshow(double(z(:,:,ceil(NumberOfFrames/2)))); % show the central frame title(sprintf('Noisy frame #%d',ceil(NumberOfFrames/2))); figure, imshow(double(y_hat_wi(:,:,ceil(NumberOfFrames/2)))); % show the central frame title(sprintf('Denoised frame #%d',ceil(NumberOfFrames/2))); text_psnr = sprintf('_PSNR%.2f', PSNR_FINAL_ESTIMATE); end fprintf('The denoising took: %.1f sec (%.4f sec/frame). ', ... wiener_elapsed_time+estimate_elapsed_time, (wiener_elapsed_time+estimate_elapsed_time)/NumberOfFrames); text_vid = 'Denoised'; FRATE = 30; % default value if isCharacterName, text_vid = Xnoisy_name(1:end-4); ainfo = aviinfo(Xnoisy_name); FRATE = ainfo.FramesPerSecond; end avi_filename = sprintf('%s%s_%s_BM3D.avi', text_vid, text_psnr, bm3dProfile); if exist(avi_filename, 'file') ~= 0, delete(avi_filename); end mov = avifile(avi_filename, 'Colormap', gray(256), 'compression', 'None', 'fps', FRATE); for ii = [1:NumberOfFrames], mov = addframe(mov, uint8(round(255*double(y_hat_wi(:,:,ii))))); end mov = close(mov); fprintf('The denoised video written to: %s.\n\n', avi_filename); end return; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Some auxiliary functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % Create forward and inverse transform matrices, which allow for perfect % reconstruction. The forward transform matrix is normalized so that the % l2-norm of each basis element is 1. % % [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels) % % INPUTS: % % N --> Size of the transform (for wavelets, must be 2^K) % % transform_type --> 'dct', 'dst', 'hadamard', or anything that is % listed by 'help wfilters' (bi-orthogonal wavelets) % 'DCrand' -- an orthonormal transform with a DC and all % the other basis elements of random nature % % dec_levels --> If a wavelet transform is generated, this is the % desired decomposition level. Must be in the % range [0, log2(N)-1], where "0" implies % full decomposition. % % OUTPUTS: % % Tforward --> (N x N) Forward transform matrix % % Tinverse --> (N x N) Inverse transform matrix % if exist('dec_levels', 'var') ~= 1, dec_levels = 0; end if N == 1, Tforward = 1; elseif strcmp(transform_type, 'hadamard') == 1, Tforward = hadamard(N); elseif (N == 8) & strcmp(transform_type, 'bior1.5')==1 % hardcoded transform so that the wavelet toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.219417649252501 0.449283757993216 0.449283757993216 0.219417649252501 -0.219417649252501 -0.449283757993216 -0.449283757993216 -0.219417649252501; 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846 -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284; -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846; 0.707106781186547 -0.707106781186547 0 0 0 0 0 0; 0 0 0.707106781186547 -0.707106781186547 0 0 0 0; 0 0 0 0 0.707106781186547 -0.707106781186547 0 0; 0 0 0 0 0 0 0.707106781186547 -0.707106781186547]; elseif (N == 8) & strcmp(transform_type, 'dct')==1 % hardcoded transform so that the signal processing toolbox is not needed to generate it Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274; 0.490392640201615 0.415734806151273 0.277785116509801 0.097545161008064 -0.097545161008064 -0.277785116509801 -0.415734806151273 -0.490392640201615; 0.461939766255643 0.191341716182545 -0.191341716182545 -0.461939766255643 -0.461939766255643 -0.191341716182545 0.191341716182545 0.461939766255643; 0.415734806151273 -0.097545161008064 -0.490392640201615 -0.277785116509801 0.277785116509801 0.490392640201615 0.097545161008064 -0.415734806151273; 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274; 0.277785116509801 -0.490392640201615 0.097545161008064 0.415734806151273 -0.415734806151273 -0.097545161008064 0.490392640201615 -0.277785116509801; 0.191341716182545 -0.461939766255643 0.461939766255643 -0.191341716182545 -0.191341716182545 0.461939766255643 -0.461939766255643 0.191341716182545; 0.097545161008064 -0.277785116509801 0.415734806151273 -0.490392640201615 0.490392640201615 -0.415734806151273 0.277785116509801 -0.097545161008064]; elseif (N == 8) & strcmp(transform_type, 'dst')==1 % hardcoded transform so that the PDE toolbox is not needed to generate it Tforward = [ 0.161229841765317 0.303012985114696 0.408248290463863 0.464242826880013 0.464242826880013 0.408248290463863 0.303012985114696 0.161229841765317; 0.303012985114696 0.464242826880013 0.408248290463863 0.161229841765317 -0.161229841765317 -0.408248290463863 -0.464242826880013 -0.303012985114696; 0.408248290463863 0.408248290463863 0 -0.408248290463863 -0.408248290463863 0 0.408248290463863 0.408248290463863; 0.464242826880013 0.161229841765317 -0.408248290463863 -0.303012985114696 0.303012985114696 0.408248290463863 -0.161229841765317 -0.464242826880013; 0.464242826880013 -0.161229841765317 -0.408248290463863 0.303012985114696 0.303012985114696 -0.408248290463863 -0.161229841765317 0.464242826880013; 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863; 0.303012985114696 -0.464242826880013 0.408248290463863 -0.161229841765317 -0.161229841765317 0.408248290463863 -0.464242826880013 0.303012985114696; 0.161229841765317 -0.303012985114696 0.408248290463863 -0.464242826880013 0.464242826880013 -0.408248290463863 0.303012985114696 -0.161229841765317]; elseif (N == 7) & strcmp(transform_type, 'dct')==1 % hardcoded transform so that the signal processing toolbox is not needed to generate it Tforward =[ 0.377964473009227 0.377964473009227 0.377964473009227 0.377964473009227 0.377964473009227 0.377964473009227 0.377964473009227; 0.521120889169602 0.417906505941275 0.231920613924330 0 -0.231920613924330 -0.417906505941275 -0.521120889169602; 0.481588117120063 0.118942442321354 -0.333269317528993 -0.534522483824849 -0.333269317528993 0.118942442321354 0.481588117120063; 0.417906505941275 -0.231920613924330 -0.521120889169602 0 0.521120889169602 0.231920613924330 -0.417906505941275; 0.333269317528993 -0.481588117120063 -0.118942442321354 0.534522483824849 -0.118942442321354 -0.481588117120063 0.333269317528993; 0.231920613924330 -0.521120889169602 0.417906505941275 0 -0.417906505941275 0.521120889169602 -0.231920613924330; 0.118942442321354 -0.333269317528993 0.481588117120063 -0.534522483824849 0.481588117120063 -0.333269317528993 0.118942442321354]; elseif strcmp(transform_type, 'dct') == 1, Tforward = dct(eye(N)); elseif strcmp(transform_type, 'dst') == 1, Tforward = dst(eye(N)); elseif strcmp(transform_type, 'DCrand') == 1, x = randn(N); x(1:end,1) = 1; [Q,R] = qr(x); if (Q(1) < 0), Q = -Q; end; Tforward = Q'; else %% a wavelet decomposition supported by 'wavedec' %%% Set periodic boundary conditions, to preserve bi-orthogonality dwtmode('per','nodisp'); Tforward = zeros(N,N); for i = 1:N Tforward(:,i)=wavedec(circshift([1 zeros(1,N-1)],[dec_levels i-1]), log2(N), transform_type); %% construct transform matrix end end %%% Normalize the basis elements Tforward = (Tforward' * diag(sqrt(1./sum(Tforward.^2,2))))'; %%% Compute the inverse transform matrix Tinverse = inv(Tforward); return; ================================================ FILE: BM3D/main.m ================================================ clear;clc; pauseTime = 1; data_path = "..\Set12"; ext = ["*.jpg", "*.png", "*.jpeg"]; filePaths = []; for i = 1 : length(ext) filePaths = cat(1,filePaths, dir(fullfile(data_path,ext(i)))); end noise_leval = [10,15,20,25,30,35,40,45,50,55,60,65,70]; for i = 1:length(noise_leval) PSNRs = []; SSIMs = []; sigma = noise_leval(i); for j = 1:length(filePaths) y = imread(filePaths(j).name); if length(size(y)) > 2 y = rgb2gray(y); end y = im2double(y); z = y + (sigma/255)*randn(size(y)); % ͼ [PSNR,SSIM,y_est] = BM3D(y, z, sigma, 'np', 0); PSNRs(j) = PSNR; SSIMs(j) = SSIM; imshow(cat(2,im2uint8(y),im2uint8(z),im2uint8(y_est))); title([num2str(sigma),' ', filePaths(j).name,' ',num2str(PSNR,'%2.2f'),'dB',' ',num2str(SSIMs(j),'%2.4f')]) drawnow; pause(pauseTime) end disp(["sigma:", sigma, " psnr:", mean(PSNRs), " ssim:", mean(SSIMs)]); end ================================================ FILE: DnCNN/Demo_FDnCNN_Color.m ================================================ % This is the testing demo of Flexible DnCNN (FDnCNN) for denoising noisy color images corrupted by % AWGN. % % To run the code, you should install Matconvnet first. Alternatively, you can use the % function `vl_ffdnet_matlab` to perform denoising without Matconvnet. % % "Beyond a Gaussian Denoiser: Residual Learning of Deep CNN for Image Denoising" % "FFDNet: Toward a Fast and Flexible Solution for CNN based Image Denoising" % % Denoising" 2018/05 % If you have any question, please feel free to contact with me. % Kai Zhang (e-mail: cskaizhang@gmail.com) % clear; clc; format compact; global sigmas; % input noise level or input noise level map addpath(fullfile('utilities')); folderModel = 'model'; folderTest = 'testsets'; folderResult= 'results'; imageSets = {'CBSD68','Kodak24','McMaster'}; % testing datasets % see https://github.com/cszn/FFDNet/tree/master/testsets setTestCur = imageSets{1}; % current testing dataset showResult = 1; useGPU = 1; pauseTime = 0; imageNoiseSigma = 25; % image noise level inputNoiseSigma = 25; % input noise level folderResultCur = fullfile(folderResult, [setTestCur,'_',num2str(imageNoiseSigma(1)),'_',num2str(inputNoiseSigma(1))]); if ~isdir(folderResultCur) mkdir(folderResultCur) end load(fullfile('model','FDnCNN_color.mat')); net = vl_simplenn_tidy(net); % for i = 1:size(net.layers,2) % net.layers{i}.precious = 1; % end if useGPU net = vl_simplenn_move(net, 'gpu') ; end % read images ext = {'*.jpg','*.png','*.bmp','*.tif'}; filePaths = []; for i = 1 : length(ext) filePaths = cat(1,filePaths, dir(fullfile(folderTest,setTestCur,ext{i}))); end % PSNR and SSIM PSNRs = zeros(1,length(filePaths)); SSIMs = zeros(1,length(filePaths)); for i = 1:length(filePaths) % read images label = imread(fullfile(folderTest,setTestCur,filePaths(i).name)); [w,h,c] = size(label); if c == 3 [~,nameCur,extCur] = fileparts(filePaths(i).name); label = im2double(label); % add noise randn('seed',0); noise = bsxfun(@times,randn(size(label)),permute(imageNoiseSigma/255,[3 4 1 2])); input = single(label + noise); % tic; if useGPU input = gpuArray(input); end % set noise level map sigmas = inputNoiseSigma/255; % see "vl_simplenn.m". % perform denoising res = vl_simplenn(net,input,[],[],'conserveMemory',true,'mode','test'); % matconvnet default % res = vl_ffdnet_concise(net, input); % concise version of vl_simplenn for testing FFDNet % res = vl_ffdnet_matlab(net, input); % use this if you did not install matconvnet; very slow output = res(end).x; if useGPU output = gather(output); input = gather(input); end %toc; % calculate PSNR, SSIM and save results [PSNRCur, SSIMCur] = Cal_PSNRSSIM(im2uint8(label),im2uint8(output),0,0); if showResult imshow(cat(2,im2uint8(input),im2uint8(label),im2uint8(output))); title([filePaths(i).name,' ',num2str(PSNRCur,'%2.2f'),'dB',' ',num2str(SSIMCur,'%2.4f')]) %imwrite(im2uint8(output), fullfile(folderResultCur, [nameCur, '_' num2str(imageNoiseSigma(1),'%02d'),'_' num2str(inputNoiseSigma(1),'%02d'),'_PSNR_',num2str(PSNRCur*100,'%4.0f'), extCur] )); drawnow; pause(pauseTime) end disp([filePaths(i).name,' ',num2str(PSNRCur,'%2.2f'),'dB',' ',num2str(SSIMCur,'%2.4f')]) PSNRs(i) = PSNRCur; SSIMs(i) = SSIMCur; end end disp([mean(PSNRs),mean(SSIMs)]); ================================================ FILE: DnCNN/Demo_FDnCNN_Color_Clip.m ================================================ % This is the testing demo of Flexible DnCNN (FDnCNN) for denoising noisy color images corrupted by % AWGN with clipping setting. The noisy input is 8-bit quantized. % % To run the code, you should install Matconvnet first. Alternatively, you can use the % function `vl_ffdnet_matlab` to perform denoising without Matconvnet. % % "Beyond a Gaussian Denoiser: Residual Learning of Deep CNN for Image Denoising" % "FFDNet: Toward a Fast and Flexible Solution for CNN based Image Denoising" % % Denoising" 2018/05 % If you have any question, please feel free to contact with me. % Kai Zhang (e-mail: cskaizhang@gmail.com) % clear; clc; format compact; global sigmas; % input noise level or input noise level map addpath(fullfile('utilities')); folderModel = 'model'; folderTest = 'testsets'; folderResult= 'results'; imageSets = {'CBSD68','Kodak24','McMaster'}; % testing datasets setTestCur = imageSets{1}; % current testing dataset showResult = 1; useGPU = 1; pauseTime = 0; imageNoiseSigma = 25; % image noise level, 25.5 is the default setting of imnoise( ,'gaussian') inputNoiseSigma = 25; % input noise level folderResultCur = fullfile(folderResult, [setTestCur,'_Clip_',num2str(imageNoiseSigma(1)),'_',num2str(inputNoiseSigma(1))]); if ~isdir(folderResultCur) mkdir(folderResultCur) end load(fullfile('model','FDnCNN_Clip_color.mat')); net = vl_simplenn_tidy(net); % for i = 1:size(net.layers,2) % net.layers{i}.precious = 1; % end if useGPU net = vl_simplenn_move(net, 'gpu') ; end % read images ext = {'*.jpg','*.png','*.bmp','*.tif'}; filePaths = []; for i = 1 : length(ext) filePaths = cat(1,filePaths, dir(fullfile(folderTest,setTestCur,ext{i}))); end % PSNR and SSIM PSNRs = zeros(1,length(filePaths)); SSIMs = zeros(1,length(filePaths)); for i = 1:length(filePaths) % read images label = imread(fullfile(folderTest,setTestCur,filePaths(i).name)); [w,h,c] = size(label); if c == 3 [~,nameCur,extCur] = fileparts(filePaths(i).name); label = im2single(label); % add noise randn('seed',0); %input = imnoise(label,'gaussian'); % corresponds to imageNoiseSigma = 25.5; input = imnoise(label,'gaussian',0,(imageNoiseSigma/255)^2); % tic; if useGPU input = gpuArray(input); end % set noise level map sigmas = inputNoiseSigma/255; % see "vl_simplenn.m". % perform denoising res = vl_simplenn(net,input,[],[],'conserveMemory',true,'mode','test'); % matconvnet default %res = vl_ffdnet_concise(net, input); % concise version of vl_simplenn for testing FFDNet %res = vl_ffdnet_matlab(net, input); % use this if you did not install matconvnet; very slow . note: you should also comment net = vl_simplenn_tidy(net); and if useGPU net = vl_simplenn_move(net, 'gpu') ; end output = res(end).x; if useGPU output = gather(output); input = gather(input); end %toc; % calculate PSNR, SSIM and save results [PSNRCur, SSIMCur] = Cal_PSNRSSIM(im2uint8(label),im2uint8(output),0,0); if showResult imshow(cat(2,im2uint8(input),im2uint8(label),im2uint8(output))); title([filePaths(i).name,' ',num2str(PSNRCur,'%2.2f'),'dB',' ',num2str(SSIMCur,'%2.4f')]) % imwrite(im2uint8(output), fullfile(folderResultCur, [nameCur, '_' num2str(imageNoiseSigma(1),'%02d'),'_' num2str(inputNoiseSigma(1),'%02d'),'_PSNR_',num2str(PSNRCur*100,'%4.0f'), extCur] )); % imwrite(im2uint8(input), fullfile(folderResultCur, [nameCur, '_' num2str(imageNoiseSigma(1),'%02d'),'_' num2str(inputNoiseSigma(1),'%02d'), extCur] )); drawnow; pause(pauseTime) end disp([filePaths(i).name,' ',num2str(PSNRCur,'%2.2f'),'dB',' ',num2str(SSIMCur,'%2.4f')]) PSNRs(i) = PSNRCur; SSIMs(i) = SSIMCur; end end disp([mean(PSNRs),mean(SSIMs)]); ================================================ FILE: DnCNN/Demo_FDnCNN_Gray.m ================================================ % This is the testing demo of Flexible DnCNN (FDnCNN) for denoising noisy grayscale images corrupted by % AWGN. % % To run the code, you should install Matconvnet first. Alternatively, you can use the % function `vl_ffdnet_matlab` to perform denoising without Matconvnet. % % "Beyond a Gaussian Denoiser: Residual Learning of Deep CNN for Image Denoising" % "FFDNet: Toward a Fast and Flexible Solution for CNN based Image Denoising" % % 2018/05 % If you have any question, please feel free to contact with me. % Kai Zhang (e-mail: cskaizhang@gmail.com) %clear; clc; format compact; global sigmas; % input noise level or input noise level map addpath(fullfile('utilities')); folderModel = 'model'; folderTest = 'testsets'; folderResult= 'results'; imageSets = {'BSD68','Set12'}; % testing datasets setTestCur = imageSets{2}; % current testing dataset showResult = 1; useGPU = 1; % CPU or GPU. For single-threaded (ST) CPU computation, use "matlab -singleCompThread" to start matlab. pauseTime = 0; imageNoiseSigma = 50; % image noise level inputNoiseSigma = 50; % input noise level folderResultCur = fullfile(folderResult, [setTestCur,'_',num2str(imageNoiseSigma),'_',num2str(inputNoiseSigma)]); if ~isfolder(folderResultCur) mkdir(folderResultCur) end load(fullfile('model','FDnCNN_gray.mat')); net = vl_simplenn_tidy(net); % for i = 1:size(net.layers,2) % net.layers{i}.precious = 1; % end if useGPU net = vl_simplenn_move(net, 'gpu') ; end % read images ext = {'*.jpg','*.png','*.bmp'}; filePaths = []; for i = 1 : length(ext) filePaths = cat(1,filePaths, dir(fullfile(folderTest,setTestCur,ext{i}))); end % PSNR and SSIM PSNRs = zeros(1,length(filePaths)); SSIMs = zeros(1,length(filePaths)); for i = 1:length(filePaths) % read images label = imread(fullfile(folderTest,setTestCur,filePaths(i).name)); [w,h,~]=size(label); if size(label,3)==3 label = rgb2gray(label); end [~,nameCur,extCur] = fileparts(filePaths(i).name); label = im2double(label); % add noise randn('seed',0); noise = imageNoiseSigma/255.*randn(size(label)); input = single(label + noise); % tic; if useGPU input = gpuArray(input); end % set noise level map sigmas = inputNoiseSigma/255; % see "vl_simplenn.m". % perform denoising res = vl_simplenn(net,input,[],[],'conserveMemory',true,'mode','test'); % matconvnet default %res = vl_ffdnet_concise(net, input); % concise version of vl_simplenn for testing FFDNet %res = vl_ffdnet_matlab(net, input); % use this if you did not install matconvnet; very slow . note: you should also comment net = vl_simplenn_tidy(net); and if useGPU net = vl_simplenn_move(net, 'gpu') ; end output = res(end).x; if useGPU output = gather(output); input = gather(input); end % toc; % calculate PSNR, SSIM and save results [PSNRCur, SSIMCur] = Cal_PSNRSSIM(im2uint8(label),im2uint8(output),0,0); if showResult imshow(cat(2,im2uint8(input),im2uint8(label),im2uint8(output))); title([filePaths(i).name,' ',num2str(PSNRCur,'%2.2f'),'dB',' ',num2str(SSIMCur,'%2.4f')]) %imwrite(im2uint8(output), fullfile(folderResultCur, [nameCur, '_' num2str(imageNoiseSigma,'%02d'),'_' num2str(inputNoiseSigma,'%02d'),'_PSNR_',num2str(PSNRCur*100,'%4.0f'), extCur] )); drawnow; pause(pauseTime) end disp([filePaths(i).name,' ',num2str(PSNRCur,'%2.2f'),'dB',' ',num2str(SSIMCur,'%2.4f')]) PSNRs(i) = PSNRCur; SSIMs(i) = SSIMCur; end disp([mean(PSNRs),mean(SSIMs)]); ================================================ FILE: DnCNN/Demo_FDnCNN_Gray_Clip.m ================================================ % This is the testing demo of Flexible DnCNN (FDnCNN) for denoising noisy grayscale images corrupted by % AWGN with clipping setting. The noisy input is 8-bit quantized. % % "Beyond a Gaussian Denoiser: Residual Learning of Deep CNN for Image Denoising" % "FFDNet: Toward a Fast and Flexible Solution for CNN based Image Denoising" % % 2018/05 % If you have any question, please feel free to contact with me. % Kai Zhang (e-mail: cskaizhang@gmail.com) %clear; clc; format compact; global sigmas; % input noise level or input noise level map addpath(fullfile('utilities')); folderModel = 'models'; folderTest = 'testsets'; folderResult= 'results'; imageSets = {'BSD68','Set12'}; % testing datasets setTestCur = imageSets{2}; % current testing dataset showResult = 1; useGPU = 1; % CPU or GPU. For single-threaded (ST) CPU computation, use "matlab -singleCompThread" to start matlab. pauseTime = 0; imageNoiseSigma = 50; % image noise level, 25.5 is the default setting of imnoise( ,'gaussian') inputNoiseSigma = 50; % input noise level folderResultCur = fullfile(folderResult, [setTestCur,'_Clip_',num2str(imageNoiseSigma),'_',num2str(inputNoiseSigma)]); if ~isdir(folderResultCur) mkdir(folderResultCur) end load(fullfile('model','FDnCNN_Clip_gray.mat')); net = vl_simplenn_tidy(net); % for i = 1:size(net.layers,2) % net.layers{i}.precious = 1; % end if useGPU net = vl_simplenn_move(net, 'gpu') ; end % read images ext = {'*.jpg','*.png','*.bmp'}; filePaths = []; for i = 1 : length(ext) filePaths = cat(1,filePaths, dir(fullfile(folderTest,setTestCur,ext{i}))); end % PSNR and SSIM PSNRs = zeros(1,length(filePaths)); SSIMs = zeros(1,length(filePaths)); for i = 1:length(filePaths) % read images label = imread(fullfile(folderTest,setTestCur,filePaths(i).name)); [w,h,~]=size(label); if size(label,3)==3 label = rgb2gray(label); end [~,nameCur,extCur] = fileparts(filePaths(i).name); label = im2single(label); % add noise randn('seed',0); %input = imnoise(label,'gaussian'); % corresponds to imageNoiseSigma = 25.5; input = imnoise(label,'gaussian',0,(imageNoiseSigma/255)^2); % tic; if useGPU input = gpuArray(input); end % set noise level map sigmas = inputNoiseSigma/255; % see "vl_simplenn.m". % denoising res = vl_simplenn(net,input,[],[],'conserveMemory',true,'mode','test'); output = res(end).x; if useGPU output = gather(output); input = gather(input); end % toc; % calculate PSNR, SSIM and save results [PSNRCur, SSIMCur] = Cal_PSNRSSIM(im2uint8(label),im2uint8(output),0,0); if showResult imshow(cat(2,im2uint8(input),im2uint8(label),im2uint8(output))); title([filePaths(i).name,' ',num2str(PSNRCur,'%2.2f'),'dB',' ',num2str(SSIMCur,'%2.4f')]) %imwrite(im2uint8(output), fullfile(folderResultCur, [nameCur, '_' num2str(imageNoiseSigma,'%02d'),'_' num2str(inputNoiseSigma,'%02d'),'_PSNR_',num2str(PSNRCur*100,'%4.0f'), extCur] )); %imwrite(im2uint8(input), fullfile(folderResultCur, [nameCur, '_' num2str(imageNoiseSigma,'%02d'),'_' num2str(inputNoiseSigma,'%02d'), extCur] )); drawnow; pause(pauseTime) end disp([filePaths(i).name,' ',num2str(PSNRCur,'%2.2f'),'dB',' ',num2str(SSIMCur,'%2.4f')]) PSNRs(i) = PSNRCur; SSIMs(i) = SSIMCur; end disp([mean(PSNRs),mean(SSIMs)]); ================================================ FILE: DnCNN/Demo_test_CDnCNN_Specific.m ================================================ % This is the testing demo of CDnCNN for denoising noisy color images corrupted by % AWGN. % clear; clc; addpath('utilities'); folderTest = fullfile('testsets','CBSD68'); %%% test dataset folderModel = 'model'; showResult = 1; useGPU = 1; pauseTime = 0; % image noise level noiseSigma = 25; % model noise level modelSigma = 25; % from {5, 10, 15, 25, 35, 50} load(fullfile(folderModel,'specifics_color',['color_sigma=',num2str(modelSigma,'%02d'),'.mat'])); net = vl_simplenn_tidy(net); % for i = 1:size(net.layers,2) % net.layers{i}.precious = 1; % end % move to gpu if useGPU net = vl_simplenn_move(net, 'gpu') ; end % read images ext = {'*.jpg','*.png','*.bmp'}; filePaths = []; for i = 1 : length(ext) filePaths = cat(1,filePaths, dir(fullfile(folderTest,ext{i}))); end %%% PSNR and SSIM PSNRs = zeros(1,length(filePaths)); for i = 1:length(filePaths) % read current image label = imread(fullfile(folderTest,filePaths(i).name)); [~,nameCur,extCur] = fileparts(filePaths(i).name); label = im2double(label); % add Gaussian noise randn('seed',0); input = single(label + noiseSigma/255*randn(size(label))); % convert to GPU if useGPU input = gpuArray(input); end res = vl_simplenn(net,input,[],[],'conserveMemory',true,'mode','test'); %res = vl_ffdnet_matlab(net, input); %%% use this if you did not install matconvnet. output = input - res(end).x; % convert to CPU if useGPU output = gather(output); input = gather(input); end % calculate PSNR [PSNRCur] = Cal_PSNRSSIM(im2uint8(label),im2uint8(output),0,0); if showResult imshow(cat(2,im2uint8(label),im2uint8(input),im2uint8(output))); title([filePaths(i).name,' ',num2str(PSNRCur,'%2.2f'),'dB']) drawnow; pause(pauseTime) end PSNRs(i) = PSNRCur; end disp(mean(PSNRs)); ================================================ FILE: DnCNN/Demo_test_DnCNN.m ================================================ %%% This is the testing demo for gray image (Gaussian) denoising. %%% Training data: 400 images of size 180X180 % гԽ SCRIPT vl_nnconv Ϊִ: % һmatconvnetmatlabļµvl_setupnnͿ % clear; clc; addpath('utilities'); folderTest = fullfile('testsets','Set12'); %%% test dataset %folderTest = 'testsets\BSD68'; folderModel = 'model'; showResult = 1; useGPU = 0; pauseTime = 1; %%% load [specific] Gaussian denoising model sigm = {10,15,20,25,30,35,40,45,50,55,60,65,70}; for index=1:length(sigm) noiseSigma = sigm{index}; %%% image noise level modelSigma = min(75,max(10,round(noiseSigma/5)*5)); %%% model noise level disp(noiseSigma) load(fullfile(folderModel,'specifics',['sigma=',num2str(modelSigma,'%02d'),'.mat'])); %%% load [blind] Gaussian denoising model %%% for sigma in [0,55] % load(fullfile(folderModel,'GD_Gray_Blind.mat')); %%% net = vl_simplenn_tidy(net); % for i = 1:size(net.layers,2) % net.layers{i}.precious = 1; % end %%% move to gpu if useGPU net = vl_simplenn_move(net, 'gpu') ; end %%% read images ext = {'*.jpg','*.png','*.bmp'}; filePaths = []; for i = 1 : length(ext) % length(ext) filePaths = cat(1,filePaths, dir(fullfile(folderTest,ext{i}))); end %%% PSNR and SSIM PSNRs = zeros(1,length(filePaths)); SSIMs = zeros(1,length(filePaths)); % disp(filePaths); for i = 1:length(filePaths) %%% read images label = imread(fullfile(folderTest,filePaths(i).name)); [~,nameCur,extCur] = fileparts(filePaths(i).name); label = im2double(label); randn('seed',0); input = single(label + noiseSigma/255*randn(size(label))); %%% convert to GPU if useGPU input = gpuArray(input); end res = vl_simplenn(net,input,[],[],'conserveMemory',true,'mode','test'); %res = simplenn_matlab(net, input); %%% use this if you did not install matconvnet. output = input - res(end).x; %%% convert to CPU if useGPU output = gather(output); input = gather(input); end %%% calculate PSNR and SSIM [PSNRCur, SSIMCur] = Cal_PSNRSSIM(im2uint8(label),im2uint8(output),0,0); if showResult imshow(cat(2,im2uint8(label),im2uint8(input),im2uint8(output))); title([filePaths(i).name,' ',num2str(PSNRCur,'%2.2f'),'dB',' ',num2str(SSIMCur,'%2.4f')]) drawnow; pause(pauseTime) end PSNRs(i) = PSNRCur; SSIMs(i) = SSIMCur; end disp([mean(PSNRs),mean(SSIMs)]); disp('=========='); end ================================================ FILE: DnCNN/Demo_test_DnCNN3.m ================================================ %%% This is the testing demo for learning a single model for three tasks, including Gaussian denoing, SISR, JPEG image deblocking. % clear; clc; addpath('utilities'); %%% testing set tasks = {'GD','SR','DB'}; %%% three tasks imageSets = {'BSD68','Set5','Set14','BSD100','Urben100','classic5','LIVE1'}; %%% testing dataset %%% setting taskTest = tasks([1 2 3]); %%% choose the tasks for evaluation setTest = {imageSets([1]),imageSets([2:5]),imageSets([6 7])}; %%% select the datasets for each tasks showResult = [1 1 1]; %%% save the restored images pauseTime = 1; folderModel = 'model'; useGPU = 1; % 1 or 0, true or false folderTest = 'testsets'; folderResult= 'results'; if ~exist(folderResult,'file') mkdir(folderResult); end %%% task GD = Gaussian Denoising sigma = 25; %%% task SR = Single Image Super-Resolution scale = 3; %%% task DB = DeBlocking Q = 20; %%% load DnCNN-3 model load(fullfile(folderModel,'DnCNN3.mat')); %net = vl_simplenn_tidy(net); % for i = 1:size(net.layers,2) % net.layers{i}.precious = 1; % end if useGPU net = vl_simplenn_move(net, 'gpu') ; end %%% input (single); output (single); label (ground-truth, uint8) %%% input_RGB (uint8); output_RGB (uint8); label_RGB (ground-truth, uint8) %%%------------------------------------------------------------------------------------- %%% Gaussian Denoising (GD) %%%------------------------------------------------------------------------------------- if ismember('GD',taskTest) taskTestCur = 'GD'; for n_set = 1 : numel(setTest{1}) %%% read images setTestCur = cell2mat(setTest{1}(n_set)); disp('-----------------------------------------------'); disp(['----',setTestCur,'------Gaussian Denoising-----']); disp('-----------------------------------------------'); folderTestCur = fullfile(folderTest,setTestCur); ext = {'*.jpg','*.png','*.bmp'}; filepaths = []; for i = 1 : length(ext) filepaths = cat(1,filepaths,dir(fullfile(folderTestCur, ext{i}))); end eval(['PSNR_',taskTestCur,'_',setTestCur,'_s',num2str(sigma),' = zeros(length(filepaths),1);']); eval(['SSIM_',taskTestCur,'_',setTestCur,'_s',num2str(sigma),' = zeros(length(filepaths),1);']); %%% folder to store results folderResultCur = fullfile(folderResult, [taskTestCur,'_',setTestCur,'_s',num2str(sigma)]); if ~exist(folderResultCur,'file') mkdir(folderResultCur); end for i = 1 : length(filepaths) label = imread(fullfile(folderTestCur,filepaths(i).name)); [~,imageName,ext] = fileparts(filepaths(i).name); chanel = size(label,3); if chanel == 3 %%% label (uint8) label = rgb2gray(label); end %%% input (single) randn('seed',0); input = single(im2double(label) + sigma/255*randn(size(label))); if useGPU input = gpuArray(input); end res = vl_simplenn(net, input,[],[],'conserveMemory',true,'mode','test'); im = res(end).x; %%% output (single) output = gather(input - im); [PSNR_Cur,SSIM_Cur] = Cal_PSNRSSIM(label,im2uint8(output),0,0); disp(['Denoising ',num2str(PSNR_Cur,'%2.2f'),'dB',' ',filepaths(i).name]); eval(['PSNR_',taskTestCur,'_',setTestCur,'_s',num2str(sigma),'(',num2str(i),') = PSNR_Cur;']); eval(['SSIM_',taskTestCur,'_',setTestCur,'_s',num2str(sigma),'(',num2str(i),') = SSIM_Cur;']); if showResult(1) imshow(cat(1,cat(2,im2uint8(input),im2uint8(output)),cat(2,im2uint8(abs(input-output)*10),label))); drawnow; title(['Denoising ',filepaths(i).name,' ',num2str(PSNR_Cur,'%2.2f'),'dB'],'FontSize',12) pause(pauseTime) %pause() %%% save results imwrite(output,fullfile(folderResultCur,[imageName,'_s',num2str(sigma),'.png'])); end end disp(['Average PSNR is ',num2str(mean(eval(['PSNR_',taskTestCur,'_',setTestCur,'_s',num2str(sigma)])),'%2.2f'),'dB']); disp(['Average SSIM is ',num2str(mean(eval(['SSIM_',taskTestCur,'_',setTestCur,'_s',num2str(sigma)])),'%2.4f')]); %%% save PSNR and SSIM metrics save(fullfile(folderResultCur,['PSNR_',taskTestCur,'_',setTestCur,'_s',num2str(sigma),'.mat']),['PSNR_',taskTestCur,'_',setTestCur,'_s',num2str(sigma)]) save(fullfile(folderResultCur,['SSIM_',taskTestCur,'_',setTestCur,'_s',num2str(sigma),'.mat']),['SSIM_',taskTestCur,'_',setTestCur,'_s',num2str(sigma)]) end end %%%------------------------------------------------------------------------------------- %%% Single Image Super-Resolution (SR) %%%------------------------------------------------------------------------------------- if ismember('SR',taskTest) taskTestCur = 'SR'; for n_set = 1 : numel(setTest{2}) %%% read images setTestCur = cell2mat(setTest{2}(n_set)); disp('--------------------------------------------'); disp(['----',setTestCur,'-----Super-Resolution-----']); disp('--------------------------------------------'); folderTestCur = fullfile(folderTest,setTestCur); ext = {'*.jpg','*.png','*.bmp'}; filepaths = []; for i = 1 : length(ext) filepaths = cat(1,filepaths,dir(fullfile(folderTestCur, ext{i}))); end eval(['PSNR_',taskTestCur,'_',setTestCur,'_x',num2str(scale),' = zeros(length(filepaths),1);']); eval(['SSIM_',taskTestCur,'_',setTestCur,'_x',num2str(scale),' = zeros(length(filepaths),1);']); if fix(scale) == scale crop = scale; else crop = scale*10; end %%% folder to store results folderResultCur = fullfile(folderResult, [taskTestCur,'_',setTestCur,'_x',num2str(scale)]); if ~exist(folderResultCur,'file') mkdir(folderResultCur); end for i = 1 : length(filepaths) HR = imread(fullfile(folderTestCur,filepaths(i).name)); [~,imageName,ext] = fileparts(filepaths(i).name); HR = modcrop(HR, crop); %%% label_RGB (uint8) label_RGB = HR; chanel = size(HR,3); %%% LR (uint8) LR = imresize(HR,1/scale,'bicubic'); if chanel == 3 %%% label (single) HR_ycc = single(rgb2ycbcr(im2double(HR))); label = HR_ycc(:,:,1); %%% input (single) HR_bic = imresize(im2double(LR),scale,'bicubic'); LR_bic_ycc = rgb2ycbcr(HR_bic); input = im2single(LR_bic_ycc(:,:,1)); %%% input_RGB (uint8) input_RGB = im2uint8(HR_bic); else %%% label (single) label = im2single(HR); HR_bic = imresize(LR,scale,'bicubic'); %%% input (single) input = im2single(HR_bic); %%% input_RGB (uint8) input_RGB = HR_bic; end if useGPU input = gpuArray(input); end res = vl_simplenn(net, input,[],[],'conserveMemory',true,'mode','test'); im = res(end).x; %%% output (single) output = gather(input - im); if chanel == 3 %%% output_RGB (uint8) LR_bic_ycc(:,:,1) = double(output); output_RGB = im2uint8(ycbcr2rgb(LR_bic_ycc)); else %%% output_RGB (uint8) output_RGB = im2uint8(output); end [PSNR_Cur,SSIM_Cur] = Cal_PSNRSSIM(label*255,output*255,ceil(scale),ceil(scale)); %%% single disp(['Single Image Super-Resolution ',num2str(PSNR_Cur,'%2.2f'),'dB',' ',filepaths(i).name]); eval(['PSNR_SR_',setTestCur,'_x',num2str(scale),'(',num2str(i),') = PSNR_Cur;']); eval(['SSIM_SR_',setTestCur,'_x',num2str(scale),'(',num2str(i),') = SSIM_Cur;']); if showResult(2) imshow(cat(1,cat(2,input_RGB,output_RGB),cat(2,(output_RGB-input_RGB),label_RGB))); drawnow; title(['Single Image Super-Resolution ',filepaths(i).name,' ',num2str(PSNR_Cur,'%2.2f'),'dB'],'FontSize',12) pause(pauseTime) % pause() %%% save results imwrite(output_RGB,fullfile(folderResultCur,[imageName,'_x',num2str(scale),'.png'])); end end disp(['Average PSNR is ',num2str(mean(eval(['PSNR_',taskTestCur,'_',setTestCur,'_x',num2str(scale)])),'%2.2f'),'dB']); disp(['Average SSIM is ',num2str(mean(eval(['SSIM_',taskTestCur,'_',setTestCur,'_x',num2str(scale)])),'%2.4f')]); %%% save PSNR and SSIM metrics save(fullfile(folderResultCur,['PSNR_',taskTestCur,'_',setTestCur,'_x',num2str(scale),'.mat']),['PSNR_',taskTestCur,'_',setTestCur,'_x',num2str(scale)]) save(fullfile(folderResultCur,['SSIM_',taskTestCur,'_',setTestCur,'_x',num2str(scale),'.mat']),['SSIM_',taskTestCur,'_',setTestCur,'_x',num2str(scale)]) end end %%%------------------------------------------------------------------------------------- %%% JPEG Image Deblocking (DB) %%%------------------------------------------------------------------------------------- if ismember('DB',taskTest) taskTestCur = 'DB'; for n_set = 1 : numel(setTest{3}) %%% read image names setTestCur = cell2mat(setTest{3}(n_set)); disp('---------------------------------------'); disp(['----',setTestCur,'------Deblocking-----']); disp('---------------------------------------'); folderTestCur = fullfile(folderTest,setTestCur); ext = {'*.jpg','*.png','*.bmp'}; filepaths = []; for i = 1 : length(ext) filepaths = cat(1,filepaths,dir(fullfile(folderTestCur, ext{i}))); end %%% to store PSNR and SSIM results eval(['PSNR_',taskTestCur,'_',setTestCur,'_q',num2str(Q),' = zeros(length(filepaths),1);']); eval(['SSIM_',taskTestCur,'_',setTestCur,'_q',num2str(Q),' = zeros(length(filepaths),1);']); %%% to store results folderResultCur = fullfile(folderResult, [taskTestCur,'_',setTestCur,'_q',num2str(Q)]); if ~exist(folderResultCur,'file') mkdir(folderResultCur); end for i = 1 : length(filepaths) label = imread(fullfile(folderTestCur,filepaths(i).name)); [~,imageName,ext] = fileparts(filepaths(i).name); chanel = size(label,3); if chanel == 3 %%% label (uint8) label = rgb2ycbcr(label); label = label(:,:,1); end %%% input (single) imwrite(label,'test.jpg','jpg','quality',Q); input = im2single(imread('test.jpg')); if useGPU input = gpuArray(input); end res = vl_simplenn(net, input,[],[],'conserveMemory',true,'mode','test'); im = res(end).x; %%% output (single) output = gather(input - im); [PSNR_Cur,SSIM_Cur] = Cal_PSNRSSIM(label,im2uint8(output),0,0); disp(['Deblocking ',num2str(PSNR_Cur,'%2.2f'),'dB',' ',filepaths(i).name]); eval(['PSNR_',taskTestCur,'_',setTestCur,'_q',num2str(Q),'(',num2str(i),') = PSNR_Cur;']); eval(['SSIM_',taskTestCur,'_',setTestCur,'_q',num2str(Q),'(',num2str(i),') = SSIM_Cur;']); if showResult(3) imshow(cat(1,cat(2,im2uint8(input),im2uint8(output)),cat(2,im2uint8(abs(input-output)*10),label))); drawnow; title(['Deblocking ',filepaths(i).name,' ',num2str(PSNR_Cur,'%2.2f'),'dB'],'FontSize',12) pause(pauseTime) %%% save results imwrite(output,fullfile(folderResultCur,[imageName,'_q',num2str(Q),'.png'])); end end disp(['Average PSNR is ',num2str(mean(eval(['PSNR_',taskTestCur,'_',setTestCur,'_q',num2str(Q)])),'%2.2f'),'dB']); disp(['Average SSIM is ',num2str(mean(eval(['SSIM_',taskTestCur,'_',setTestCur,'_q',num2str(Q)])),'%2.4f')]); %%% save PSNR and SSIM metrics save(fullfile(folderResultCur,['PSNR_',taskTestCur,'_',setTestCur,'_q',num2str(Q),'.mat']),['PSNR_',taskTestCur,'_',setTestCur,'_q',num2str(Q)]) save(fullfile(folderResultCur,['SSIM_',taskTestCur,'_',setTestCur,'_q',num2str(Q),'.mat']),['SSIM_',taskTestCur,'_',setTestCur,'_q',num2str(Q)]) end end ================================================ FILE: DnCNN/Demo_test_DnCNN_C.m ================================================ %%% This is the testing code demo for color image (Gaussian) denoising. %%% The model is trained with 1) noise levels in [0 55]; 2) 432 training images. % clear; clc; addpath('utilities'); folderTest = 'testsets\CBSD68'; %%% test dataset folderModel = 'model'; noiseSigma = 45; %%% image noise level showResult = 1; useGPU = 1; pauseTime = 1; %%% load blind Gaussian denoising model (color image) load(fullfile(folderModel,'GD_Color_Blind.mat')); %%% for sigma in [0,55] %%% % net = vl_simplenn_tidy(net); % for i = 1:size(net.layers,2) % net.layers{i}.precious = 1; % end %%% move to gpu if useGPU net = vl_simplenn_move(net, 'gpu') ; end %%% read images ext = {'*.jpg','*.png','*.bmp'}; filePaths = []; for i = 1 : length(ext) filePaths = cat(1,filePaths, dir(fullfile(folderTest,ext{i}))); end %%% PSNR and SSIM PSNRs = zeros(1,length(filePaths)); for i = 1:length(filePaths) %%% read current image label = imread(fullfile(folderTest,filePaths(i).name)); [~,nameCur,extCur] = fileparts(filePaths(i).name); label = im2double(label); %%% add Gaussian noise randn('seed',0); input = single(label + noiseSigma/255*randn(size(label))); %%% convert to GPU if useGPU input = gpuArray(input); end res = vl_simplenn(net,input,[],[],'conserveMemory',true,'mode','test'); %res = simplenn_matlab(net, input); %%% use this if you did not install matconvnet. output = input - res(end).x; %%% convert to CPU if useGPU output = gather(output); input = gather(input); end %%% calculate PSNR [PSNRCur] = Cal_PSNRSSIM(im2uint8(label),im2uint8(output),0,0); if showResult imshow(cat(2,im2uint8(label),im2uint8(input),im2uint8(output))); title([filePaths(i).name,' ',num2str(PSNRCur,'%2.2f'),'dB']) drawnow; pause(pauseTime) end PSNRs(i) = PSNRCur; end disp(mean(PSNRs)); ================================================ FILE: DnCNN/model/README.txt ================================================ ## Beyond a Gaussian Denoiser: Residual Learning of Deep CNN for Image Denoising ### Main Contents **demos**: `Demo_test_DnCNN-.m`. **model**: including the trained models for Gaussian denoising; a single model for Gaussian denoising, single image super-resolution (SISR) and deblocking. **testsets**: BSD68 and Set10 for Gaussian denoising evaluation; Set5, Set14, BSD100 and Urban100 datasets for SISR evaluation; Classic5 and LIVE1 for JPEG image deblocking evaluation. To run the testing demos `Demo_test_DnCNN-.m`, you should first [install](http://www.vlfeat.org/matconvnet/install/) [MatConvNet](http://www.vlfeat.org/matconvnet/). Note: If you did not install MatConvNet, just replace `res = vl_simplenn(net,input,[],[],'conserveMemory',true,'mode','test')` with `res = simplenn_matlab(net, input)`. For the training code, feel free to contact: cskaizhang@gmail.com ### Results #### Gaussian Denoising The average PSNR(dB) results of different methods on the BSD6868 dataset. | Noise Level | BM3D | WNNM | EPLL | MLP | CSF |TNRD | DnCNN-S | DnCNN-B | |:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|:-------:| | 15 | 31.07 | 31.37 | 31.21 | - | 31.24 | 31.42 | **31.73** | **31.61** | | 25 | 28.57 | 28.83 | 28.68 | 28.96 | 28.74 | 28.92 | **29.23** | **29.16** | | 50 | 25.62 | 25.87 | 25.67 | 26.03 | - | 25.97 | **26.23** | **26.23** | #### Gaussian Denoising, Single ImageSuper-Resolution and JPEG Image Deblocking via a Single (DnCNN-3) Model Average PSNR(dB)/SSIM results of different methods for Gaussian denoising with noise level 15, 25 and 50 on BSD68 dataset, single image super-resolution with upscaling factors 2, 3 and 40 on Set5, Set14, BSD100 and Urban100 datasets, JPEG image deblocking with quality factors 10, 20, 30 and 40 on Classic5 and LIVE11 datasets. ###### Gaussian Denoising | Dataset | Noise Level | BM3D | TNRD | DnCNN-3 | |:---------:|:---------:|:---------:|:---------:|:---------:| | | 15 | 31.08 / 0.8722 | 31.42 / 0.8826 | 31.46 / 0.8826 | | BSD68 | 25 | 28.57 / 0.8017 | 28.92 / 0.8157 | 29.02 / 0.8190 | | | 50 | 25.62 / 0.6869 | 25.97 / 0.7029 | 26.10 / 0.7076 | ###### Single Image Super-Resolution | Dataset | Upscaling Factor | TNRD | VDSR |DnCNN-3| |:---------:|:---------:|:---------:|:---------:|:---------:| | | 2 | 36.86 / 0.9556 | 37.56 / 0.9591 | 37.58 / 0.9590 | |Set5 | 3 | 33.18 / 0.9152 | 33.67 / 0.9220 | 33.75 / 0.9222 | | | 4 | 30.85 / 0.8732 | 31.35 / 0.8845 | 31.40 / 0.8845 | || | | 2 | 32.51 / 0.9069 | 33.02 / 0.9128 | 33.03 / 0.9128 | |Set14 | 3 | 29.43 / 0.8232 | 29.77 / 0.8318 | 29.81 / 0.8321 | | | 4 | 27.66 / 0.7563 | 27.99 / 0.7659 | 28.04 / 0.7672 | || | | 2 | 31.40 / 0.8878 | 31.89 / 0.8961 | 31.90 / 0.8961 | |BSD100 | 3 | 28.50 / 0.7881 | 28.82 / 0.7980 | 28.85 / 0.7981 | | | 4 | 27.00 / 0.7140 | 27.28 / 0.7256 | 27.29 / 0.7253 | || | | 2 | 29.70 / 0.8994 | 30.76 / 0.9143 | 30.74 / 0.9139 | |Urban100| 3 | 26.42 / 0.8076 | 27.13 / 0.8283 | 27.15 / 0.8276 | | | 4 | 24.61 / 0.7291 | 25.17 / 0.7528 | 25.20 / 0.7521 | ###### JPEG Image Deblocking | Dataset | Quality Factor | AR-CNN | TNRD | DnCNN-3 | |:---------:|:---------:|:---------:|:---------:|:---------:| |Classic5| 10 | 29.03 / 0.7929 | 29.28 / 0.7992 | 29.40 / 0.8026 | | | 20 | 31.15 / 0.8517 | 31.47 / 0.8576 | 31.63 / 0.8610 | | | 30 | 32.51 / 0.8806 | 32.78 / 0.8837 | 32.91 / 0.8861 | | | 40 | 33.34 / 0.8953 | - | 33.77 / 0.9003 | || | LIVE1 | 10 | 28.96 / 0.8076 | 29.15 / 0.8111 | 29.19 / 0.8123 | | | 20 | 31.29 / 0.8733 | 31.46 / 0.8769 | 31.59 / 0.8802 | | | 30 | 32.67 / 0.9043 | 32.84 / 0.9059 | 32.98 / 0.9090 | | | 40 | 33.63 / 0.9198 | - | 33.96 / 0.9247 | ================================================ FILE: DnCNN/model/specifics_color/Add (color) specific models.md ================================================ ================================================ FILE: DnCNN/utilities/Cal_PSNRSSIM.m ================================================ function [psnr_cur, ssim_cur] = Cal_PSNRSSIM(A,B,row,col) [n,m,ch]=size(B); A = A(row+1:n-row,col+1:m-col,:); B = B(row+1:n-row,col+1:m-col,:); A=double(A); % Ground-truth B=double(B); % e=A(:)-B(:); mse=mean(e.^2); psnr_cur=10*log10(255^2/mse); if ch==1 [ssim_cur, ~] = ssim_index(A, B); else ssim_cur = (ssim_index(A(:,:,1), B(:,:,1)) + ssim_index(A(:,:,2), B(:,:,2)) + ssim_index(A(:,:,3), B(:,:,3)))/3; end function [mssim, ssim_map] = ssim_index(img1, img2, K, window, L) %======================================================================== %SSIM Index, Version 1.0 %Copyright(c) 2003 Zhou Wang %All Rights Reserved. % %The author is with Howard Hughes Medical Institute, and Laboratory %for Computational Vision at Center for Neural Science and Courant %Institute of Mathematical Sciences, New York University. % %---------------------------------------------------------------------- %Permission to use, copy, or modify this software and its documentation %for educational and research purposes only and without fee is hereby %granted, provided that this copyright notice and the original authors' %names appear on all copies and supporting documentation. This program %shall not be used, rewritten, or adapted as the basis of a commercial %software or hardware product without first obtaining permission of the %authors. The authors make no representations about the suitability of %this software for any purpose. It is provided "as is" without express %or implied warranty. %---------------------------------------------------------------------- % %This is an implementation of the algorithm for calculating the %Structural SIMilarity (SSIM) index between two images. Please refer %to the following paper: % %Z. Wang, A. C. Bovik, H. R. Sheikh, and E. P. Simoncelli, "Image %quality assessment: From error measurement to structural similarity" %IEEE Transactios on Image Processing, vol. 13, no. 1, Jan. 2004. % %Kindly report any suggestions or corrections to zhouwang@ieee.org % %---------------------------------------------------------------------- % %Input : (1) img1: the first image being compared % (2) img2: the second image being compared % (3) K: constants in the SSIM index formula (see the above % reference). defualt value: K = [0.01 0.03] % (4) window: local window for statistics (see the above % reference). default widnow is Gaussian given by % window = fspecial('gaussian', 11, 1.5); % (5) L: dynamic range of the images. default: L = 255 % %Output: (1) mssim: the mean SSIM index value between 2 images. % If one of the images being compared is regarded as % perfect quality, then mssim can be considered as the % quality measure of the other image. % If img1 = img2, then mssim = 1. % (2) ssim_map: the SSIM index map of the test image. The map % has a smaller size than the input images. The actual size: % size(img1) - size(window) + 1. % %Default Usage: % Given 2 test images img1 and img2, whose dynamic range is 0-255 % % [mssim ssim_map] = ssim_index(img1, img2); % %Advanced Usage: % User defined parameters. For example % % K = [0.05 0.05]; % window = ones(8); % L = 100; % [mssim ssim_map] = ssim_index(img1, img2, K, window, L); % %See the results: % % mssim %Gives the mssim value % imshow(max(0, ssim_map).^4) %Shows the SSIM index map % %======================================================================== if (nargin < 2 || nargin > 5) ssim_index = -Inf; ssim_map = -Inf; return; end if (size(img1) ~= size(img2)) ssim_index = -Inf; ssim_map = -Inf; return; end [M N] = size(img1); if (nargin == 2) if ((M < 11) || (N < 11)) ssim_index = -Inf; ssim_map = -Inf; return end window = fspecial('gaussian', 11, 1.5); % K(1) = 0.01; % default settings K(2) = 0.03; % L = 255; % end if (nargin == 3) if ((M < 11) || (N < 11)) ssim_index = -Inf; ssim_map = -Inf; return end window = fspecial('gaussian', 11, 1.5); L = 255; if (length(K) == 2) if (K(1) < 0 || K(2) < 0) ssim_index = -Inf; ssim_map = -Inf; return; end else ssim_index = -Inf; ssim_map = -Inf; return; end end if (nargin == 4) [H W] = size(window); if ((H*W) < 4 || (H > M) || (W > N)) ssim_index = -Inf; ssim_map = -Inf; return end L = 255; if (length(K) == 2) if (K(1) < 0 || K(2) < 0) ssim_index = -Inf; ssim_map = -Inf; return; end else ssim_index = -Inf; ssim_map = -Inf; return; end end if (nargin == 5) [H W] = size(window); if ((H*W) < 4 || (H > M) || (W > N)) ssim_index = -Inf; ssim_map = -Inf; return end if (length(K) == 2) if (K(1) < 0 || K(2) < 0) ssim_index = -Inf; ssim_map = -Inf; return; end else ssim_index = -Inf; ssim_map = -Inf; return; end end C1 = (K(1)*L)^2; C2 = (K(2)*L)^2; window = window/sum(sum(window)); img1 = double(img1); img2 = double(img2); mu1 = filter2(window, img1, 'valid'); mu2 = filter2(window, img2, 'valid'); mu1_sq = mu1.*mu1; mu2_sq = mu2.*mu2; mu1_mu2 = mu1.*mu2; sigma1_sq = filter2(window, img1.*img1, 'valid') - mu1_sq; sigma2_sq = filter2(window, img2.*img2, 'valid') - mu2_sq; sigma12 = filter2(window, img1.*img2, 'valid') - mu1_mu2; if (C1 > 0 & C2 > 0) ssim_map = ((2*mu1_mu2 + C1).*(2*sigma12 + C2))./((mu1_sq + mu2_sq + C1).*(sigma1_sq + sigma2_sq + C2)); else numerator1 = 2*mu1_mu2 + C1; numerator2 = 2*sigma12 + C2; denominator1 = mu1_sq + mu2_sq + C1; denominator2 = sigma1_sq + sigma2_sq + C2; ssim_map = ones(size(mu1)); index = (denominator1.*denominator2 > 0); ssim_map(index) = (numerator1(index).*numerator2(index))./(denominator1(index).*denominator2(index)); index = (denominator1 ~= 0) & (denominator2 == 0); ssim_map(index) = numerator1(index)./denominator1(index); end mssim = mean2(ssim_map); return ================================================ FILE: DnCNN/utilities/Merge_Bnorm_Demo.m ================================================ load('sigma=25_Bnorm.mat'); [net] = vl_simplenn_mergebnorm(net); save sigma=25 net; ================================================ FILE: DnCNN/utilities/data_augmentation.m ================================================ function image = data_augmentation(image, mode) if mode == 1 return; end if mode == 2 % flipped image = flipud(image); return; end if mode == 3 % rotation 90 image = rot90(image,1); return; end if mode == 4 % rotation 90 & flipped image = rot90(image,1); image = flipud(image); return; end if mode == 5 % rotation 180 image = rot90(image,2); return; end if mode == 6 % rotation 180 & flipped image = rot90(image,2); image = flipud(image); return; end if mode == 7 % rotation 270 image = rot90(image,3); return; end if mode == 8 % rotation 270 & flipped image = rot90(image,3); image = flipud(image); return; end ================================================ FILE: DnCNN/utilities/modcrop.m ================================================ function imgs = modcrop(imgs, modulo) if size(imgs,3)==1 sz = size(imgs); sz = sz - mod(sz, modulo); imgs = imgs(1:sz(1), 1:sz(2)); else tmpsz = size(imgs); sz = tmpsz(1:2); sz = sz - mod(sz, modulo); imgs = imgs(1:sz(1), 1:sz(2),:); end ================================================ FILE: DnCNN/utilities/shave.m ================================================ function I = shave(I, border) I = I(1+border(1):end-border(1), ... 1+border(2):end-border(2), :, :); ================================================ FILE: DnCNN/utilities/simplenn_matlab.m ================================================ function res = simplenn_matlab(net, input) %% If you did not install the matconvnet package, you can use this for testing. n = numel(net.layers); res = struct('x', cell(1,n+1)); res(1).x = input; for ilayer = 1 : n l = net.layers{ilayer}; switch l.type case 'conv' for noutmaps = 1 : size(l.weights{1},4) z = zeros(size(res(ilayer).x,1),size(res(ilayer).x,2),'single'); for ninmaps = 1 : size(res(ilayer).x,3) z = z + convn(res(ilayer).x(:,:,ninmaps), l.weights{1}(:,:,ninmaps,noutmaps),'same'); end res(ilayer+1).x(:,:,noutmaps) = z + l.weights{2}(noutmaps); end case 'relu' res(ilayer+1).x = max(res(ilayer).x,0); end res(ilayer).x = []; end end ================================================ FILE: DnCNN/utilities/vl_ffdnet_concise.m ================================================ function res = vl_ffdnet_concise(net, x) global sigmas; n = numel(net.layers); res = struct('x', cell(1,n+1)); res(1).x = x ; cudnn = {'CuDNN'} ; %cudnn = {'NoCuDNN'} ; for i=1:n l = net.layers{i} ; switch l.type case 'conv' res(i+1).x = vl_nnconv(res(i).x, l.weights{1}, l.weights{2}, ... 'pad', l.pad, ... 'stride', l.stride, ... 'dilate', l.dilate, ... l.opts{:}, ... cudnn{:}) ; case 'concat' if size(sigmas,1)~=size(res(i).x,1) sigmaMap = bsxfun(@times,ones(size(res(i).x,1),size(res(i).x,2),1,size(res(i).x,4),'single'),permute(sigmas,[3 4 1 2])); res(i+1).x = cat(3,res(i).x,sigmaMap); else res(i+1).x = cat(3,res(i).x,sigmaMap); end case 'SubP' res(i+1).x = vl_nnSubP(res(i).x, [],'scale',l.scale); case 'relu' res(i+1).x = max(res(i).x,0) ; end res(i).x = [] ; end ================================================ FILE: DnCNN/utilities/vl_ffdnet_matlab.m ================================================ function res = vl_ffdnet_matlab(net, input) %% If you did not install the matconvnet package, you can use this for testing. global sigmas; n = numel(net.layers); res = struct('x', cell(1,n+1)); res(1).x = input; for i = 1 : n l = net.layers{i}; switch l.type case 'conv' disp(['Processing ... ',int2str(i),'/',int2str(n)]); for noutmaps = 1 : size(l.weights{1},4) z = zeros(size(res(i).x,1),size(res(i).x,2),'single'); for ninmaps = 1 : size(res(i).x,3) z = z + convn(res(i).x(:,:,ninmaps), rot90(l.weights{1}(:,:,ninmaps,noutmaps),2),'same'); % 180 degree rotation for kernel end res(i+1).x(:,:,noutmaps) = z + l.weights{2}(noutmaps); end case 'relu' res(i+1).x = max(res(i).x,0); case 'concat' if size(sigmas,1)~=size(res(i).x,1) sigmaMap = bsxfun(@times,ones(size(res(i).x,1),size(res(i).x,2),1,size(res(i).x,4),'single'),permute(sigmas,[3 4 1 2])); res(i+1).x = cat(3,res(i).x,sigmaMap); else res(i+1).x = cat(3,res(i).x,sigmaMap); end case 'SubP' res(i+1).x = vl_nnSubP(res(i).x, [],'scale',l.scale); end res(i).x = []; end end ================================================ FILE: DnCNN/utilities/vl_simplenn.m ================================================ function res = vl_simplenn(net, x, dzdy, res, varargin) %VL_SIMPLENN Evaluate a SimpleNN network. % RES = VL_SIMPLENN(NET, X) evaluates the convnet NET on data X. % RES = VL_SIMPLENN(NET, X, DZDY) evaluates the convnent NET and its % derivative on data X and output derivative DZDY (foward+bacwkard pass). % RES = VL_SIMPLENN(NET, X, [], RES) evaluates the NET on X reusing the % structure RES. % RES = VL_SIMPLENN(NET, X, DZDY, RES) evaluates the NET on X and its % derivatives reusing the structure RES. % % This function process networks using the SimpleNN wrapper % format. Such networks are 'simple' in the sense that they consist % of a linear sequence of computational layers. You can use the % `dagnn.DagNN` wrapper for more complex topologies, or write your % own wrapper around MatConvNet computational blocks for even % greater flexibility. % Copyright (C) 2014-15 Andrea Vedaldi. % All rights reserved. % % This file is part of the VLFeat library and is made available under % the terms of the BSD license (see the COPYING file). global sigmas; opts.conserveMemory = false ; opts.sync = false ; opts.mode = 'normal' ; opts.accumulate = false ; opts.cudnn = true ; opts.backPropDepth = +inf ; opts.skipForward = false ; opts.parameterServer = [] ; opts.holdOn = false ; opts = vl_argparse(opts, varargin); n = numel(net.layers) ; assert(opts.backPropDepth > 0, 'Invalid `backPropDepth` value (!>0)'); backPropLim = max(n - opts.backPropDepth + 1, 1); if (nargin <= 2) || isempty(dzdy) doder = false ; if opts.skipForward error('simplenn:skipForwardNoBackwPass', ... '`skipForward` valid only when backward pass is computed.'); end else doder = true ; end if opts.cudnn cudnn = {'CuDNN'} ; bnormCudnn = {'NoCuDNN'} ; % ours seems slighty faster else cudnn = {'NoCuDNN'} ; bnormCudnn = {'NoCuDNN'} ; end switch lower(opts.mode) case 'normal' testMode = false ; case 'test' testMode = true ; otherwise error('Unknown mode ''%s''.', opts. mode) ; end gpuMode = isa(x, 'gpuArray') ; if nargin <= 3 || isempty(res) if opts.skipForward error('simplenn:skipForwardEmptyRes', ... 'RES structure must be provided for `skipForward`.'); end res = struct(... 'x', cell(1,n+1), ... 'dzdx', cell(1,n+1), ... 'dzdw', cell(1,n+1), ... 'aux', cell(1,n+1), ... 'stats', cell(1,n+1), ... 'time', num2cell(zeros(1,n+1)), ... 'backwardTime', num2cell(zeros(1,n+1))) ; end if ~opts.skipForward res(1).x = x ; end % ------------------------------------------------------------------------- % Forward pass % ------------------------------------------------------------------------- for i=1:n if opts.skipForward, break; end; l = net.layers{i} ; %res(i).time = tic ; switch l.type case 'conv' res(i+1).x = vl_nnconv(res(i).x, l.weights{1}, l.weights{2}, ... 'pad', l.pad, ... 'stride', l.stride, ... 'dilate', l.dilate, ... l.opts{:}, ... cudnn{:}) ; case 'concat' if size(sigmas,1)~=size(res(i).x,1) sigmaMap = bsxfun(@times,ones(size(res(i).x,1),size(res(i).x,2),1,size(res(i).x,4)),permute(sigmas,[3 4 1 2])) ; res(i+1).x = vl_nnconcat({res(i).x,sigmaMap}) ; else res(i+1).x = vl_nnconcat({res(i).x,sigmas}) ; end case 'SubP' res(i+1).x = vl_nnSubP(res(i).x, [],'scale',l.scale) ; case 'relu' leak = {} ; res(i+1).x = vl_nnrelu(res(i).x,[],leak{:}) ; end % optionally forget intermediate results needsBProp = doder && i >= backPropLim; forget = opts.conserveMemory && ~needsBProp ; if i > 1 lp = net.layers{i-1} ; % forget RELU input, even for BPROP forget = forget && (~needsBProp || (strcmp(l.type, 'relu') && ~lp.precious)) ; forget = forget && ~(strcmp(lp.type, 'loss') || strcmp(lp.type, 'softmaxloss')) ; forget = forget && ~lp.precious ; end if forget res(i).x = [] ; end if gpuMode && opts.sync wait(gpuDevice) ; end %res(i).time = toc(res(i).time) ; end ================================================ FILE: DnCNN/utilities/vl_simplenn_mergebnorm.m ================================================ function [net1] = vl_simplenn_mergebnorm(net) %% merge bnorm parameters into adjacent Conv layer for i = 1:numel(net.layers) if strcmp(net.layers{i}.type, 'conv') net.layers{i}.weightDecay(2) = 1; end end for i = 1:numel(net.layers) if strcmp(net.layers{i}.type, 'bnorm') ws = net.layers{i}.weights{1}; bs = net.layers{i}.weights{2}; mu_sigmas = net.layers{i}.weights{3}; for j = 1:numel(ws) net.layers{i-1}.weights{1}(:,:,:,j) =single(double(net.layers{i-1}.weights{1}(:,:,:,j))*double(ws(j))/(double(mu_sigmas(j,2)))); net.layers{i-1}.weights{2}(j) =single(double(bs(j)) - double(ws(j))*double(mu_sigmas(j,1))/(double(mu_sigmas(j,2)))); end net.layers{i-1}.learningRate(2) = 1; end end net1 = net; net1.layers = {}; net1 = rmfield(net1,'meta'); for i = 1:numel(net.layers) if ~strcmp(net.layers{i}.type, 'bnorm') net1.layers{end+1} = net.layers{i}; end end net1.layers = net1.layers(1:end-1); end ================================================ FILE: README.md ================================================ ### 1. 项目介绍 #### 1.1 项目的背景 该项目是为了研究基于深度卷积神经网络的图像去噪算法,是利用DnCNN模型,但是为了比较该算法的效果,另外实现了四种传统的图像去噪算法(均值滤波、中值滤波、非局部均值滤波NLM和三维块匹配滤波BM3D)作为对照组。 #### 1.2 噪声强度和类型 项目中实现五种算法对噪声强度为10,15,20...60,65,70的高斯白噪声进行处理。 #### 1.3 评价指标 图像去噪后,如何评估算法去噪效果的好坏呢?项目中采用峰值信噪比PSNR和结构相似性SSIM作为评价指标。一般来说,PSNR越大,去噪效果越好。SSIM取值为0到1,越接近1,表示效果越好。 ### 2. 数据集介绍 该项目中只是对Set12数据集进行处理,也就是项目中的Set12目录下的12张图片。如果觉得数据量不够充分,可以自行添加其他数据集,在代码中修改一下数据集的目录即可。 ### 3. 代码介绍 对于均值滤波、中值滤波、和NLM,MATLAB都已经实现了,所以我们直接调用MATLAB自带的函数就可以。 BM3D和DnCNN的代码都是从别人那儿clone下来,做了一些小的修改。 五种算法都是对Set12数据集进行去噪,去噪的结果并没有保存,只是在运行过程中能看到去噪前和去噪后的图像对比,感兴趣的朋友可以自己将图像保存下来观察。 ### 4. 代码运行 五种算法分别在五个不同的目录中,所以你只需要进行对应的目录,运行代码即可。 + 均值滤波、中值滤波、NLM算法对应的目录分别为avefilter、medainfilter、nlm-image-denoising。每个目录下只有一个.m文件,所以只需要运行对应的文件即可。 + BM3D对应的目录是BM3D,运行该目录下的main.m程序即可。 + DnCNN对应的目录是DnCNN,运行该目录下的Demo_test_DnCNN.m程序即可,该算法目录中对应的还有好几个代码,都是原项目中有的,我没有动过,感兴趣的朋友可以自己看看。 ================================================ FILE: avefilter/avefilt.m ================================================ clear,clc; pauseTime = 1; data_path = "..\Set12"; ext = ["*.jpg", "*.png", "*.jpeg"]; filePaths = []; for i = 1 : length(ext) filePaths = cat(1,filePaths, dir(fullfile(data_path,ext(i)))); end noise_leval = [10,15,20,25,30,35,40,45,50,55,60,65,70]; for ii = 1:length(noise_leval) PSNRs = zeros(1, length(filePaths)); SSIMs = zeros(1, length(filePaths)); sigma = noise_leval(ii); for jj = 1:length(filePaths) % ԭͼ originImage = im2double(imread(filePaths(jj).name)); % Ӹ˹ imageWithNoise = single(originImage + sigma/255*randn(size(originImage))); [rows, cols] = size(originImage); y = imageWithNoise; % ֵ˲㷨 % ָģߴ boxSize = 3; template = zeros(boxSize); for i = 1:rows-boxSize+1 for j = 1:cols-boxSize+1 % ȡģ template = imageWithNoise(i:i+(boxSize-1),j:j+(boxSize-1)); % þֵģĵֵ s = sum(template(:)); y(i+(boxSize-1)/2,j+(boxSize-1)/2) = s/boxSize^2; end end % psnrssim % se2 = (y - originImage).^2; % MSE2 = sum(se2(:)) / (rows * cols); PSNRs(jj) = psnr(im2uint8(originImage), im2uint8(y)); SSIMs(jj) = ssim(im2uint8(originImage), im2uint8(y)); imshow(cat(2,im2uint8(originImage),im2uint8(imageWithNoise),im2uint8(y))); title(['sigma=',num2str(sigma),' ',filePaths(jj).name,' psnr=',num2str(PSNRs(jj),'%2.2f'),'dB',' ssim=',num2str(SSIMs(jj),'%2.4f')]) drawnow; pause(pauseTime) % fprintf('˲MSE=%f\n', MSE2); end disp(["sigma:",sigma,"psnr:",mean(PSNRs),"ssim:", mean(SSIMs)]); end ================================================ FILE: medianfilter/medianfilt.m ================================================ clear,clc; pauseTime = 1; data_path = "..\Set12"; ext = ["*.jpg", "*.png", "*.jpeg"]; filePaths = []; for i = 1 : length(ext) filePaths = cat(1,filePaths, dir(fullfile(data_path,ext(i)))); end noise_leval = [10,15,20,25,30,35,40,45,50,55,60,65,70]; for ii = 1:length(noise_leval) PSNRs = zeros(1, length(filePaths)); SSIMs = zeros(1, length(filePaths)); sigma = noise_leval(ii); for jj = 1:length(filePaths) % ԭͼ originImage = im2double(imread(filePaths(jj).name)); % Ӹ˹ imageWithNoise = single(originImage + sigma/255*randn(size(originImage))); [rows, cols] = size(originImage); y = imageWithNoise; % ֵ˲㷨 % ָģߴ boxSize = 3; template = zeros(boxSize); for i = 1:rows-boxSize+1 for j = 1:cols-boxSize+1 % ȡģ template = imageWithNoise(i:i+(boxSize-1),j:j+(boxSize-1)); % ֵ滻ģĵֵ m = median(template(:)); y(i+(boxSize-1)/2,j+(boxSize-1)/2) = m; end end % psnrssim PSNRs(jj) = psnr(im2uint8(originImage), im2uint8(y)); SSIMs(jj) = ssim(im2uint8(originImage), im2uint8(y)); imshow(cat(2,im2uint8(originImage),im2uint8(imageWithNoise),im2uint8(y))); title(['sigma=',num2str(sigma),' ',filePaths(jj).name,' psnr=',num2str(PSNRs(jj),'%2.2f'),'dB',' ssim=',num2str(SSIMs(jj),'%2.4f')]) drawnow; pause(pauseTime) end disp(["sigma:",sigma,"psnr:",mean(PSNRs),"ssim:", mean(SSIMs)]); end ================================================ FILE: nlm-image-denoising/NLmeansfilt.m ================================================ clear,clc; pauseTime = 1; data_path = "..\Set12"; ext = ["*.jpg", "*.png", "*.jpeg"]; filePaths = []; for i = 1 : length(ext) filePaths = cat(1,filePaths, dir(fullfile(data_path,ext(i)))); end noise_leval = [10,15,20,25,30,35,40,45,50,55,60,65,70]; for ii = 1:length(noise_leval) PSNRs = zeros(1, length(filePaths)); SSIMs = zeros(1, length(filePaths)); sigma = noise_leval(ii); for jj = 1:length(filePaths) disp(['ڴͼƬ', filePaths(jj).name]); % ԭͼ originImage = im2double(imread(filePaths(jj).name)); % Ӹ˹ imageWithNoise = single(originImage + sigma/255*randn(size(originImage))); % imageWithNoise = imnoise(originImage,'gaussian',0,(sigma/255)^2); % NL-Means˲ % y = NLmeans(imageWithNoise,2,7,sigma/100); y = imnlmfilt(imageWithNoise); % psnrssim PSNRs(jj) = psnr(im2uint8(originImage), im2uint8(y)); SSIMs(jj) = ssim(im2uint8(originImage), im2uint8(y)); imshow(cat(2,im2uint8(originImage),im2uint8(imageWithNoise),im2uint8(y))); title(['sigma=',num2str(sigma),' ',filePaths(jj).name,' psnr=',num2str(PSNRs(jj),'%2.2f'),'dB',' ssim=',num2str(SSIMs(jj),'%2.4f')]) drawnow; pause(pauseTime) end disp(["sigma:",sigma,"psnr:",mean(PSNRs),"ssim:", mean(SSIMs)]); end