Denoise CFD Data

The following compares divergence-free wavelet denoising with existing methods on a flow data generated by OpenFOAM

Contents

Clear all and check path

close all
clc
clear

if ~exist('dfwavelet_thresh','file')
    error('Cannot find dfwavelet functions. run setPath!');
end

Load CFD data and set parameters

load pipe

% Set parameters
vMag = sqrt(vx.^2+vy.^2+vz.^2); % Velocity magnitude
vMax = max(vMag(:));            % Maximum speed
FOV = size(vMag);               % Field of view
N = FOV(3);                     % Number of slices (for plotting)
res = [1,1,1];                  % Relative resolution

ph0 = zeros(FOV);               % Reference phase in phase contrast
imMask = imMag;                 % Segmentation mask


% Plot
figure,imshow_flow(imMag,vx,vy,vz,vMax,[1,2,3]),title('Original Flow Field','FontSize',14);
figure,imshow_flowmag(imMag,vx,vy,vz,vMax,[1,2,3]),title('Original Velocity Magnitude','FontSize',14);
pause(1);

Add Noise

sigma = 0.2*max(imMag(:));


% Transform velocity to 5-point method phase encodes
gamma = 26753;
M1 = pi/(2*vMax*gamma);
[s1,s2,s3,s4,s5] = fivePointPC(vx,vy,vz,imMag,ph0,M1);


% Add noise
s1 = s1+sigma/sqrt(2)*(randn(FOV)+1i*randn(FOV));
s2 = s2+sigma/sqrt(2)*(randn(FOV)+1i*randn(FOV));
s3 = s3+sigma/sqrt(2)*(randn(FOV)+1i*randn(FOV));
s4 = s4+sigma/sqrt(2)*(randn(FOV)+1i*randn(FOV));
s5 = s5+sigma/sqrt(2)*(randn(FOV)+1i*randn(FOV));
[vxN,vyN,vzN,imMagN] = invfivePointPC(s1,s2,s3,s4,s5,M1);


% Crop velocities using image magnitude
[vxN,vyN,vzN] = maskIM(imMag,vxN,vyN,vzN);


% Plot
figure,imshow_flow(imMag,vxN,vyN,vzN,vMax,[1,2,3]),title('Noisy Flow Field','FontSize',14);
figure,imshow_flowmag(imMag,vxN,vyN,vzN,vMax,[1,2,3]),title('Noisy Velocity Magnitude','FontSize',14);


% Calculate errors
disp('Noisy Flow Error')
[vNRMSE_Noise,vMagErr_Noise,angErr_Noise] = calcVelError(imMask,vx,vy,vz,vxN,vyN,vzN);
PVNR = 20*log10(1/vNRMSE_Noise);
fprintf('PVNR: \t\t\t%.2fdB\nNRMSE: \t\t\t%f\nvMag Error: \t\t%f\nAbsolute Angle Error: \t%f\n\n',PVNR,vNRMSE_Noise,vMagErr_Noise,angErr_Noise);

pause(1);
Noisy Flow Error
PVNR: 			22.07dB
NRMSE: 			0.078780
vMag Error: 		0.049744
Absolute Angle Error: 	0.253787

DivFree Wavelet with SureShrink and MAD sigma estimation

Here, we use Median Absolute Deviation to estimate noise std and then use SureShrink to find the optimal threshold that minimizes MSE

minSize = 20*ones(1,3); % Smallest wavelet level size

% Denoise
[vxDFWsm,vyDFWsm,vzDFWsm] = dfwavelet_thresh_SURE_MAD(vxN,vyN,vzN,minSize,res);


% Plot
figure,imshow_flow(imMag,vxDFWsm,vyDFWsm,vzDFWsm,vMax,[1,2,3])
title('Div Free Wavelet w/ SureShrink (Flow Field)','FontSize',14)
figure,imshow_flowmag(imMag,vxDFWsm,vyDFWsm,vzDFWsm,vMax,[1,2,3])
title('Div Free Wavelet w/ SureShrink (Vel Mag)','FontSize',14)


