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
- Load CFD data and set parameters
- Add Noise
- DivFree Wavelet with SureShrink and MAD sigma estimation
- DivFree Wavelet with SureShrink, MAD and random cycle spinning
- Finite Difference Method
- DivFree Radial Basis Function
- Plot all
- Visualize DivFree Wavelet Coefficients
- Plot Velocity profiles
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);
