1 function [ISNR, y_hat_RWI] = BM3DDEB(experiment_number, test_image_name)
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4 % Copyright © 2008 Tampere University of Technology. All rights reserved.
5 % This work should only be used for nonprofit purposes.
8 % Kostadin Dabov, email: kostadin.dabov _at_ tut.fi
10 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12 % This function implements the image deblurring method proposed in:
14 % [1] K. Dabov, A. Foi, V. Katkovnik, and K. Egiazarian, "Image
15 % restoration by sparse 3D transform-domain collaborative filtering,"
16 % Proc SPIE Electronic Imaging, January 2008.
20 % [PSNR, y_hat_RWI] = BM3DDEB(experiment_number, test_image_name)
23 % 1) experiment_number: 1 -> PSF 1, sigma^2 = 2
24 % 2 -> PSF 1, sigma^2 = 8
25 % 3 -> PSF 2, sigma^2 = 0.308
26 % 4 -> PSF 3, sigma^2 = 49
27 % 5 -> PSF 4, sigma^2 = 4
28 % 6 -> PSF 5, sigma^2 = 64
30 % 2) test_image_name: a valid filename of a grayscale test image
33 % 1) ISNR: the output improvement in SNR, dB
34 % 2) y_hat_RWI: the restored image
36 % ! The function can work without any of the input arguments,
37 % in which case, the internal default ones are used !
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41 %%%% Fixed regularization parameters (obtained empirically after a rough optimization)
42 Regularization_alpha_RI = 4e-4;
43 Regularization_alpha_RWI = 5e-3;
45 %%%% Experiment number (see below for details, e.g. how the blur is generated, etc.)
46 if (exist('experiment_number') ~= 1)
47 experiment_number = 3; % 1 -- 6
50 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
51 %%%% Select a single image filename (might contain path)
53 if (exist('test_image_name') ~= 1)
62 %%%% Select 2D transforms ('dct', 'dst', 'hadamard', or anything that is listed by 'help wfilters'):
63 transform_2D_HT_name = 'dst'; %% 2D transform (of size N1 x N1) used in Step 1
64 transform_2D_Wiener_name = 'dct'; %% 2D transform (of size N1_wiener x N1_wiener) used in Step 2
65 transform_3rd_dimage_name = 'haar'; %% 1D tranform used in the 3-rd dim, the same for both steps
67 %%%% Step 1 (BM3D with collaborative hard-thresholding) parameters:
68 N1 = 8; %% N1 x N1 is the block size
69 Nstep = 3; %% sliding step to process every next refernece block
70 N2 = 16; %% maximum number of similar blocks (maximum size of the 3rd dimensiona of a 3D array)
71 Ns = 39; %% length of the side of the search neighborhood for full-search block-matching (BM)
72 tau_match = 6000;%% threshold for the block distance (d-distance)
73 lambda_thr2D = 0; %% threshold for the coarse initial denoising used in the d-distance measure
74 lambda_thr3D = 2.9; %% threshold for the hard-thresholding
75 beta = 0; %% the beta parameter of the 2D Kaiser window used in the reconstruction
77 %%%% Step 2 (BM3D with collaborative Wiener filtering) parameters:
82 tau_match_wiener = 800;
85 %%%% Specify whether to print results and display images
89 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 %%%% Note: touch below this point only if you know what you are doing!
92 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
95 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
96 %%%% Make parameters compatible with the interface of the mex-functions
99 [Tfor, Tinv] = getTransfMatrix(N1, transform_2D_HT_name, 0); %% get (normalized) forward and inverse transform matrices
100 [TforW, TinvW] = getTransfMatrix(N1_wiener, transform_2D_Wiener_name, 0); %% get (normalized) forward and inverse transform matrices
102 if (strcmp(transform_3rd_dimage_name, 'haar') == 1),
103 %%% Fast internal transform is used, no need to generate transform
105 hadper_trans_single_den = {};
106 inverse_hadper_trans_single_den = {};
108 %%% Create transform matrices. The transforms are later applied by
109 %%% vector-matrix multiplications
110 for hpow = 0:ceil(log2(max(N2,N2_wiener))),
112 [Tfor3rd, Tinv3rd] = getTransfMatrix(h, transform_3rd_dimage_name, 0);
113 hadper_trans_single_den{h} = single(Tfor3rd);
114 inverse_hadper_trans_single_den{h} = single(Tinv3rd');
118 if beta == 0 & beta_wiener == 0
119 Wwin2D = ones(N1_wiener,N1_wiener);
120 Wwin2D_wiener = ones(N1,N1);
122 Wwin2D = kaiser(N1, beta) * kaiser(N1, beta)'; % Kaiser window used in the hard-thresholding part
123 Wwin2D_wiener = kaiser(N1_wiener, beta_wiener) * kaiser(N1_wiener, beta_wiener)'; % Kaiser window used in the Wiener filtering part
126 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127 %%%% Read an image and generate a blurred and noisy image
129 y = im2double(imread(test_image_name));
131 if experiment_number==1
133 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(:));
135 if experiment_number==2
137 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(:));
139 if experiment_number==3
140 BSNR=40; sigma=-1; % if "sigma=-1", then the value of sigma depends on the BSNR
141 v=ones(9); v=v./sum(v(:));
143 if experiment_number==4
145 v=[1 4 6 4 1]'*[1 4 6 4 1]; v=v./sum(v(:)); % PSF
147 if experiment_number==5
149 v=fspecial('gaussian', 25, 1.6);
151 if experiment_number==6
153 v=fspecial('gaussian', 25, .4);
159 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
160 V = fft2(big_v); % frequency response of the PSF
161 y_blur = imfilter(y, v, 'circular'); % performs blurring (by circular convolution)
163 randn('seed',0); %%% fix seed for the random number generator
164 if sigma == -1; %% check whether to use BSNR in order to define value of sigma
165 sigma=sqrt(norm(y_blur(:)-mean(y_blur(:)),2)^2 /(Xh*Xv*10^(BSNR/10))); % compute sigma from the desired BSNR
168 %%%% Create a blurred and noisy observation
169 z = y_blur + sigma*randn(Xv,Xh);
173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
174 %%%% Step 1: Final estimate by Regularized Inversion (RI) followed by
175 %%%% BM3D with collaborative hard-thresholding
178 %%%% Step 1.1. Regularized Inversion
179 RI= conj(V)./( (abs(V).^2) + Regularization_alpha_RI * Xv*Xh*sigma^2); % Transfer Matrix for RI %% Standard Tikhonov Regularization
180 zRI=real(ifft2( fft2(z).* RI )); % Regularized Inverse Estimate (RI OBSERVATION)
182 stdRI = zeros(N1, N1);
185 UnitMatrix = zeros(N1,N1); UnitMatrix(ii,jj)=1;
186 BasisElementPadded = zeros(Xv, Xh); BasisElementPadded(1:N1,1:N1) = Tinv*UnitMatrix*Tinv';
187 TransfBasisElementPadded = fft2(BasisElementPadded);
188 stdRI(ii,jj) = sqrt( (1/(Xv*Xh)) * sum(sum(abs(TransfBasisElementPadded.*RI).^2)) )*sigma;
192 %%%% Step 1.2. Colored noise suppression by BM3D with collaborative hard-
195 y_hat_RI = bm3d_thr_colored_noise(zRI, hadper_trans_single_den, Nstep, N1, N2, lambda_thr2D,...
196 lambda_thr3D, tau_match*N1*N1/(255*255), (Ns-1)/2, sigma, 0, single(Tfor), single(Tinv)',...
197 inverse_hadper_trans_single_den, single(stdRI'), Wwin2D, 0, 1 );
199 PSNR_INITIAL_ESTIMATE = 10*log10(1/mean((y(:)-y_hat_RI(:)).^2));
200 ISNR_INITIAL_ESTIMATE = PSNR_INITIAL_ESTIMATE - 10*log10(1/mean((y(:)-z(:)).^2));
202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
203 %%%% Step 2: Final estimate by Regularized Wiener Inversion (RWI) followed
204 %%%% by BM3D with collaborative Wiener filtering
207 %%%% Step 2.1. Regularized Wiener Inversion
208 Wiener_Pilot = abs(fft2(double(y_hat_RI))); %%% Wiener reference estimate
209 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')
210 zRWI = real(ifft2(fft2(z).*RWI)); % RWI OBSERVATION
212 stdRWI = zeros(N1_wiener, N1_wiener);
215 UnitMatrix = zeros(N1,N1); UnitMatrix(ii,jj)=1;
216 BasisElementPadded = zeros(Xv, Xh); BasisElementPadded(1:N1,1:N1) = idct2(UnitMatrix);
217 TransfBasisElementPadded = fft2(BasisElementPadded);
218 stdRWI(ii,jj) = sqrt( (1/(Xv*Xh)) * sum(sum(abs(TransfBasisElementPadded.*RWI).^2)) )*sigma;
222 %%%% Step 2.2. Colored noise suppression by BM3D with collaborative Wiener
224 y_hat_RWI = bm3d_wiener_colored_noise(zRWI, y_hat_RI, hadper_trans_single_den, Nstep_wiener, N1_wiener, N2_wiener, ...
225 0, tau_match_wiener*N1_wiener*N1_wiener/(255*255), (Ns_wiener-1)/2, 0, single(stdRWI'), single(TforW), single(TinvW)',...
226 inverse_hadper_trans_single_den, Wwin2D_wiener, 0, 1, single(ones(N1_wiener)) );
231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
232 %%%% Calculate the final estimate's PSNR and ISNR, print them, and show the
235 PSNR = 10*log10(1/mean((y(:)-y_hat_RWI(:)).^2));
236 ISNR = PSNR - 10*log10(1/mean((y(:)-z(:)).^2));
238 if print_to_screen == 1
239 fprintf('Image: %s, Exp %d, Time: %.1f sec, PSNR-RI: %.2f dB, PSNR-RWI: %.2f, ISNR-RWI: %.2f dB\n', ...
240 test_image_name, experiment_number, elapsed_time, PSNR_INITIAL_ESTIMATE, PSNR, ISNR);
242 figure,imshow(double(y_hat_RWI));
248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
249 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
250 % Some auxiliary functions
251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
256 function [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels)
258 % Create forward and inverse transform matrices, which allow for perfect
259 % reconstruction. The forward transform matrix is normalized so that the
260 % l2-norm of each basis element is 1.
262 % [Tforward, Tinverse] = getTransfMatrix (N, transform_type, dec_levels)
266 % N --> Size of the transform (for wavelets, must be 2^K)
268 % transform_type --> 'dct', 'dst', 'hadamard', or anything that is
269 % listed by 'help wfilters' (bi-orthogonal wavelets)
270 % 'DCrand' -- an orthonormal transform with a DC and all
271 % the other basis elements of random nature
273 % dec_levels --> If a wavelet transform is generated, this is the
274 % desired decomposition level. Must be in the
275 % range [0, log2(N)-1], where "0" implies
276 % full decomposition.
280 % Tforward --> (N x N) Forward transform matrix
282 % Tinverse --> (N x N) Inverse transform matrix
285 if exist('dec_levels') ~= 1,
291 elseif strcmp(transform_type, 'hadamard') == 1,
292 Tforward = hadamard(N);
293 elseif (N == 8) & strcmp(transform_type, 'bior1.5')==1 % hardcoded transform so that the wavelet toolbox is not needed to generate it
294 Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274;
295 0.219417649252501 0.449283757993216 0.449283757993216 0.219417649252501 -0.219417649252501 -0.449283757993216 -0.449283757993216 -0.219417649252501;
296 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846 -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284;
297 -0.083506045090284 0.083506045090284 -0.083506045090284 0.083506045090284 0.569359398342846 0.402347308162278 -0.402347308162278 -0.569359398342846;
298 0.707106781186547 -0.707106781186547 0 0 0 0 0 0;
299 0 0 0.707106781186547 -0.707106781186547 0 0 0 0;
300 0 0 0 0 0.707106781186547 -0.707106781186547 0 0;
301 0 0 0 0 0 0 0.707106781186547 -0.707106781186547];
302 elseif (N == 8) & strcmp(transform_type, 'dct')==1 % hardcoded transform so that the signal processing toolbox is not needed to generate it
303 Tforward = [ 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274 0.353553390593274;
304 0.490392640201615 0.415734806151273 0.277785116509801 0.097545161008064 -0.097545161008064 -0.277785116509801 -0.415734806151273 -0.490392640201615;
305 0.461939766255643 0.191341716182545 -0.191341716182545 -0.461939766255643 -0.461939766255643 -0.191341716182545 0.191341716182545 0.461939766255643;
306 0.415734806151273 -0.097545161008064 -0.490392640201615 -0.277785116509801 0.277785116509801 0.490392640201615 0.097545161008064 -0.415734806151273;
307 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274 0.353553390593274 -0.353553390593274 -0.353553390593274 0.353553390593274;
308 0.277785116509801 -0.490392640201615 0.097545161008064 0.415734806151273 -0.415734806151273 -0.097545161008064 0.490392640201615 -0.277785116509801;
309 0.191341716182545 -0.461939766255643 0.461939766255643 -0.191341716182545 -0.191341716182545 0.461939766255643 -0.461939766255643 0.191341716182545;
310 0.097545161008064 -0.277785116509801 0.415734806151273 -0.490392640201615 0.490392640201615 -0.415734806151273 0.277785116509801 -0.097545161008064];
311 elseif (N == 8) & strcmp(transform_type, 'dst')==1 % hardcoded transform so that the PDE toolbox is not needed to generate it
312 Tforward = [ 0.161229841765317 0.303012985114696 0.408248290463863 0.464242826880013 0.464242826880013 0.408248290463863 0.303012985114696 0.161229841765317;
313 0.303012985114696 0.464242826880013 0.408248290463863 0.161229841765317 -0.161229841765317 -0.408248290463863 -0.464242826880013 -0.303012985114696;
314 0.408248290463863 0.408248290463863 0 -0.408248290463863 -0.408248290463863 0 0.408248290463863 0.408248290463863;
315 0.464242826880013 0.161229841765317 -0.408248290463863 -0.303012985114696 0.303012985114696 0.408248290463863 -0.161229841765317 -0.464242826880013;
316 0.464242826880013 -0.161229841765317 -0.408248290463863 0.303012985114696 0.303012985114696 -0.408248290463863 -0.161229841765317 0.464242826880013;
317 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863 0 0.408248290463863 -0.408248290463863;
318 0.303012985114696 -0.464242826880013 0.408248290463863 -0.161229841765317 -0.161229841765317 0.408248290463863 -0.464242826880013 0.303012985114696;
319 0.161229841765317 -0.303012985114696 0.408248290463863 -0.464242826880013 0.464242826880013 -0.408248290463863 0.303012985114696 -0.161229841765317];
320 elseif strcmp(transform_type, 'dct') == 1,
321 Tforward = dct(eye(N));
322 elseif strcmp(transform_type, 'dst') == 1,
323 Tforward = dst(eye(N));
324 elseif strcmp(transform_type, 'DCrand') == 1,
325 x = randn(N); x(1:end,1) = 1; [Q,R] = qr(x);
330 else %% a wavelet decomposition supported by 'wavedec'
331 %%% Set periodic boundary conditions, to preserve bi-orthogonality
332 dwtmode('per','nodisp');
334 Tforward = zeros(N,N);
336 Tforward(:,i)=wavedec(circshift([1 zeros(1,N-1)],[dec_levels i-1]), log2(N), transform_type); %% construct transform matrix
340 %%% Normalize the basis elements
341 Tforward = (Tforward' * diag(sqrt(1./sum(Tforward.^2,2))))';
343 %%% Compute the inverse transform matrix
344 Tinverse = inv(Tforward);