% Calculate errors
disp('DivFree Wavelet w/ SureShrink and MAD')
[vNRMSE_DFWsm,vMagErr_DFWsm,angErr_DFWsm] = calcVelError(imMask,vx,vy,vz,vxDFWsm,vyDFWsm,vzDFWsm);
fprintf('NRMSE: \t\t\t%f\nvMag Error: \t\t%f\nAbsolute Angle Error: \t%f\n\n',vNRMSE_DFWsm,vMagErr_DFWsm,angErr_DFWsm);
pause(1);
DivFree Wavelet w/ SureShrink and MAD
NRMSE: 			0.021307
vMag Error: 		0.014186
Absolute Angle Error: 	0.049334

DivFree Wavelet with SureShrink, MAD and random cycle spinning

To remove the blocking artifacts, we do partial cycle spinning Here we do 2^3=8 random shifts

spins = 2;              % Number of cycle spinning per dimension
isRandShift = 1;        % Use random shift
minSize = 20*ones(1,3); % Smallest wavelet level size

% Denoise
[vxDFWsms,vyDFWsms,vzDFWsms] = dfwavelet_thresh_SURE_MAD_spin(vxN,vyN,vzN,minSize,res,spins,isRandShift);


% Plot
figure,imshow_flow(imMag,vxDFWsms,vyDFWsms,vzDFWsms,vMax,[1,2,3])
title('Div Free Wavelet w/ SureShrink and Partial Cycle Spinning (Flow Field)','FontSize',14)
figure,imshow_flowmag(imMag,vxDFWsms,vyDFWsms,vzDFWsms,vMax,[1,2,3])
title('Div Free Wavelet w/ SureShrink and Partial Cycle Spinning (Vel Mag)','FontSize',14)


% Calculate errors
disp('DivFree Wavelet w/ SureShrink, MAD and Partial Cycle Spinning')
[vNRMSE_DFWsms,vMagErr_DFWsms,angErr_DFWsms] = calcVelError(imMask,vx,vy,vz,vxDFWsms,vyDFWsms,vzDFWsms);
fprintf('NRMSE: \t\t\t%f\nvMag Error: \t\t%f\nAbsolute Angle Error: \t%f\n\n',vNRMSE_DFWsms,vMagErr_DFWsms,angErr_DFWsms);
pause(1);
DivFree Wavelet w/ SureShrink, MAD and Partial Cycle Spinning
NRMSE: 			0.014618
vMag Error: 		0.010480
Absolute Angle Error: 	0.028312

Finite Difference Method

The following implements finite difference method denoising as described in: Song SM, Pelc NJ., et al. JMRI 1993 Noise reduction in three-dimensional phase-contrast MR velocity measurementsl.

% Denoise
[vxFDM,vyFDM,vzFDM] = fdmDenoise(vxN,vyN,vzN,res);


% Plot
figure,imshow_flow(imMag,vxFDM,vyFDM,vzFDM,vMax,[1,2,3])
title('Div Free FDM','FontSize',14)
figure,imshow_flowmag(imMag,vxFDM,vyFDM,vzFDM,vMax,[1,2,3])
title('Div Free FDM','FontSize',14)


% Calculate errors
disp('Finite Difference Method Error')
[vNRMSE_FDM,vMagErr_FDM,angErr_FDM] = calcVelError(imMask,vx,vy,vz,vxFDM,vyFDM,vzFDM);
fprintf('NRMSE: \t\t\t%f\nvMag Error: \t\t%f\nAbsolute Angle Error: \t%f\n\n',vNRMSE_FDM,vMagErr_FDM,angErr_FDM);
pause(1);
Finite Difference Method Error
NRMSE: 			0.064306
vMag Error: 		0.039657
Absolute Angle Error: 	0.211950

DivFree Radial Basis Function

This can take a while to run The following implements divergence-free radial basis function denoising as described in: Busch J, Kozerke S., et al. MRM 2012 Construction of divergence-free velocity fields from cine 3D phase-contrast flow measurements.

radius = 9;     % Radius of kernel
nIter = 20;     % Number of iterations for lsqr

% Plot during iterations, if on, does gradient descent instead of lsqr
doplot = 0;


[vxRBF,vyRBF,vzRBF] = rbfDenoise(vxN,vyN,vzN,imMask,radius,res,nIter,doplot);


% Denoise
figure,imshow_flow(imMag,vxRBF,vyRBF,vzRBF,vMax,[1,2,3]),
title('Div Free RBF','FontSize',14)
figure,imshow_flowmag(imMag,vxRBF,vyRBF,vzRBF,vMax,[1,2,3]),
title('Div Free RBF','FontSize',14)


% Calculate errors
disp('Radial Basis Function Error')
[vNRMSE_RBF,vMagErr_RBF,angErr_RBF] = calcVelError(imMask,vx,vy,vz,vxRBF,vyRBF,vzRBF);
fprintf('NRMSE: \t\t\t%f\nvMag Error: \t\t%f\nAbsolute Angle Error: \t%f\n\n',vNRMSE_RBF,vMagErr_RBF,angErr_RBF);
pause(1);
RBF Denoising...
done
Radial Basis Function Error
NRMSE: 			0.012547
vMag Error: 		0.010310
Absolute Angle Error: 	0.021251

Plot all

Plotting results from all methods.

figure,imshow_flowmag(...
    cat(2,imMask,imMask,imMask,imMask,imMask,imMask),...
    cat(2,vx,vxN,vxDFWsm,vxDFWsms,vxFDM,vxRBF),...
    cat(2,vy,vyN,vyDFWsm,vyDFWsms,vyFDM,vyRBF),...
    cat(2,vz,vzN,vzDFWsm,vzDFWsms,vzFDM,vzRBF),...
    vMax,[1,2,3]);

title('Original,Noisy,DFW,DFW/spin,FDM and RBF','FontSize',14)

pause(1)

Visualize DivFree Wavelet Coefficients

% Wavelet coefficients for original
minSize = 20*ones(1,3);
[wcdf1,wcdf2,wcn,numLevels,wcSizes] = dfwavelet_forward(vx,vy,vz,minSize,res);
wcdf1_tile = wcTile(wcdf1,numLevels,wcSizes);
wcdf2_tile = wcTile(wcdf2,numLevels,wcSizes);
wcn_tile = wcTile(wcn,numLevels,wcSizes);

% Wavelet coefficients for noisy
[wcdf1N,wcdf2N,wcnN,numLevels,wcSizes] = dfwavelet_forward(vxN,vyN,vzN,minSize,res);
wcdf1N_tile = wcTile(wcdf1N,numLevels,wcSizes);
wcdf2N_tile = wcTile(wcdf2N,numLevels,wcSizes);
wcnN_tile = wcTile(wcnN,numLevels,wcSizes);


% Wavelet coefficeints for div free wavelet w/ cycle spinning
[wcdf1sms,wcdf2sms,wcnsms,numLevels,wcSizes] = dfwavelet_forward(vxDFWsms,vyDFWsms,vzDFWsms,minSize,res);
wcdf1sms_tile = wcTile(wcdf1sms,numLevels,wcSizes);
wcdf2sms_tile = wcTile(wcdf2sms,numLevels,wcSizes);
wcnsms_tile = wcTile(wcnsms,numLevels,wcSizes);


% Plot
figure,imshow3f(abs(cat(2,wcn_tile,wcnN_tile,wcnsms_tile)),[1,2,3],N/8),title('Non-divfree wavelet subbands for Original, Noisy and DFW','FontSize',15);

pause(1)

Plot Velocity profiles

figure,plot3f(cat(2,vx,vxN,vxDFWsms),[1,2,3]),
title('Velocity profiles for Original,Noisy, and DFW/spin','FontSize',14);