├── Matlab Code ├── .DS_Store ├── DVC.m ├── Example │ └── exampleRunFile.m ├── IDVC.m ├── Icon_ ├── Supplemental MFiles │ ├── Icon_ │ ├── inpaint_nans.m │ ├── inpaint_nans3.m │ ├── license.txt │ ├── mirt3D_mexinterp.cpp │ ├── mirt3D_mexinterp.m │ └── mirt3D_mexinterp.mexw64 ├── addDisplacements.m ├── checkConvergenceSSD.m ├── filterDisplacements.m ├── funIDVC.m ├── removeOutliers.m └── volumeMapping.m └── README.md /Matlab Code/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDVC/d7254cb150e497ec53d3080469517c6c97fc7793/Matlab Code/.DS_Store -------------------------------------------------------------------------------- /Matlab Code/DVC.m: -------------------------------------------------------------------------------- 1 | function [u, cc] = DVC(varargin) 2 | % [du, cc] = DVC(I,sSize,sSpacing,ccThreshold) estimates 3 | % displacements between two volumetric images through digital volume 4 | % correlation. 5 | % 6 | % INPUTS 7 | % ------------------------------------------------------------------------- 8 | % I: cell containing the undeformed, I{1}, and deformed, I{2} 3-D images 9 | % sSize: interrogation window (subset) size 10 | % sSpacing: interrogation window (subset) spacing. Determines window 11 | % overlap factor 12 | % ccThreshold: threshold value that defines a bad cross-correlation 13 | % 14 | % OUTPUTS 15 | % ------------------------------------------------------------------------- 16 | % u: cell containing the displacement field (u{1:3} = {u_x, u_y, u_z}) 17 | % cc: peak values of the cross-correlation for each interrogation 18 | % 19 | % NOTES 20 | % ------------------------------------------------------------------------- 21 | % all functions are self contained 22 | % 23 | % If used please cite: 24 | % Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast 25 | % iterative digital volume correlation algorithm for large deformations. 26 | % Experimental Mechanics. doi: 10.1007/s11340-014-9874-2 27 | 28 | % Parse inputs and create meshgrid 29 | [I,m,mSize,sSize,MTF,M,ccThreshold] = parseInputs(varargin{:}); 30 | 31 | % Initialize variables 32 | mSize_ = prod(mSize); 33 | u123 = zeros(mSize_,3); 34 | cc = zeros(mSize_,1); 35 | 36 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 37 | wb = findall(0,'Tag','TMWWaitbar'); wb = wb(1); 38 | 39 | waitbar(1/7,wb,'Estimating Displacements (Time Remaining: )'); 40 | 41 | for k = 1:mSize_ 42 | 43 | 44 | tStart = tic; % begin timer 45 | %-------------------------------------------------------------------------- 46 | % grab the moving subset from the images 47 | A = I{1}(m{1}(k,:),m{2}(k,:),m{3}(k,:)); 48 | B = I{2}(m{1}(k,:),m{2}(k,:),m{3}(k,:)); 49 | 50 | % multiply by the modular transfer function to alter frequency content 51 | A = MTF.*A; B = MTF.*B; 52 | 53 | % run cross-correlation 54 | A = xCorr3(A,B,sSize); 55 | 56 | % find maximum index of the cross-correlaiton 57 | [cc(k), maxIdx] = max(A(:)); 58 | 59 | % compute voxel resolution displacements 60 | [u1, u2, u3] = ind2sub(sSize,maxIdx); 61 | 62 | % gather the 3x3x3 voxel neighborhood around the peak 63 | try xCorrPeak = reshape(A(u1 + (-1:1), u2 + (-1:1), u3 + (-1:1)),27,1); 64 | % last squares fitting of the peak to calculate sub-voxel displacements 65 | du123 = lsqPolyFit3(xCorrPeak, M{1}, M{2}); 66 | u123(k,:) = [u1 u2 u3] + du123' - sSize/2 - 1; 67 | %-------------------------------------------------------------------------- 68 | catch 69 | u123(k,:) = nan; 70 | end 71 | % xCorrPeak = reshape(A(u1 + (-1:1), u2 + (-1:1), u3 + (-1:1)),27,1); 72 | % 73 | % % least squares fitting of the peak to calculate sub-voxel displacements 74 | % du123 = lsqPolyFit3(xCorrPeak, M{1}, M{2}); 75 | % u123(k,:) = [u1 u2 u3] + du123' - (sSize/2) - 1; 76 | %-------------------------------------------------------------------------- 77 | 78 | % waitbar calculations (update only every 100 iterations) 79 | if rem(k,100) == 0 80 | tRemaining = (toc(tStart)*(mSize_ - k)); % Time remaining for waitbar 81 | waitbar(1/7*(k/mSize_ + 1),wb,['Estimating Displacements (Time Remaining: ', datestr(datenum(0,0,0,0,0,tRemaining),'MM:SS'),')']) 82 | end 83 | 84 | end 85 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 86 | % Reshape displacements and set bad correlations to zero 87 | waitbar(2/7,wb,'Removing Bad Correlations') 88 | 89 | cc = reshape(double(cc),mSize); 90 | % cc = permute(cc,[2 1 3]); 91 | [cc, ccMask] = removeBadCorrelations(I,cc,ccThreshold); 92 | 93 | % u = cell(1,3); 94 | % for i = 1:3 95 | % u{i} = reshape(double(u123(:,i)),mSize).*ccMask; 96 | % u{i} = permute(u{i},[2 1 3]); 97 | % end 98 | u{1} = reshape(double(u123(:,2)),mSize).*ccMask; 99 | u{2} = reshape(double(u123(:,1)),mSize).*ccMask; 100 | u{3} = reshape(double(u123(:,3)),mSize).*ccMask; 101 | 102 | end 103 | 104 | %% ======================================================================== 105 | function varargout = parseInputs(varargin) 106 | % Parse inputs and create meshgrid 107 | 108 | I{1} = varargin{1}{1}; 109 | I{2} = varargin{1}{2}; 110 | sSize = varargin{2}; 111 | sSpacing = varargin{3}; 112 | padSize = varargin{4}; 113 | ccThreshold = varargin{5}; 114 | 115 | % pad images with zeros so that we don't grab any subset outside of the image 116 | % domain. This would produce an error 117 | I{1} = padarray(I{1},padSize,0,'both'); 118 | I{2} = padarray(I{2},padSize,0,'both'); 119 | sizeV = size(I{1}); 120 | 121 | % Initialize Mesh Variables 122 | idx = cell(1,3); 123 | for i = 1:3, idx{i} = (1+padSize(i)) : sSpacing(i) : (sizeV(i)-sSize(i)-padSize(i)+1); end 124 | [m{1},m{2},m{3}] = ndgrid(idx{:}); 125 | 126 | 127 | % sSize = [sSize(2) sSize(1) sSize(3)]; 128 | mSize = size(m{1}); 129 | mSize_ = prod(mSize); 130 | 131 | m_ = cell(1,3); 132 | for k = 1:3, 133 | m_{k} = zeros([mSize_,sSize(k)],'uint16'); 134 | repmat_ = repmat((1:sSize(k))-1,mSize_,1); 135 | m_{k} = bsxfun(@plus, repmat_,m{k}(:)); 136 | end 137 | 138 | % Initialize quadratic least squares fitting coefficients 139 | [mx, my, mz] = meshgrid((-1:1),(-1:1),(-1:1)); 140 | m = [mx(:), my(:), mz(:)]; 141 | 142 | M{1} = zeros(size(m,1),10); 143 | for i = 1:size(m,1) 144 | x = m(i,1); y = m(i,2); z = m(i,3); 145 | M{1}(i,:) = [1,x,y,z,x^2,x*y,x*z,y^2,y*z,z^2]; 146 | end 147 | 148 | M{2} = M{1}'*M{1}; 149 | 150 | % Generate Moduluar transfer function (see eq. 3) 151 | [~,~,MTF] = generateMTF(sSize); 152 | 153 | %% Parse outputs 154 | 155 | varargout{ 1} = I; 156 | varargout{end+1} = m_; 157 | varargout{end+1} = mSize; 158 | varargout{end+1} = sSize; 159 | varargout{end+1} = MTF; 160 | varargout{end+1} = M; 161 | varargout{end+1} = ccThreshold; 162 | 163 | end 164 | 165 | %% ======================================================================== 166 | function A = xCorr3(A,B,sSize) 167 | % performs fft based cross correlation of A and B (see equation 2) 168 | 169 | A = fftn(A,sSize); 170 | B = fftn(B,sSize); 171 | B = conj(B); 172 | A = A.*B; 173 | A = ifftn(A); 174 | A = real(A); 175 | A = fftshift(A); 176 | end 177 | 178 | %% ======================================================================== 179 | function duvw = lsqPolyFit3(b, M, trMM) 180 | % LeastSqPoly performs a 3D polynomial fit in the least squares sense 181 | % Solves M*x = b, 182 | % trMM = transpose(M)*M 183 | % trMb = tranpose(M)*b 184 | % 185 | % If you need to generate the coefficients then uncomment the following 186 | % [mx, my, mz] = meshgrid(-1:1,-1:1,-1:1); 187 | % m = [mx(:), my(:), mz(:)]; 188 | % 189 | % for i = 1:size(m,1) 190 | % x = m(i,1); y = m(i,2); z = m(i,3); 191 | % M1(i,:) = [1,x,y,z,x^2,x*y,x*z,y^2,y*z,z^2]; 192 | % end 193 | % 194 | % trMM1 = M'*M; 195 | 196 | % b = log(b); 197 | trMb = sum(bsxfun(@times, M, b)); 198 | 199 | x = trMM\trMb'; %solve for unknown coefficients 200 | 201 | A = [x(6), 2*x(5), x(7); 202 | 2*x(8), x(6) x(9) 203 | x(9), x(7), 2*x(10)]; 204 | 205 | duvw = (A\(-x([2 3 4]))); 206 | end 207 | 208 | %% ======================================================================== 209 | function varargout = generateMTF(sSize) 210 | % MTF functions taken from 211 | % J. Nogueira, A Lecuona, P. A. Rodriguez, J. A. Alfaro, and A. Acosta. 212 | % Limits on the resolution of correlation PIV iterative methods. Practical 213 | % implementation and design of weighting functions. Exp. Fluids, 214 | % 39(2):314{321, July 2005. doi: 10.1007/s00348-005-1017-1 215 | 216 | %% equation 4 217 | 218 | if prod(single(sSize == 32)) || prod(single(sSize == 16)) 219 | sSize = sSize(1); 220 | 221 | x = cell(1,3); 222 | [x{1}, x{2}, x{3}] = meshgrid(1:sSize,1:sSize,1:sSize); 223 | 224 | nu{1} = 1; 225 | for i = 1:3 226 | x{i} = x{i} - sSize/2 - 0.5; 227 | x{i} = abs(x{i}/sSize); 228 | nu{1} = nu{1}.*(3*(4*x{i}.^2-4*x{i}+1)); 229 | end 230 | 231 | %% equation 5 232 | [x{1}, x{2}, x{3}] = meshgrid(1:sSize,1:sSize,1:sSize); 233 | 234 | for i = 1:3, x{i} = x{i} - sSize/2 - 0.5; end 235 | 236 | r = abs(sqrt(x{1}.^2 + x{2}.^2 + x{3}.^2)/sSize); 237 | nu{2} = zeros(size(r)); 238 | nu{2}(r < 0.5) = 24/pi*(4*r(r < 0.5).^2-4*r(r < 0.5)+1); 239 | 240 | %% equation 6 241 | [x{1}, x{2}, x{3}] = meshgrid(1:sSize,1:sSize,1:sSize); 242 | 243 | nu{3} = 1; 244 | for i = 1:3, 245 | x{i} = x{i} - sSize/2 - 0.5; 246 | x{i} = (x{i}/sSize); 247 | 248 | nu{3} = nu{3}.*(12*abs(x{i}).^2 - 12*abs(x{i}) + 3 + ... 249 | 0.15*cos(4*pi*x{i}) + 0.20*cos(6*pi*x{i}) + ... 250 | 0.10*cos(8*pi*x{i}) + 0.05*cos(10*pi*x{i})); 251 | 252 | end 253 | nu{3}(nu{3} < 0) = 0; 254 | 255 | else 256 | 257 | nu{1} = ones(sSize(1),sSize(2),sSize(3)); 258 | nu{2} = nu{1}; 259 | nu{3} = nu{1}; 260 | 261 | end 262 | 263 | nu = cellfun(@(x) x/sum(x(:)), nu, 'UniformOutput',0); 264 | nu = cellfun(@sqrt, nu, 'UniformOutput',0); 265 | 266 | varargout = nu; 267 | 268 | end 269 | 270 | %% ======================================================================== 271 | function [cc, ccMask] = removeBadCorrelations(I,cc,ccThreshold) 272 | % removes bad correlations. You can insert your own method here. 273 | minOS = 1; 274 | for i = 1:2 275 | zeroIdx = I{i} == 0; 276 | threshold = mean2(I{i}(~zeroIdx)); 277 | I_ = I{i}.*(I{i} < threshold); 278 | minOS = minOS*sum(I_(:))/sum(~zeroIdx(:)); 279 | end 280 | cc = cc - minOS; 281 | cc = cc/(max(I{1}(:))*max(I{2}(:))); 282 | ccMask = double(cc >= ccThreshold); 283 | 284 | CC = bwconncomp(~ccMask); 285 | [~,idx] = max(cellfun(@numel,CC.PixelIdxList)); 286 | if ~isempty(idx) 287 | ccMask(CC.PixelIdxList{idx}) = inf; 288 | end 289 | ccMask(cc == 0) = nan; 290 | ccMask(~isfinite(ccMask)) = 0; 291 | cc = cc.*ccMask; 292 | 293 | end 294 | -------------------------------------------------------------------------------- /Matlab Code/Example/exampleRunFile.m: -------------------------------------------------------------------------------- 1 | %% Example run file for the IDVC - LDTFM 2 | % The two images that are going to be run are 'vol00.mat' and 'vol01.mat.' 3 | % the deformation are defined as a four-pole Gaussian prescribed 4 | % displacement field. See below for distriub 5 | % 6 | % Central z (x_3) plane. Number denotes width of gaussian in voxels. See 7 | % section 3.1 and 3.2 in Bar-Kochba et al. (2014) 8 | % -------------------------- 9 | % | | 10 | % | 32 64 | 11 | % | | 12 | % | | 13 | % | | 14 | % | 96 128 | 15 | % | | 16 | % | | 17 | % -------------------------- 18 | % 19 | % VARIABLES OPTIONS 20 | % ------------------------------------------------------------------------- 21 | % filename: string for the filename prefix for the volumetric images in 22 | % the current directory. 23 | % Input options: 24 | % --- If image is not within a cell) --- 25 | % 1) 'filename*.mat' or 'filename*' 26 | % 27 | % --- If image is within a cell that contains multichannels --- 28 | % 2) filename{1} = 'filename*.mat' or 'filename*' and 29 | % filename{2} = channel number containing images you want to 30 | % run IDVC on. 31 | % (if the channel is not provided, i.e. length(filename) = 1 32 | % , then channel = 1 33 | % 34 | % sSize: interrogation window (subset) size for the first iterations. 35 | % Must be 32,64,96, or 128 voxels and a three column 36 | % array (one for each dimenision) or scalar (equal for all 37 | % dimensions). 38 | % incORcum: string that defines the method of running IDVC. Options: 39 | % cumulative (time0 -> time1, time0 -> time2, ...) 40 | % (Allowable inputs: 'c','cum','cumulative') 41 | % or 42 | % incremental (time0 -> time1, time1 -> time2, ...) 43 | % (Allowable inputs: 'i','inc','incremental') 44 | % 45 | % OUTPUTS 46 | % ------------------------------------------------------------------------- 47 | % u: displacement field vector calculated from FIDVC. Format: cell array, 48 | % which is a 3D vector (components in x,y,z) per each time point 49 | % (units are in voxels) 50 | % u{time}{1} = displacement in x-direction at t=time of size MxNxP 51 | % u{time}{2} = displacement in y-direction at t=time of size MxNxP 52 | % u{time}{3} = displacement in z-direction at t=time of size MxNxP 53 | % cc: peak values of the cross-correlation for each interrogation 54 | % dm: meshgrid spacing (8 by default) 55 | % m: The grid points at which displacements are computed. The grid 56 | % points locations are in the same format as 'u'. 57 | % 58 | % NOTES 59 | % ------------------------------------------------------------------------- 60 | % To run you need a compatible C compiler. Please see 61 | % (http://www.mathworks.com/support/compilers/R2014a/index.html) 62 | % 63 | % If used please cite: 64 | % Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast 65 | % iterative digital volume correlation algorithm for large deformations. 66 | % Experimental Mechanics. doi: 10.1007/s11340-014-9874-2 67 | 68 | clear; close all; 69 | 70 | sSize = [128 128 64]; 71 | incORcum = 'incremental'; 72 | filename = 'vol*.mat'; 73 | 74 | % Estimate displacements via IDVC 75 | [u, cc, dm, m] = funIDVC(filename, sSize, incORcum); 76 | 77 | save('resultsFIDVC.mat','u','cc','dm'); 78 | 79 | 80 | %% PLOTTING 81 | load('vol01.mat'); 82 | sizeI = size(vol{1}); 83 | plotIdx = cell(1,3); 84 | for i = 1:3, plotIdx{i} = 1:dm:sizeI(i)+1; end 85 | 86 | close all; 87 | % plot IDVC results 88 | figure; 89 | for i = 1:3 90 | subplot(2,3,i); 91 | [~,h] = contourf(plotIdx{1}, plotIdx{2}, u{1}{i}(:,:,ceil(size(u{1}{i},3)/2)),25); colorbar 92 | set(h,'linestyle','none'); axis image 93 | title(['displacement component u_',num2str(i)]) 94 | xlabel('X_1'); ylabel('X_2'); 95 | 96 | subplot(2,3,i+3); 97 | [~,h] = contourf(plotIdx{1}, plotIdx{3}, squeeze(u{1}{i}(:,48,:))',25); colorbar 98 | set(h,'linestyle','none'); axis image 99 | xlabel('X_2'); ylabel('X_3'); 100 | 101 | end 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /Matlab Code/IDVC.m: -------------------------------------------------------------------------------- 1 | function [u, cc, dm, m] = IDVC(varargin) 2 | % [u, cc] = IDVC(I,sSize,u0,className); 3 | % I = filterDisplacements(I0,filterSize,z) applies a low-pass convolution 4 | % filter to the displacement field to mitigate divergence based on 5 | % 6 | % F. F. J. Schrijer and F. Scarano. Effect of predictor corrector filtering 7 | % on the stability and spatial resolution of iterative PIV interrogation. 8 | % Exp. Fluids, 45(5):927{941, May 2008. doi: 10.1007/s00348-008-0511-7 9 | % 10 | % INPUTS 11 | % ------------------------------------------------------------------------- 12 | % I0: cell containing the undeformed, I0{1}, and deformed, I0{2} 3-D 13 | % images 14 | % sSize: interrogation window (subset) size 15 | % u0: pre-estimated displacement field (typically zeros) 16 | % 17 | % OUTPUTS 18 | % ------------------------------------------------------------------------- 19 | % u: displacement field vector defined at every meshgrid point with 20 | % spacing dm. Format: cell array, each containing a 3D matrix 21 | % (components in x,y,z) 22 | % u{1} = displacement in x-direction 23 | % u{2} = displacement in y-direction 24 | % u{3} = displacement in z-direction 25 | % u{4} = magnitude 26 | % cc: peak values of the cross-correlation for each interrogation 27 | % dm: meshgrid spacing (8 by default) 28 | % m: The grid points at which displacements are computed. The grid 29 | % points locations are in the same format as 'u'. 30 | % 31 | % NOTES 32 | % ------------------------------------------------------------------------- 33 | % To run you need a compatible C compiler. Please see 34 | % (http://www.mathworks.com/support/compilers/R2014a/index.html) 35 | % 36 | % If used please cite: 37 | % Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast 38 | % iterative digital volume correlation algorithm for large deformations. 39 | % Experimental Mechanics. doi: 10.1007/s11340-014-9874-2 40 | 41 | 42 | wb = waitbar(0,'Parsing Inputs','Name','Running IDVC'); 43 | 44 | % PRESET CONSTANTS 45 | maxIterations = 20; % maximum number of iterations 46 | dm = 8; % desired output mesh spacing 47 | convergenceCrit = [0.25, 0.5, 0.0625]; % convergence criteria 48 | ccThreshold = 1e-4; % bad cross-correlation threshold 49 | 50 | [I0, sSize, sSpacing, padSize, DVCPadSize, u] = parseInputs(varargin{:}); 51 | 52 | % START ITERATING 53 | i = 2; converged01 = 0; SSE = []; I = I0; 54 | 55 | t0 = tic; 56 | while ~converged01 && i - 1 < maxIterations 57 | ti = tic; 58 | 59 | set(wb,'name',['Running IDVC (Iteration ',num2str(i-1),')']); 60 | waitbar(0/7,wb,'Checking Convergence'); 61 | % Check for convergence 62 | [converged01, SSE(i-1) , sSize(i,:), sSpacing(i,:)] = checkConvergenceSSD(I,SSE,sSize,sSpacing,convergenceCrit); 63 | 64 | if ~converged01 65 | finalSize = sSize(i,:); 66 | 67 | [I, m] = parseImages(I,sSize(i,:),sSpacing(i,:)); 68 | 69 | % run cross-correlation to get an estimate of the displacements 70 | [du, cc] = DVC(I,sSize(i,:),sSpacing(i,:),DVCPadSize,ccThreshold); 71 | 72 | % add the displacements from previous iteration to current 73 | waitbar(3/7,wb,'Adding displacements from previous iteration'); 74 | [u, ~, cc, mFinal] = addDisplacements(u,du,cc,m,dm); 75 | 76 | % filter the displacements using a predictor filter 77 | waitbar(4/7,wb,'Filtering Displacements'); 78 | u = filterDisplacements(u,sSize(i,:)/dm); 79 | 80 | % remove outliers in displacement field 81 | waitbar(5/7,wb,'Removing Outliers'); 82 | u = removeOutliers(u); 83 | 84 | % mesh and pad images based on new subset size and spacing 85 | [I, m] = parseImages(I0,sSize(i,:),sSpacing(i,:)); 86 | 87 | % map volumes based on displacment field 88 | waitbar(6/7,wb,'Warping Images'); 89 | I = volumeMapping(I,m,u); 90 | 91 | disp(['Elapsed time (iteration ',num2str(i-1),'): ',num2str(toc(ti))]); 92 | i = i + 1; 93 | end 94 | 95 | end 96 | 97 | [u,cc,m] = parseOutputs(u,cc,finalSize,padSize,mFinal); 98 | delete(wb); 99 | disp(['Convergence at iteration ',num2str(i)]); 100 | disp(['Title time: ',num2str(toc(t0))]); 101 | end 102 | 103 | 104 | 105 | %% ======================================================================== 106 | function varargout = parseImages(varargin) 107 | % pads images and creates meshgrid 108 | 109 | I{1} = single(varargin{1}{1}); 110 | I{2} = single(varargin{1}{2}); 111 | sSize = varargin{2}; 112 | sSpacing = varargin{3}; 113 | 114 | 115 | prePad = sSize/2; 116 | postPad = sSize/2; 117 | 118 | sizeI = size(I{1}); 119 | I{1} = padarray(I{1},prePad,0,'pre'); 120 | I{1} = padarray(I{1},postPad,0,'post'); 121 | 122 | I{2} = padarray(I{2},prePad,0,'pre'); 123 | I{2} = padarray(I{2},postPad,0,'post'); 124 | 125 | 126 | idx = cell(1,3); 127 | for i = 1:3, idx{i} = (1:sSpacing(i):(sizeI(i) + 1)) + sSize(i)/2; end 128 | 129 | % [m{1},m{2},m{3}] = meshgrid(idx{:}); 130 | 131 | varargout{ 1} = I; 132 | varargout{end+1} = idx; 133 | 134 | end 135 | 136 | %% ======================================================================== 137 | function varargout = parseInputs(varargin) 138 | % parses inputs and pads images so that there is an divisable meshgrid number. 139 | 140 | I0{1} = single(varargin{1}{1}); 141 | I0{2} = single(varargin{1}{2}); 142 | 143 | % I0{1} = permute(I0{1},[2 1 3]); 144 | % I0{2} = permute(I0{2},[2 1 3]); 145 | 146 | sSize = varargin{2}; 147 | sSize = [sSize(2), sSize(1), sSize(3)]; 148 | 149 | sSpacing = sSize/2; 150 | u0 = varargin{3}; 151 | 152 | DVCPadSize = sSpacing/2; 153 | 154 | sizeI0 = size(I0{1}); 155 | sizeI = ceil(sizeI0./sSpacing).*sSpacing; 156 | prePad = ceil((sizeI - sizeI0)/2); 157 | postPad = floor((sizeI - sizeI0)/2); 158 | 159 | I{1} = padarray(I0{1},prePad,0,'pre'); 160 | I{1} = padarray(I{1},postPad,0,'post'); 161 | 162 | I{2} = padarray(I0{2},prePad,0,'pre'); 163 | I{2} = padarray(I{2},postPad,0,'post'); 164 | 165 | varargout{ 1} = I; 166 | varargout{end+1} = sSize; 167 | varargout{end+1} = sSpacing; 168 | varargout{end+1} = [prePad; postPad]; 169 | varargout{end+1} = DVCPadSize; 170 | varargout{end+1} = u0; 171 | end 172 | 173 | 174 | function [u,cc,m] = parseOutputs(u,cc,finalsSize,padSize,m) 175 | % parses outputs and unpads the displacment field and cc. 176 | 177 | % % % % unpadSize(1,:) = ceil(padSize(1,:)/filterSpacing); 178 | % % % % unpadSize(2,:) = floor((padSize(2,:)+1)/filterSpacing); 179 | % % % % % +1 from the extra meshgrid point during the meshing of the DVC algorithm. EBK (10-23-2013) 180 | % % % % 181 | % % % % for i = 1:3 182 | % % % % u{i} = u{i}(1+unpadSize(1,1):end-unpadSize(2,1),... 183 | % % % % 1+unpadSize(1,2):end-unpadSize(2,2),... 184 | % % % % 1+unpadSize(1,3):end-unpadSize(2,3)); 185 | % % % % end 186 | % % % % u{4} = sqrt(u{1}.^2 + u{2}.^2 + u{3}.^2); 187 | % % % % 188 | % % % % cc = cc(1+unpadSize(1,1):end-unpadSize(2,1),... 189 | % % % % 1+unpadSize(1,2):end-unpadSize(2,2),... 190 | % % % % 1+unpadSize(1,3):end-unpadSize(2,3)); 191 | filterSpacing = finalsSize/2; 192 | u{4} = sqrt(u{1}.^2 + u{2}.^2 + u{3}.^2); 193 | for i = 1:3 194 | m{i} = m{i}-padSize(1,i)-filterSpacing(i); 195 | end 196 | 197 | end 198 | -------------------------------------------------------------------------------- /Matlab Code/Icon_: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDVC/d7254cb150e497ec53d3080469517c6c97fc7793/Matlab Code/Icon_ -------------------------------------------------------------------------------- /Matlab Code/Supplemental MFiles/Icon_: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDVC/d7254cb150e497ec53d3080469517c6c97fc7793/Matlab Code/Supplemental MFiles/Icon_ -------------------------------------------------------------------------------- /Matlab Code/Supplemental MFiles/inpaint_nans.m: -------------------------------------------------------------------------------- 1 | function B=inpaint_nans(A,method) % INPAINT_NANS: in-paints over nans in an array % usage: B=INPAINT_NANS(A) % default method % usage: B=INPAINT_NANS(A,method) % specify method used % % Solves approximation to one of several pdes to % interpolate and extrapolate holes in an array % % arguments (input): % A - nxm array with some NaNs to be filled in % % method - (OPTIONAL) scalar numeric flag - specifies % which approach (or physical metaphor to use % for the interpolation.) All methods are capable % of extrapolation, some are better than others. % There are also speed differences, as well as % accuracy differences for smooth surfaces. % % methods {0,1,2} use a simple plate metaphor. % method 3 uses a better plate equation, % but may be much slower and uses % more memory. % method 4 uses a spring metaphor. % method 5 is an 8 neighbor average, with no % rationale behind it compared to the % other methods. I do not recommend % its use. % % method == 0 --> (DEFAULT) see method 1, but % this method does not build as large of a % linear system in the case of only a few % NaNs in a large array. % Extrapolation behavior is linear. % % method == 1 --> simple approach, applies del^2 % over the entire array, then drops those parts % of the array which do not have any contact with % NaNs. Uses a least squares approach, but it % does not modify known values. % In the case of small arrays, this method is % quite fast as it does very little extra work. % Extrapolation behavior is linear. % % method == 2 --> uses del^2, but solving a direct % linear system of equations for nan elements. % This method will be the fastest possible for % large systems since it uses the sparsest % possible system of equations. Not a least % squares approach, so it may be least robust % to noise on the boundaries of any holes. % This method will also be least able to % interpolate accurately for smooth surfaces. % Extrapolation behavior is linear. % % Note: method 2 has problems in 1-d, so this % method is disabled for vector inputs. % % method == 3 --+ See method 0, but uses del^4 for % the interpolating operator. This may result % in more accurate interpolations, at some cost % in speed. % % method == 4 --+ Uses a spring metaphor. Assumes % springs (with a nominal length of zero) % connect each node with every neighbor % (horizontally, vertically and diagonally) % Since each node tries to be like its neighbors, % extrapolation is as a constant function where % this is consistent with the neighboring nodes. % % method == 5 --+ See method 2, but use an average % of the 8 nearest neighbors to any element. % This method is NOT recommended for use. % % % arguments (output): % B - nxm array with NaNs replaced % % % Example: % [x,y] = meshgrid(0:.01:1); % z0 = exp(x+y); % znan = z0; % znan(20:50,40:70) = NaN; % znan(30:90,5:10) = NaN; % znan(70:75,40:90) = NaN; % % z = inpaint_nans(znan); % % % See also: griddata, interp1 % % Author: John D'Errico % e-mail address: woodchips@rochester.rr.com % Release: 2 % Release date: 4/15/06 % I always need to know which elements are NaN, % and what size the array is for any method [n,m]=size(A); A=A(:); nm=n*m; k=isnan(A(:)); % list the nodes which are known, and which will % be interpolated nan_list=find(k); known_list=find(~k); % how many nans overall nan_count=length(nan_list); % convert NaN indices to (r,c) form % nan_list==find(k) are the unrolled (linear) indices % (row,column) form [nr,nc]=ind2sub([n,m],nan_list); % both forms of index in one array: % column 1 == unrolled index % column 2 == row index % column 3 == column index nan_list=[nan_list,nr,nc]; % supply default method if (nargin<2) || isempty(method) method = 0; elseif ~ismember(method,0:5) error 'If supplied, method must be one of: {0,1,2,3,4,5}.' end % for different methods switch method case 0 % The same as method == 1, except only work on those % elements which are NaN, or at least touch a NaN. % is it 1-d or 2-d? if (m == 1) || (n == 1) % really a 1-d case work_list = nan_list(:,1); work_list = unique([work_list;work_list - 1;work_list + 1]); work_list(work_list <= 1) = []; work_list(work_list >= nm) = []; nw = numel(work_list); u = (1:nw)'; fda = sparse(repmat(u,1,3),bsxfun(@plus,work_list,-1:1), ... repmat([1 -2 1],nw,1),nw,nm); else % a 2-d case % horizontal and vertical neighbors only talks_to = [-1 0;0 -1;1 0;0 1]; neighbors_list=identify_neighbors(n,m,nan_list,talks_to); % list of all nodes we have identified all_list=[nan_list;neighbors_list]; % generate sparse array with second partials on row % variable for each element in either list, but only % for those nodes which have a row index > 1 or < n L = find((all_list(:,2) > 1) & (all_list(:,2) < n)); nl=length(L); if nl>0 fda=sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3)+repmat([-1 0 1],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); else fda=spalloc(n*m,n*m,size(all_list,1)*5); end % 2nd partials on column index L = find((all_list(:,3) > 1) & (all_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3)+repmat([-n 0 n],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); end end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); k=find(any(fda(:,nan_list(:,1)),2)); % and solve... B=A; B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k); case 1 % least squares approach with del^2. Build system % for every array element as an unknown, and then % eliminate those which are knowns. % Build sparse matrix approximating del^2 for % every element in A. % is it 1-d or 2-d? if (m == 1) || (n == 1) % a 1-d case u = (1:(nm-2))'; fda = sparse(repmat(u,1,3),bsxfun(@plus,u,0:2), ... repmat([1 -2 1],nm-2,1),nm-2,nm); else % a 2-d case % Compute finite difference for second partials % on row variable first [i,j]=ndgrid(2:(n-1),1:m); ind=i(:)+(j(:)-1)*n; np=(n-2)*m; fda=sparse(repmat(ind,1,3),[ind-1,ind,ind+1], ... repmat([1 -2 1],np,1),n*m,n*m); % now second partials on column variable [i,j]=ndgrid(1:n,2:(m-1)); ind=i(:)+(j(:)-1)*n; np=n*(m-2); fda=fda+sparse(repmat(ind,1,3),[ind-n,ind,ind+n], ... repmat([1 -2 1],np,1),nm,nm); end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); k=find(any(fda(:,nan_list),2)); % and solve... B=A; B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k); case 2 % Direct solve for del^2 BVP across holes % generate sparse array with second partials on row % variable for each nan element, only for those nodes % which have a row index > 1 or < n % is it 1-d or 2-d? if (m == 1) || (n == 1) % really just a 1-d case error('Method 2 has problems for vector input. Please use another method.') else % a 2-d case L = find((nan_list(:,2) > 1) & (nan_list(:,2) < n)); nl=length(L); if nl>0 fda=sparse(repmat(nan_list(L,1),1,3), ... repmat(nan_list(L,1),1,3)+repmat([-1 0 1],nl,1), ... repmat([1 -2 1],nl,1),n*m,n*m); else fda=spalloc(n*m,n*m,size(nan_list,1)*5); end % 2nd partials on column index L = find((nan_list(:,3) > 1) & (nan_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,3), ... repmat(nan_list(L,1),1,3)+repmat([-n 0 n],nl,1), ... repmat([1 -2 1],nl,1),n*m,n*m); end % fix boundary conditions at extreme corners % of the array in case there were nans there if ismember(1,nan_list(:,1)) fda(1,[1 2 n+1])=[-2 1 1]; end if ismember(n,nan_list(:,1)) fda(n,[n, n-1,n+n])=[-2 1 1]; end if ismember(nm-n+1,nan_list(:,1)) fda(nm-n+1,[nm-n+1,nm-n+2,nm-n])=[-2 1 1]; end if ismember(nm,nan_list(:,1)) fda(nm,[nm,nm-1,nm-n])=[-2 1 1]; end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); % and solve... B=A; k=nan_list(:,1); B(k)=fda(k,k)\rhs(k); end case 3 % The same as method == 0, except uses del^4 as the % interpolating operator. % del^4 template of neighbors talks_to = [-2 0;-1 -1;-1 0;-1 1;0 -2;0 -1; ... 0 1;0 2;1 -1;1 0;1 1;2 0]; neighbors_list=identify_neighbors(n,m,nan_list,talks_to); % list of all nodes we have identified all_list=[nan_list;neighbors_list]; % generate sparse array with del^4, but only % for those nodes which have a row & column index % >= 3 or <= n-2 L = find( (all_list(:,2) >= 3) & ... (all_list(:,2) <= (n-2)) & ... (all_list(:,3) >= 3) & ... (all_list(:,3) <= (m-2))); nl=length(L); if nl>0 % do the entire template at once fda=sparse(repmat(all_list(L,1),1,13), ... repmat(all_list(L,1),1,13) + ... repmat([-2*n,-n-1,-n,-n+1,-2,-1,0,1,2,n-1,n,n+1,2*n],nl,1), ... repmat([1 2 -8 2 1 -8 20 -8 1 2 -8 2 1],nl,1),nm,nm); else fda=spalloc(n*m,n*m,size(all_list,1)*5); end % on the boundaries, reduce the order around the edges L = find((((all_list(:,2) == 2) | ... (all_list(:,2) == (n-1))) & ... (all_list(:,3) >= 2) & ... (all_list(:,3) <= (m-1))) | ... (((all_list(:,3) == 2) | ... (all_list(:,3) == (m-1))) & ... (all_list(:,2) >= 2) & ... (all_list(:,2) <= (n-1)))); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,5), ... repmat(all_list(L,1),1,5) + ... repmat([-n,-1,0,+1,n],nl,1), ... repmat([1 1 -4 1 1],nl,1),nm,nm); end L = find( ((all_list(:,2) == 1) | ... (all_list(:,2) == n)) & ... (all_list(:,3) >= 2) & ... (all_list(:,3) <= (m-1))); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3) + ... repmat([-n,0,n],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); end L = find( ((all_list(:,3) == 1) | ... (all_list(:,3) == m)) & ... (all_list(:,2) >= 2) & ... (all_list(:,2) <= (n-1))); nl=length(L); if nl>0 fda=fda+sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3) + ... repmat([-1,0,1],nl,1), ... repmat([1 -2 1],nl,1),nm,nm); end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); k=find(any(fda(:,nan_list(:,1)),2)); % and solve... B=A; B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k); case 4 % Spring analogy % interpolating operator. % list of all springs between a node and a horizontal % or vertical neighbor hv_list=[-1 -1 0;1 1 0;-n 0 -1;n 0 1]; hv_springs=[]; for i=1:4 hvs=nan_list+repmat(hv_list(i,:),nan_count,1); k=(hvs(:,2)>=1) & (hvs(:,2)<=n) & (hvs(:,3)>=1) & (hvs(:,3)<=m); hv_springs=[hv_springs;[nan_list(k,1),hvs(k,1)]]; end % delete replicate springs hv_springs=unique(sort(hv_springs,2),'rows'); % build sparse matrix of connections, springs % connecting diagonal neighbors are weaker than % the horizontal and vertical springs nhv=size(hv_springs,1); springs=sparse(repmat((1:nhv)',1,2),hv_springs, ... repmat([1 -1],nhv,1),nhv,nm); % eliminate knowns rhs=-springs(:,known_list)*A(known_list); % and solve... B=A; B(nan_list(:,1))=springs(:,nan_list(:,1))\rhs; case 5 % Average of 8 nearest neighbors % generate sparse array to average 8 nearest neighbors % for each nan element, be careful around edges fda=spalloc(n*m,n*m,size(nan_list,1)*9); % -1,-1 L = find((nan_list(:,2) > 1) & (nan_list(:,3) > 1)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([-n-1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % 0,-1 L = find(nan_list(:,3) > 1); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([-n, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % +1,-1 L = find((nan_list(:,2) < n) & (nan_list(:,3) > 1)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([-n+1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % -1,0 L = find(nan_list(:,2) > 1); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([-1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % +1,0 L = find(nan_list(:,2) < n); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % -1,+1 L = find((nan_list(:,2) > 1) & (nan_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([n-1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % 0,+1 L = find(nan_list(:,3) < m); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([n, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % +1,+1 L = find((nan_list(:,2) < n) & (nan_list(:,3) < m)); nl=length(L); if nl>0 fda=fda+sparse(repmat(nan_list(L,1),1,2), ... repmat(nan_list(L,1),1,2)+repmat([n+1, 0],nl,1), ... repmat([1 -1],nl,1),n*m,n*m); end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); % and solve... B=A; k=nan_list(:,1); B(k)=fda(k,k)\rhs(k); end % all done, make sure that B is the same shape as % A was when we came in. B=reshape(B,n,m); % ==================================================== % end of main function % ==================================================== % ==================================================== % begin subfunctions % ==================================================== function neighbors_list=identify_neighbors(n,m,nan_list,talks_to) % identify_neighbors: identifies all the neighbors of % those nodes in nan_list, not including the nans % themselves % % arguments (input): % n,m - scalar - [n,m]=size(A), where A is the % array to be interpolated % nan_list - array - list of every nan element in A % nan_list(i,1) == linear index of i'th nan element % nan_list(i,2) == row index of i'th nan element % nan_list(i,3) == column index of i'th nan element % talks_to - px2 array - defines which nodes communicate % with each other, i.e., which nodes are neighbors. % % talks_to(i,1) - defines the offset in the row % dimension of a neighbor % talks_to(i,2) - defines the offset in the column % dimension of a neighbor % % For example, talks_to = [-1 0;0 -1;1 0;0 1] % means that each node talks only to its immediate % neighbors horizontally and vertically. % % arguments(output): % neighbors_list - array - list of all neighbors of % all the nodes in nan_list if ~isempty(nan_list) % use the definition of a neighbor in talks_to nan_count=size(nan_list,1); talk_count=size(talks_to,1); nn=zeros(nan_count*talk_count,2); j=[1,nan_count]; for i=1:talk_count nn(j(1):j(2),:)=nan_list(:,2:3) + ... repmat(talks_to(i,:),nan_count,1); j=j+nan_count; end % drop those nodes which fall outside the bounds of the % original array L = (nn(:,1)<1)|(nn(:,1)>n)|(nn(:,2)<1)|(nn(:,2)>m); nn(L,:)=[]; % form the same format 3 column array as nan_list neighbors_list=[sub2ind([n,m],nn(:,1),nn(:,2)),nn]; % delete replicates in the neighbors list neighbors_list=unique(neighbors_list,'rows'); % and delete those which are also in the list of NaNs. neighbors_list=setdiff(neighbors_list,nan_list,'rows'); else neighbors_list=[]; end -------------------------------------------------------------------------------- /Matlab Code/Supplemental MFiles/inpaint_nans3.m: -------------------------------------------------------------------------------- 1 | function B=inpaint_nans3(A,method) % INPAINT_NANS3: in-paints over nans in a 3-D array % usage: B=INPAINT_NANS3(A) % default method (0) % usage: B=INPAINT_NANS3(A,method) % specify method used % % Solves approximation to a boundary value problem to % interpolate and extrapolate holes in a 3-D array. % % Note that if the array is large, and there are many NaNs % to be filled in, this may take a long time, or run into % memory problems. % % arguments (input): % A - n1 x n2 x n3 array with some NaNs to be filled in % % method - (OPTIONAL) scalar numeric flag - specifies % which approach (or physical metaphor to use % for the interpolation.) All methods are capable % of extrapolation, some are better than others. % There are also speed differences, as well as % accuracy differences for smooth surfaces. % % method 0 uses a simple plate metaphor. % method 1 uses a spring metaphor. % % method == 0 --> (DEFAULT) Solves the Laplacian % equation over the set of nan elements in the % array. % Extrapolation behavior is roughly linear. % % method == 1 --+ Uses a spring metaphor. Assumes % springs (with a nominal length of zero) % connect each node with every neighbor % (horizontally, vertically and diagonally) % Since each node tries to be like its neighbors, % extrapolation is roughly a constant function where % this is consistent with the neighboring nodes. % % There are only two different methods in this code, % chosen as the most useful ones (IMHO) from my % original inpaint_nans code. % % % arguments (output): % B - n1xn2xn3 array with NaNs replaced % % % Example: % % A linear function of 3 independent variables, % % used to test whether inpainting will interpolate % % the missing elements correctly. % [x,y,z] = ndgrid(-10:10,-10:10,-10:10); % W = x + y + z; % % % Pick a set of distinct random elements to NaN out. % ind = unique(ceil(rand(3000,1)*numel(W))); % Wnan = W; % Wnan(ind) = NaN; % % % Do inpainting % Winp = inpaint_nans3(Wnan,0); % % % Show that the inpainted values are essentially % % within eps of the originals. % std(Winp(ind) - W(ind)) % ans = % 4.3806e-15 % % % See also: griddatan, inpaint_nans % % Author: John D'Errico % e-mail address: woodchips@rochester.rr.com % Release: 1 % Release date: 8/21/08 % Need to know which elements are NaN, and % what size is the array. Unroll A for the % inpainting, although inpainting will be done % fully in 3-d. NA = size(A); A = A(:); nt = prod(NA); k = isnan(A(:)); % list the nodes which are known, and which will % be interpolated nan_list=find(k); known_list=find(~k); % how many nans overall nan_count=length(nan_list); % convert NaN indices to (r,c) form % nan_list==find(k) are the unrolled (linear) indices % (row,column) form [n1,n2,n3]=ind2sub(NA,nan_list); % both forms of index for all the nan elements in one array: % column 1 == unrolled index % column 2 == index 1 % column 3 == index 2 % column 4 == index 3 nan_list=[nan_list,n1,n2,n3]; % supply default method if (nargin<2) || isempty(method) method = 0; elseif ~ismember(method,[0 1]) error 'If supplied, method must be one of: {0,1}.' end % alternative methods switch method case 0 % The same as method == 1, except only work on those % elements which are NaN, or at least touch a NaN. % horizontal and vertical neighbors only talks_to = [-1 0 0;1 0 0;0 -1 0;0 1 0;0 0 -1;0 0 1]; neighbors_list=identify_neighbors(NA,nan_list,talks_to); % list of all nodes we have identified all_list=[nan_list;neighbors_list]; % generate sparse array with second partials on row % variable for each element in either list, but only % for those nodes which have a row index > 1 or < n L = find((all_list(:,2) > 1) & (all_list(:,2) < NA(1))); nL=length(L); if nL>0 fda=sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3)+repmat([-1 0 1],nL,1), ... repmat([1 -2 1],nL,1),nt,nt); else fda=spalloc(nt,nt,size(all_list,1)*7); end % 2nd partials on column index L = find((all_list(:,3) > 1) & (all_list(:,3) < NA(2))); nL=length(L); if nL>0 fda=fda+sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3)+repmat([-NA(1) 0 NA(1)],nL,1), ... repmat([1 -2 1],nL,1),nt,nt); end % 2nd partials on third index L = find((all_list(:,4) > 1) & (all_list(:,4) < NA(3))); nL=length(L); if nL>0 ntimesm = NA(1)*NA(2); fda=fda+sparse(repmat(all_list(L,1),1,3), ... repmat(all_list(L,1),1,3)+repmat([-ntimesm 0 ntimesm],nL,1), ... repmat([1 -2 1],nL,1),nt,nt); end % eliminate knowns rhs=-fda(:,known_list)*A(known_list); k=find(any(fda(:,nan_list(:,1)),2)); % and solve... B=A; B(nan_list(:,1))=fda(k,nan_list(:,1))\rhs(k); case 1 % Spring analogy % interpolating operator. % list of all springs between a node and a horizontal % or vertical neighbor hv_list=[-1 -1 0 0;1 1 0 0;-NA(1) 0 -1 0;NA(1) 0 1 0; ... -NA(1)*NA(2) 0 0 -1;NA(1)*NA(2) 0 0 1]; hv_springs=[]; for i=1:size(hv_list,1) hvs=nan_list+repmat(hv_list(i,:),nan_count,1); k=(hvs(:,2)>=1) & (hvs(:,2)<=NA(1)) & ... (hvs(:,3)>=1) & (hvs(:,3)<=NA(2)) & ... (hvs(:,4)>=1) & (hvs(:,4)<=NA(3)); hv_springs=[hv_springs;[nan_list(k,1),hvs(k,1)]]; end % delete replicate springs hv_springs=unique(sort(hv_springs,2),'rows'); % build sparse matrix of connections nhv=size(hv_springs,1); springs=sparse(repmat((1:nhv)',1,2),hv_springs, ... repmat([1 -1],nhv,1),nhv,prod(NA)); % eliminate knowns rhs=-springs(:,known_list)*A(known_list); % and solve... B=A; B(nan_list(:,1))=springs(:,nan_list(:,1))\rhs; end % all done, make sure that B is the same shape as % A was when we came in. B=reshape(B,NA); % ==================================================== % end of main function % ==================================================== % ==================================================== % begin subfunctions % ==================================================== function neighbors_list=identify_neighbors(NA,nan_list,talks_to) % identify_neighbors: identifies all the neighbors of % those nodes in nan_list, not including the nans % themselves % % arguments (input): % NA - 1x3 vector = size(A), where A is the % array to be interpolated % nan_list - array - list of every nan element in A % nan_list(i,1) == linear index of i'th nan element % nan_list(i,2) == row index of i'th nan element % nan_list(i,3) == column index of i'th nan element % nan_list(i,4) == third index of i'th nan element % talks_to - px2 array - defines which nodes communicate % with each other, i.e., which nodes are neighbors. % % talks_to(i,1) - defines the offset in the row % dimension of a neighbor % talks_to(i,2) - defines the offset in the column % dimension of a neighbor % % For example, talks_to = [-1 0;0 -1;1 0;0 1] % means that each node talks only to its immediate % neighbors horizontally and vertically. % % arguments(output): % neighbors_list - array - list of all neighbors of % all the nodes in nan_list if ~isempty(nan_list) % use the definition of a neighbor in talks_to nan_count=size(nan_list,1); talk_count=size(talks_to,1); nn=zeros(nan_count*talk_count,3); j=[1,nan_count]; for i=1:talk_count nn(j(1):j(2),:)=nan_list(:,2:4) + ... repmat(talks_to(i,:),nan_count,1); j=j+nan_count; end % drop those nodes which fall outside the bounds of the % original array L = (nn(:,1)<1) | (nn(:,1)>NA(1)) | ... (nn(:,2)<1) | (nn(:,2)>NA(2)) | ... (nn(:,3)<1) | (nn(:,3)>NA(3)); nn(L,:)=[]; % form the same format 4 column array as nan_list neighbors_list=[sub2ind(NA,nn(:,1),nn(:,2),nn(:,3)),nn]; % delete replicates in the neighbors list neighbors_list=unique(neighbors_list,'rows'); % and delete those which are also in the list of NaNs. neighbors_list=setdiff(neighbors_list,nan_list,'rows'); else neighbors_list=[]; end -------------------------------------------------------------------------------- /Matlab Code/Supplemental MFiles/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009, Andriy Myronenko 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Matlab Code/Supplemental MFiles/mirt3D_mexinterp.cpp: -------------------------------------------------------------------------------- 1 | /* Fast 3D linear interpolation 2 | Andriy Myronenko, Feb 2008, email: myron@csee.ogi.edu, homepage: http://www.bme.ogi.edu/~myron/ 3 | 4 | Output_image = mirt3D_mexinterp(Input_image, XI,YI,ZI) interpolates the 3D image 'Input_image' at 5 | the points with coordinates XI,YI,ZI. Input_image is assumed to be defined at a regular grid 1:N, 1:M, 1:K, 6 | where [M,N,K]=size(Input_images). Points outside the boundary return NaNs. 7 | This is equivalent to Matlab's: 8 | Output_image = interp3(Input_image,XI,YI,ZI,'linear',NaN); 9 | 10 | Output_images = mirt3D_mexinterp(Input_images, XI,YI,ZI). Input_images can be a stack of many 3D images (4D). 11 | The function interpolates each of the 3D images at X,Y,Z coordinates and return a stack of corresponding 12 | interpolated images. This is equivalent to Matlabs 13 | 14 | Input_images(:,:,:,1)=Input_image1; 15 | Input_images(:,:,:,2)=Input_image2; 16 | Input_images(:,:,:,3)=Input_image3; 17 | Input_images(:,:,:,4)=Input_image4; 18 | 19 | Output_images(:,:,:,1) = interp3(Input_image1,XI,YI,ZI,'linear',NaN); 20 | Output_images(:,:,:,2) = interp3(Input_image2,XI,YI,ZI,'linear',NaN); 21 | Output_images(:,:,:,3) = interp3(Input_image3,XI,YI,ZI,'linear',NaN); 22 | Output_images(:,:,:,4) = interp3(Input_image4,XI,YI,ZI,'linear',NaN); 23 | 24 | This is especially usefull to vector valued 3D images, RGB images, or to interpolate the whole 3D video at the same coordinates 25 | or to interpolate image and its gradients at the same time (in image registration). 26 | The speed gain is from the precomputation of nearest points for interpolation, which are the same for all images in a stack. 27 | 28 | */ 29 | 30 | 31 | #include 32 | #include "mex.h" 33 | 34 | 35 | void mirt3D_mexinterp( 36 | double* Z, 37 | double* S, 38 | double* T, 39 | double* W, 40 | double* F, 41 | int MN, 42 | int nrows, 43 | int ncols, 44 | int npages, 45 | int ndim 46 | ) 47 | { 48 | 49 | int n, in1, in2, in3, in4, in5, in6, in7, in8; 50 | double t, s, s1, w, w1, tmp, nan; 51 | double m1, m2, m3, m4, m5, m6, m7, m8; 52 | int ndx, nw, Zshift, i, nrowsncols, ft, fs, fw; 53 | 54 | nw = nrows*ncols; 55 | nrowsncols=nw*npages; 56 | nan=mxGetNaN(); 57 | 58 | for (n=0; n < MN; n++) { 59 | 60 | t=T[n]; 61 | s=S[n]; 62 | w=W[n]; 63 | 64 | ft=(int) floor(t); 65 | fs=(int) floor(s); 66 | fw=(int) floor(w); 67 | 68 | 69 | if (fs<1 || s>ncols || ft<1 || t>nrows || fw<1 || w>npages){ 70 | /* Put nans if outside*/ 71 | for (i = 0; i < ndim; i++) F[n+i*MN]=nan; } 72 | else { 73 | 74 | 75 | ndx = ft+(fs-1)*nrows+(fw-1)*nw; 76 | 77 | if (s==ncols){ s=s+1; ndx=ndx-nrows; } 78 | s=s-fs; 79 | if (t==nrows){ t=t+1; ndx=ndx-1; } 80 | t=t-ft; 81 | if (w==npages){ w=w+1; ndx=ndx-nw; } 82 | w=w-fw; 83 | 84 | in1=ndx-1; 85 | in2=ndx; 86 | // in3=ndx+nrows-1; 87 | in3=in1+nrows; 88 | // in4=ndx+nrows; 89 | in4=in3+1; 90 | 91 | // in5=ndx+nw-1; 92 | in5=in1+nw; 93 | // in6=ndx+nw; 94 | in6=in5+1; 95 | // in7=ndx+nrows+nw-1; 96 | in7=in5+nrows; 97 | // in8=ndx+nrows+nw; 98 | in8 = in7+1; 99 | 100 | //////////// 101 | s1=1-s; 102 | w1=1-w; 103 | 104 | tmp=s1*w1; 105 | m2=t*tmp; 106 | m1=tmp-m2; 107 | 108 | tmp=s*w1; 109 | m4=t*tmp; 110 | m3=tmp-m4; 111 | 112 | tmp=s1*w; 113 | m6=t*tmp; 114 | m5=tmp-m6; 115 | 116 | tmp=s*w; 117 | m8=t*tmp; 118 | m7=tmp-m8; 119 | 120 | 121 | for (i = 0; i < ndim; i++){ 122 | 123 | Zshift=i*nrowsncols; 124 | F[n+i*MN]=Z[in1+Zshift]*m1+Z[in2+Zshift]*m2+Z[in3+Zshift]*m3+Z[in4+Zshift]*m4+Z[in5+Zshift]*m5+Z[in6+Zshift]*m6+ 125 | Z[in7+Zshift]*m7+Z[in8+Zshift]*m8; 126 | } 127 | 128 | } 129 | 130 | } // cycle end 131 | 132 | return; 133 | } 134 | 135 | 136 | 137 | 138 | // ------- Main function definitions ---------- 139 | /* Input arguments */ 140 | #define IN_Z prhs[0] 141 | #define IN_S prhs[1] 142 | #define IN_T prhs[2] 143 | #define IN_W prhs[3] 144 | 145 | /* Output arguments */ 146 | #define OUT_F plhs[0] 147 | 148 | /* Gateway routine */ 149 | void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] ) 150 | { 151 | double *Z, *S, *T, *W, *F; 152 | int i, MN, nrows, ncols, npages, vol, ndim, newXndim, Xndim, *newdims; 153 | const int *dims, *Xdims; 154 | 155 | 156 | /* Check for input errors */ 157 | if (nlhs>1) 158 | mexErrMsgTxt("Wrong number of output parameters, usage: Output_images = mirt3D_mexinterp(Input_images, X, Y, Z)"); 159 | if (nrhs!=4) 160 | mexErrMsgTxt("Wrong number of input parameters, usage: Output_images = mirt3D_mexinterp(Input_images, X, Y, Z)"); 161 | 162 | if (!mxIsDouble(IN_Z) || !mxIsDouble(IN_S) || !mxIsDouble(IN_T) || !mxIsDouble(IN_W)) 163 | mexErrMsgTxt("mirt3D_mexinterp: Input arguments must be double."); 164 | 165 | if ((mxGetNumberOfDimensions(IN_S) != mxGetNumberOfDimensions(IN_T)) || 166 | (mxGetNumberOfDimensions(IN_S) != mxGetNumberOfDimensions(IN_W)) || 167 | (mxGetNumberOfElements(IN_S) != mxGetNumberOfElements(IN_T)) || 168 | (mxGetNumberOfElements(IN_S) != mxGetNumberOfElements(IN_W))) mexErrMsgTxt("Input parameters X, Y, Z must have the same size"); 169 | 170 | /* Get the sizes of each input argument */ 171 | 172 | Xndim = mxGetNumberOfDimensions(IN_S); 173 | Xdims = mxGetDimensions(IN_S); 174 | 175 | 176 | ndim = mxGetNumberOfDimensions(IN_Z); 177 | dims = mxGetDimensions(IN_Z); 178 | newdims = (int*) calloc(ndim-1, sizeof(int)); 179 | 180 | MN=1; 181 | for (i = 0; i < Xndim; i++) {MN =MN*Xdims[i];}; /*Total number of interpolations points in 1 image*/ 182 | 183 | 184 | vol=1; newXndim=Xndim; 185 | if (ndim>3) { /*Check if we have several images*/ 186 | 187 | 188 | if ((Xndim==2) && (Xdims[1]==1)) {newXndim=newXndim-1; } /*Check if interpolate along column vectors*/ 189 | newdims = (int*) calloc(newXndim+1, sizeof(int)); /*Allocate space for the new number of dimensions for output*/ 190 | for (i = 0; i < newXndim; i++) {newdims[i]=Xdims[i];}; /*Copy original dimenstions*/ 191 | newdims[newXndim]=dims[3]; /*Add the number of images as a last dimenstion*/ 192 | newXndim=newXndim+1; /*Set the new number of dimenstions*/ 193 | vol=dims[3];} 194 | else 195 | { 196 | newdims = (int*) calloc(newXndim, sizeof(int)); 197 | for (i = 0; i < newXndim; i++) {newdims[i]=Xdims[i];}; 198 | 199 | } 200 | 201 | 202 | /*Create the array to put the interpolated points*/ 203 | OUT_F = mxCreateNumericArray(newXndim, newdims, mxDOUBLE_CLASS, mxREAL); 204 | 205 | /* Input image size */ 206 | nrows = dims[0]; 207 | ncols = dims[1]; 208 | npages = dims[2]; 209 | 210 | /* Assign pointers to the input arguments */ 211 | Z = mxGetPr(IN_Z); 212 | S = mxGetPr(IN_S); 213 | T = mxGetPr(IN_T); 214 | W = mxGetPr(IN_W); 215 | 216 | /* Assign pointers to the output arguments */ 217 | F = mxGetPr(OUT_F); 218 | 219 | /* Do the actual computations in a subroutine */ 220 | mirt3D_mexinterp(Z, S, T, W, F, MN, nrows, ncols, npages, vol); 221 | 222 | free((void*)newdims); 223 | return; 224 | } 225 | 226 | 227 | -------------------------------------------------------------------------------- /Matlab Code/Supplemental MFiles/mirt3D_mexinterp.m: -------------------------------------------------------------------------------- 1 | %MIRT3D_MEXINTERP Fast 3D linear interpolation 2 | % 3 | % Output_image = mirt3D_mexinterp(Input_image, XI,YI,ZI) interpolates the 3D image 'Input_image' at 4 | % the points with coordinates X,Y,Z. Input_image is assumed to be defined at a regular grid 1:N, 1:M, 1:K, 5 | % where [M,N,K]=size(Input_images). Points outside the boudary return NaNs. 6 | % This is equivalent (but much faster) to Matlab's: 7 | % Output_image = interp3(Input_image,XI,YI,ZI,'linear',NaN); 8 | % 9 | % Output_images = mirt3D_mexinterp(Input_images, XI,YI,ZI). Input_images can be a stack of many 3D images (4D). 10 | % The function interpolates each of the 3D images at X,Y,Z coordinates and return a stack of corresponding 11 | % interpolated images. This is equivalent to Matlab's 12 | % 13 | % Input_images(:,:,:,1)=Input_image1; 14 | % Input_images(:,:,:,2)=Input_image2; 15 | % Input_images(:,:,:,3)=Input_image3; 16 | % Input_images(:,:,:,4)=Input_image4; 17 | % 18 | % Output_images(:,:,:,1) = interp3(Input_image1,XI,YI,ZI,'linear',NaN); 19 | % Output_images(:,:,:,2) = interp3(Input_image2,XI,YI,ZI,'linear',NaN); 20 | % Output_images(:,:,:,3) = interp3(Input_image3,XI,YI,ZI,'linear',NaN); 21 | % Output_images(:,:,:,4) = interp3(Input_image4,XI,YI,ZI,'linear',NaN); 22 | % 23 | % This is especially usefull fpr vector valued 3D images, RGB images, to interpolate the whole 3D video at the same coordinates 24 | % or to interpolate image and its gradients at the same time (in image registration). 25 | % The speed gain is also from the precomputation of nearest points for interpolation, which are the same for all images in a stack. 26 | % 27 | % Andriy Myronenko, Feb 2008, email: myron@csee.ogi.edu, 28 | % homepage: http://www.bme.ogi.edu/~myron/ 29 | 30 | 31 | % The function below compiles the mirt3D_mexinterp.cpp file if you haven't done it yet. 32 | % It will be executed only once at the very first run. 33 | function Output_images = mirt3D_mexinterp(Input_images, XI,YI,ZI) 34 | 35 | pathtofile=which('mirt3D_mexinterp.cpp'); 36 | pathstr = fileparts(pathtofile); 37 | mex(pathtofile,'-outdir',pathstr); 38 | 39 | Output_images = mirt3D_mexinterp(Input_images, XI,YI,ZI); 40 | 41 | end -------------------------------------------------------------------------------- /Matlab Code/Supplemental MFiles/mirt3D_mexinterp.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDVC/d7254cb150e497ec53d3080469517c6c97fc7793/Matlab Code/Supplemental MFiles/mirt3D_mexinterp.mexw64 -------------------------------------------------------------------------------- /Matlab Code/addDisplacements.m: -------------------------------------------------------------------------------- 1 | function [u, du, cc, m] = addDisplacements(u0,du0,cc0,m0,dm) 2 | % u = addDisplacements(u,thr,epsilon) removes outliers using the universal 3 | % outlier test based on 4 | % 5 | % J. Westerweel and F. Scarano. Universal outlier detection for PIV data. 6 | % Exp. Fluids, 39(6):1096{1100, August 2005. doi: 10.1007/s00348-005-0016-6 7 | % 8 | % INPUTS 9 | % ------------------------------------------------------------------------- 10 | % u0: displacement field vector defined at every meshgrid point with 11 | % spacing dm. Format: cell array, each containing a 3D matrix 12 | % (components in x,y,z) 13 | % u0{1} = displacement in x-direction 14 | % u0{2} = displacement in y-direction 15 | % u0{3} = displacement in z-direction 16 | % u0{4} = magnitude 17 | % thr: theshold for passing residiual (default = 2) 18 | % epsilon: fluctuation level due to cross-correlation (default = 0.1) 19 | % 20 | % OUTPUTS 21 | % ------------------------------------------------------------------------- 22 | % u: cell containing the displacement field with outliers removed 23 | % normFluctValues: normalized fluctuation values based on the universal 24 | % outier test. 25 | % 26 | % NOTES 27 | % ------------------------------------------------------------------------- 28 | % needs medFilt3 and John D'Errico's inpaint_nans3 29 | % (http://www.mathworks.com/matlabcentral/fileexchange/4551-inpaint-nans)function. 30 | % 31 | % If used please cite: 32 | % Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast 33 | % iterative digital volume correlation algorithm for large deformations. 34 | % Experimental Mechanics. doi: 10.1007/s11340-014-9874-2 35 | 36 | for i = 1:3, du0{i} = inpaint_nans3(du0{i}); end % remove NaNs if present 37 | 38 | idx = cell(1,3); 39 | for i = 1:3, idx{i} = m0{i}(1):dm:m0{i}(end); end % construct new meshgrid 40 | 41 | [m0_{1}, m0_{2}, m0_{3}] = ndgrid(m0{1},m0{2},m0{3}); 42 | [m{1}, m{2}, m{3}] = ndgrid(idx{1},idx{2},idx{3}); 43 | % sample to desired mesh spacing 44 | 45 | du = cell(1,3); 46 | for i = 1:3, 47 | F = griddedInterpolant(m0_{1}, m0_{2}, m0_{3}, du0{i}, 'linear'); 48 | du{i} = F(m{1},m{2},m{3}); 49 | end 50 | 51 | F = griddedInterpolant(m0_{1}, m0_{2}, m0_{3}, cc0, 'linear'); 52 | cc = F(m{1},m{2},m{3}); 53 | 54 | if sum(cellfun(@numel, u0)) == 3, u = du; % on first iteration u = du 55 | else u = cellfun(@plus,u0,du,'UniformOutput', 0); % else u^(k) = sum(u^(k-1)) + du (see eq. 7) 56 | end 57 | 58 | end -------------------------------------------------------------------------------- /Matlab Code/checkConvergenceSSD.m: -------------------------------------------------------------------------------- 1 | function [converged01, SSE1 , sSize1, sSpacing1] = checkConvergenceSSD(I,SSE,sSize,sSpacing,convergenceCrit) 2 | % [converged01, SSD1 , sSize1, sSpacing1] = 3 | % checkConvergenceSSD(I,SSD,sSize,sSpacing,convergenceCrit) checks the 4 | % convergence of the IDVC. The convergence is based on the sum of squared 5 | % error (SSE) similarity metric between the undeformed and deformed 6 | % image. 7 | % 8 | % INPUTS 9 | % ------------------------------------------------------------------------- 10 | % I: cell containing the undeformed, I{1}, and deformed, I{2} 3-D images 11 | % SSE: array of SSD values for all iterations 12 | % sSize: interrogation window (subset) size for all iterations 13 | % sSpacing: interrogation window (subset) spacing for all iterations 14 | % convergenceCrit: Array containing convergence criteria for stopping the 15 | % iterations. [local, global] where local defines when 16 | % to refine the sSize and/or sSpacing and global defines 17 | % when to stop the iterations without refinement. 18 | % 19 | % OUTPUTS 20 | % ------------------------------------------------------------------------- 21 | % converged01: boolean, 1 = met convergence criteria for stopping, 0 = 22 | % vice versa 23 | % SSE1: SSE for current iteration 24 | % sSize1: interrogation window (subset) size for the current iteration 25 | % sSpacing1: interrogation window (subset) spacing for the current 26 | % iteration 27 | % 28 | % NOTES 29 | % ------------------------------------------------------------------------- 30 | % You are welcome to change the convergence method however you'd like. The 31 | % default constants are based on empirical results. 32 | % 33 | % If used please cite: 34 | % Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast 35 | % iterative digital volume correlation algorithm for large deformations. 36 | % Experimental Mechanics. doi: 10.1007/s11340-014-9874-2 37 | 38 | I{1}(isnan(I{1})) = 0; 39 | I{2}(isnan(I{2})) = 0; 40 | 41 | % Calculated SSE (see eq. 12) 42 | I{1} = I{1}(:); I{2} = I{2}(:); 43 | N = numel(I{1}); 44 | 45 | err = sqrt(sum((I{1}-I{2}).^2)/N); 46 | sig(1) = sqrt(sum((I{1}-mean(I{1})).^2)/N); 47 | sig(2) = sqrt(sum((I{2}-mean(I{2})).^2)/N); 48 | SSE1 = err/mean([sig(1),sig(2)]); 49 | 50 | SSE(end + 1) = SSE1; 51 | 52 | % set default values 53 | sSize0 = sSize(end,:); sSpacing0 = sSpacing(end,:); 54 | sSize1 = sSize(end,:); sSpacing1 = sSpacing(end,:); 55 | iteration = size(sSize,1); 56 | dSSE = nan; 57 | converged01 = 0; 58 | 59 | 60 | if iteration > 1 % skip before first displacement estimation 61 | sSize1 = sSize0/2; % window size refinement 62 | 63 | % ensure that all subset sizes are at minimum 32 voxels in length 64 | sSize1(sSize1 < 32) = 32; 65 | 66 | % window spacing refinement. Only do if the sSpacing > 8 voxels 67 | if (sSpacing0 > 8), sSpacing1 = sSize1/2; 68 | 69 | 70 | end 71 | 72 | if prod(single(sSpacing1 == 16)) % condition if spacing = 16 73 | 74 | idx = (find(prod(single(sSpacing == 16),2))-1):iteration; 75 | idx = idx(idx~=0); 76 | if length(idx) > 2 77 | dSSE = diff(SSE(idx)); % calculate difference 78 | dSSE = dSSE/dSSE(1); % normalize difference 79 | 80 | % if dSSE meets first convergence criteria then refine spacing 81 | % to the minimum value, 8 voxels. 82 | if dSSE(end) <= convergenceCrit(1) 83 | sSize1 = sSize0; sSpacing1 = [8 8 8]; 84 | 85 | end 86 | end 87 | 88 | % condition if spacing is the minimum, 8 voxels 89 | elseif prod(single(sSpacing1 == 8)) 90 | idx = (find(prod(single(sSpacing == 8),2))-1):iteration; 91 | 92 | if length(idx) > 2 93 | dSSE = diff(SSE(idx)); 94 | dSSE = dSSE/dSSE(1); 95 | 96 | % if dSSE meets first convergence criteria and spacing is the 97 | % mimumum then convergence has been met and stop all 98 | % iterations. 99 | if dSSE(end) <= convergenceCrit(2) 100 | sSize1 = sSize0; sSpacing1 = sSpacing0; 101 | converged01 = 1; 102 | end 103 | end 104 | end 105 | 106 | end 107 | 108 | % global threshold criteria 109 | if SSE(end)/SSE(1) < convergenceCrit(3), converged01 = 1; end 110 | 111 | end -------------------------------------------------------------------------------- /Matlab Code/filterDisplacements.m: -------------------------------------------------------------------------------- 1 | function u = filterDisplacements(u0,filterSize,z) 2 | % I = filterDisplacements(I0,filterSize,z) applies a low-pass convolution 3 | % filter to the displacement field to mitigate divergence based on 4 | % 5 | % F. F. J. Schrijer and F. Scarano. Effect of predictor corrector filtering 6 | % on the stability and spatial resolution of iterative PIV interrogation. 7 | % Exp. Fluids, 45(5):927{941, May 2008. doi: 10.1007/s00348-008-0511-7 8 | % 9 | % INPUTS 10 | % ------------------------------------------------------------------------- 11 | % u0: displacement field vector defined at every meshgrid point with 12 | % spacing dm. Format: cell array, each containing a 3D matrix 13 | % (components in x,y,z) 14 | % u0{1} = displacement in x-direction 15 | % u0{2} = displacement in y-direction 16 | % u0{3} = displacement in z-direction 17 | % u0{4} = magnitude 18 | % filterSize: size of the filter 19 | % z: filter strength 20 | % 21 | % OUTPUTS 22 | % ------------------------------------------------------------------------- 23 | % u: cell containing the filtered displacement field 24 | % 25 | % NOTES 26 | % ------------------------------------------------------------------------- 27 | % none 28 | % 29 | % For more information please see section 2.2. 30 | % If used please cite: 31 | % Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast 32 | % iterative digital volume correlation algorithm for large deformations. 33 | % Experimental Mechanics. doi: 10.1007/s11340-014-9874-2 34 | % 35 | 36 | % Parse inputs and set defaults 37 | if nargin < 3, z = 0.0075; end 38 | if ~iscell(u0), u0 = {u0}; end 39 | u = cell(size(u0)); 40 | 41 | if z == 0, 42 | u = cellfun(@double, u0, 'UniformOutput',0); % no filter 43 | else 44 | rf = generateFilter(filterSize,z); 45 | 46 | % apply filter using convolution 47 | for i = 1:length(u0), u{i} = double(convn(u0{i}, rf,'same')); end 48 | end 49 | 50 | end 51 | 52 | %% ======================================================================== 53 | function rf = generateFilter(filterSize,z) 54 | % generates the filter convolution filter 55 | l = filterSize; 56 | 57 | [m{1}, m{2}, m{3}] = ndgrid(-l(1)/2:l(1)/2,-l(2)/2:l(2)/2,-l(3)/2:l(3)/2); 58 | m = cellfun(@abs, m, 'UniformOutput', 0); 59 | 60 | 61 | f1 = (l(1)/2)^z - (m{1}).^z; 62 | f2 = (l(2)/2)^z - (m{2}).^z; 63 | f3 = (l(3)/2)^z - (m{3}).^z; 64 | 65 | i{1} = (m{1} >= m{2} & m{1} >= m{3}); 66 | i{2} = (m{1} < m{2} & m{2} >= m{3}); 67 | i{3} = (m{3} > m{2} & m{3} > m{1}); 68 | 69 | rf0 = f1.*i{1}+f2.*i{2}+f3.*i{3}; 70 | 71 | rf = rf0/sum(rf0(:)); 72 | 73 | end -------------------------------------------------------------------------------- /Matlab Code/funIDVC.m: -------------------------------------------------------------------------------- 1 | function [u, cc, dm, m] = funIDVC(varargin) 2 | % u = funIDVC(filename, sSize, incORcum) is the main function that performs 3 | % IDVC on a time increment of volumetric images. 4 | % 5 | % INPUTS 6 | % ------------------------------------------------------------------------- 7 | % filename: string for the filename prefix for the volumetric images in 8 | % the current directory. 9 | % Input options: 10 | % --- If image is not within a cell) --- 11 | % 1) 'filename*.mat' or 'filename*' 12 | % 13 | % --- If image is within a cell that contains multichannels --- 14 | % 2) filename{1} = 'filename*.mat' or 'filename*' and 15 | % filename{2} = channel number containing images you want to 16 | % run IDVC on. 17 | % (if the channel is not provided, i.e. length(filename) = 1 18 | % , then channel = 1 19 | % 20 | % sSize: interrogation window (subset) size for the first iterations. 21 | % Must be, 32,64,96, or 128 voxels and a three column 22 | % array (one for each dimenision) or scalar (equal for all 23 | % dimensions). 24 | % incORcum: string that defines the method of running IDVC. Options: 25 | % cumulative (time0 -> time1, time0 -> time2, ...) 26 | % (Allowable inputs: 'c','cum','cumulative') 27 | % or 28 | % incremental (time0 -> time1, time1 -> time2, ...) 29 | % (Allowable inputs: 'i','inc','incremental') 30 | % 31 | % OUTPUTS 32 | % ------------------------------------------------------------------------- 33 | % u: displacement field vector defined at every meshgrid point with 34 | % spacing dm. Format: cell array, each containing a 3D matrix for each 35 | % time point 36 | % (components in x,y,z) 37 | % u{time}{1} = displacement in x-direction 38 | % u{time}{2} = displacement in y-direction 39 | % u{time}{3} = displacement in z-direction 40 | % u{time}{4} = magnitude 41 | % cc: peak values of the cross-correlation for each interrogation point 42 | % dm: meshgrid spacing (8 by default) 43 | % m: The grid points at which displacements are computed. The grid 44 | % points locations are in the same format as 'u'. 45 | % 46 | % NOTES 47 | % ------------------------------------------------------------------------- 48 | % none 49 | % 50 | % For more information please see 51 | % 52 | 53 | %% ---- Opening & Reading the First Image into CPU Memory ---- 54 | [fileInfo, sSize0, incORcum, u_] = parseInputs(varargin{:}); 55 | 56 | I{1} = loadFile(fileInfo,1); 57 | 58 | %% ---- Opening and Reading Subsequent Images --- 59 | numImages = length(fileInfo.filename); 60 | u = cell(numImages-1,1); cc = cell(numImages-1,1); 61 | for i = 2:numImages % Reads Volumes Starting on the Second Volumes 62 | tStart = tic; 63 | I{2} = loadFile(fileInfo,i); 64 | 65 | %Start DVC 66 | disp(['Current file: ' fileInfo.filename{i}]) 67 | [u_, cc{i-1}, dm, m] = IDVC(I,sSize0,u_); 68 | 69 | % Saving iterations of the DVC 70 | u{i-1}{1} = -u_{1}; u{i-1}{2} = -u_{2}; u{i-1}{3} = -u_{3}; u{i-1}{4} = u_{4}; 71 | 72 | if strcmpi(incORcum(1),'i'); I{1} = I{2}; u_ = num2cell(zeros(1,3)); 73 | else u_ = u_(1:3); 74 | end 75 | 76 | disp(['Elapsed Time for all iterations: ',num2str(toc(tStart))]); 77 | end 78 | 79 | end 80 | 81 | function I = loadFile(fileInfo,idx) 82 | I = load(fileInfo.filename{idx}); 83 | fieldName = fieldnames(I); 84 | I = getfield(I,fieldName{1}); 85 | if iscell(I), 86 | if numel(I), I = I{1}; 87 | else 88 | I = I{fileInfo.dataChannel}; 89 | end 90 | end 91 | end 92 | 93 | function varargout = parseInputs(varargin) 94 | % = parseInputs(filename, sSize, incORcum) 95 | 96 | 97 | % Parse filenames 98 | filename = varargin{1}; 99 | if iscell(filename) 100 | if length(filename) == 1, fileInfo.datachannel = 1; 101 | else fileInfo.datachannel = filename{2}; 102 | end 103 | filename = filename{1}; 104 | end 105 | 106 | 107 | [~,filename,~] = fileparts(filename); 108 | filename = dir([filename,'.mat']); 109 | fileInfo.filename = {filename.name}; 110 | 111 | if isempty(fileInfo), error('File name doesn''t exist'); end 112 | 113 | % Ensure dimensionality of the subset size 114 | sSize = varargin{2}; 115 | if numel(sSize) == 1, 116 | sSize = sSize*[1 1 1]; 117 | elseif numel(sSize) ~=3, 118 | error('Subset size must be a scalar or a three column array'); 119 | end 120 | 121 | % Ensure range of subset size 122 | if min(sSize) < 32 || max(sSize > 128) 123 | error('Subset size must be within 32 and 128 pixels'); 124 | end 125 | 126 | % Ensure even subset size 127 | % if sum(mod(sSize,4)) > 0 128 | % error('Subset size must be even'); 129 | % end 130 | 131 | if sum(mod(sSize,32)) ~= 0 132 | error('Subset size must be 32, 64, 96, or 128 voxels in each dimension'); 133 | end 134 | 135 | % Check run method input 136 | incORcum = varargin{3}; 137 | 138 | if ~(strcmpi(incORcum(1),'c') || strcmpi(incORcum(1),'i')) 139 | error('Run method must be incremental or cumulative'); 140 | end 141 | 142 | % Initial guess of displacement field = [0 0 0]; 143 | u0 = num2cell(zeros(1,3)); 144 | 145 | % Outputs 146 | varargout{ 1} = fileInfo; 147 | varargout{end + 1} = sSize; 148 | varargout{end + 1} = incORcum; 149 | varargout{end+1} = u0; 150 | 151 | end -------------------------------------------------------------------------------- /Matlab Code/removeOutliers.m: -------------------------------------------------------------------------------- 1 | function [u,normFluctValues] = removeOutliers(u,thr,epsilon) 2 | % u = removeOutliers(u,thr,epsilon) removes outliers using the universal 3 | % outlier test based on 4 | % 5 | % J. Westerweel and F. Scarano. Universal outlier detection for PIV data. 6 | % Exp. Fluids, 39(6):1096{1100, August 2005. doi: 10.1007/s00348-005-0016-6 7 | % 8 | % INPUTS 9 | % ------------------------------------------------------------------------- 10 | % u: cell containing the input displacement field. (u{1:3} = {u_x, u_y, 11 | % u_z}) 12 | % thr: theshold for passing residiual (default = 2) 13 | % epsilon: fluctuation level due to cross-correlation (default = 0.1) 14 | % 15 | % OUTPUTS 16 | % ------------------------------------------------------------------------- 17 | % u: cell containing the displacement field with outliers removed 18 | % normFluctValues: normalized fluctuation values based on the universal 19 | % outier test. 20 | % 21 | % NOTES 22 | % ------------------------------------------------------------------------- 23 | % needs medFilt3 and John D'Errico's inpaint_nans3 24 | % (http://www.mathworks.com/matlabcentral/fileexchange/4551-inpaint-nans)function. 25 | % 26 | % For more information please see section 2.2. 27 | % If used please cite: 28 | % Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast 29 | % iterative digital volume correlation algorithm for large deformations. 30 | % Experimental Mechanics. doi: 10.1007/s11340-014-9874-2 31 | 32 | % set default values 33 | if nargin < 3, epsilon = 0.1; end 34 | if nargin < 2, thr = 2; end 35 | if ~iscell(u), u = {u}; end 36 | 37 | 38 | medianU = cell(size(u)); 39 | normFluct = cell(size(u)); 40 | normFluctMag = zeros(size(u{1})); 41 | 42 | for i = 1:length(u) 43 | 44 | [medianU{i}, normFluct{i}] = funRemoveOutliers(u{i},epsilon); 45 | normFluctMag = normFluctMag + normFluct{i}.^2; 46 | end 47 | 48 | normFluctMag = sqrt(normFluctMag); 49 | outlierIdx = find(normFluctMag > thr); 50 | normFluctValues = normFluctMag(outlierIdx); 51 | 52 | for i = 1:length(u), 53 | u{i}(outlierIdx) = nan; 54 | u{i} = inpaint_nans3(double(u{i}),0); 55 | end 56 | 57 | end 58 | 59 | %% ======================================================================== 60 | function [medianU, normFluct] = funRemoveOutliers(u,epsilon) 61 | 62 | nSize = 3*[1 1 1]; 63 | skipIdx = ceil(prod(nSize)/2); 64 | padOption = 'symmetric'; 65 | 66 | u = inpaint_nans3(double(u),0); 67 | 68 | medianU = medFilt3(u,nSize,padOption,skipIdx); 69 | fluct = u - medianU; 70 | medianRes = medFilt3(abs(fluct),nSize,padOption,skipIdx); 71 | normFluct = abs(fluct./(medianRes + epsilon)); 72 | 73 | end 74 | 75 | %% ======================================================================== 76 | function Vr = medFilt3(V0,nSize, padoption, skipIdx) 77 | % fast median filter for 3D data with extra options. 78 | 79 | if nargin < 4, skipIdx = 0; end 80 | if nargin < 3, padoption = 'symmetric'; end 81 | if nargin < 2, nSize = [3 3 3]; end 82 | 83 | nLength = prod(nSize); 84 | if mod(nLength,2) == 1, padSize = floor(nSize/2); 85 | elseif mod(nLength,2) == 0, padSize = [nSize(1)/2-1,nSize(2)/2]; 86 | end 87 | 88 | if strcmpi(padoption,'none') 89 | V = V0; 90 | else 91 | V = (padarray(V0,padSize(1)*[1,1,1],padoption,'pre')); 92 | V = (padarray(V,padSize(2)*[1,1,1],padoption,'post')); 93 | end 94 | 95 | S = size(V); 96 | nLength = prod(nSize)-sum(skipIdx>1); 97 | Vn = single(zeros(S(1)-(nSize(1)-1),S(2)-(nSize(2)-1),S(3)-(nSize(3)-1),nLength)); % all the neighbor 98 | 99 | %% 100 | % build the neighboor 101 | 102 | i = cell(1,nSize(1)); j = cell(1,nSize(2)); k = cell(1,nSize(3)); 103 | for m = 1:nSize(1), i{m} = m:(S(1)-(nSize(1)-m)); end 104 | for m = 1:nSize(2), j{m} = m:(S(2)-(nSize(2)-m)); end 105 | for m = 1:nSize(3), k{m} = m:(S(3)-(nSize(3)-m)); end 106 | 107 | p = 1; 108 | for m = 1:nSize(1) 109 | for n = 1:nSize(2) 110 | for o = 1:nSize(3) 111 | if p ~= skipIdx || skipIdx == 0 112 | Vn(:,:,:,p) = V(i{m},j{n},k{o}); 113 | end 114 | p = p + 1; 115 | end 116 | end 117 | end 118 | 119 | if skipIdx ~= 0, Vn(:,:,:,skipIdx) = []; end 120 | % perform the processing 121 | Vn = sort(Vn,4); 122 | 123 | if mod(nLength,2) == 1 % if odd get the middle element 124 | Vr = Vn(:,:,:,ceil(nLength/2)); 125 | else % if even get the mean of the two middle elements 126 | Vr = mean(cat(4,Vn(:,:,:,nLength/2),Vn(:,:,:,nLength/2+1)),4); 127 | end 128 | 129 | end 130 | -------------------------------------------------------------------------------- /Matlab Code/volumeMapping.m: -------------------------------------------------------------------------------- 1 | function I = volumeMapping(varargin) 2 | %VOLUMEMAPPING images for 3-D images 3 | % I = volumeMapping(I0,m,u0) symmetrically warps undeformed 4 | % and deformed 3-D images by the displacement field from previous iteration 5 | % using trilinear interpolation. 6 | % 7 | % INPUTS 8 | % ------------------------------------------------------------------------- 9 | % I0: cell containing the undeformed, I0{1}, and deformed, I0{2} 3-D 10 | % images 11 | % m: meshgrid of the displacement field 12 | % u0: displacement field vector defined at every meshgrid point with 13 | % spacing dm. Format: cell array, each containing a 3D matrix 14 | % (components in x,y,z) 15 | % u0{1} = displacement in x-direction 16 | % u0{2} = displacement in y-direction 17 | % u0{3} = displacement in z-direction 18 | % u0{4} = magnitude 19 | % 20 | % OUTPUTS 21 | % ------------------------------------------------------------------------- 22 | % I: cell containing the symmetrically warped images of I0{1} and I0{2} 23 | % 24 | % NOTES 25 | % ------------------------------------------------------------------------- 26 | % interp3FastMex and mirt3D-mexinterp are used to perform the interpolation 27 | % since they are faster and less memory demanding than MATLAB's interp3 28 | % function. To run you need a compatible C compiler. Please see 29 | % (http://www.mathworks.com/support/compilers/R2014a/index.html) 30 | % 31 | % For more information please see section 2.2. 32 | % If used please cite: 33 | % Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast 34 | % iterative digital volume correlation algorithm for large deformations. 35 | % Experimental Mechanics. doi: 10.1007/s11340-014-9874-2 36 | 37 | 38 | [I0,m0,u0] = parseInputs(varargin{:}); 39 | 40 | idx = cell(1,3); idx_u0 = cell(1,3); 41 | for i = 1:3 42 | idx{i} = m0{i}(1):(m0{i}(end) - 1); % get index for valid indices of image 43 | idx_u0{i} = linspace(1,size(u0{1},i),length(idx{i})+1); % get index for displacement meshgrid 44 | idx_u0{i} = idx_u0{i}(1:length(idx{i})); 45 | u0{i} = double(u0{i}*0.5); 46 | end 47 | 48 | [m_u0{2}, m_u0{1}, m_u0{3}] = ndgrid(idx_u0{:}); 49 | [m{2}, m{1}, m{3}] = ndgrid(idx{:}); 50 | 51 | u = cell(1,3); 52 | for i = 1:3, u{i} = mirt3D_mexinterp(u0{i}, m_u0{1}, m_u0{2}, m_u0{3}); end 53 | %% Warp images (see eq. 8) 54 | mForward = cellfun(@(x,y) x + y, m, u, 'UniformOutput',false); 55 | mBackward = cellfun(@(x,y) x - y, m, u, 'UniformOutput',false); 56 | 57 | % interpolate volume based on the deformation 58 | I{1} = mirt3D_mexinterp(I0{1}, mForward{1}, mForward{2}, mForward{3}); 59 | I{2} = mirt3D_mexinterp(I0{2}, mBackward{1}, mBackward{2}, mBackward{3}); 60 | end 61 | 62 | %% ======================================================================== 63 | function varargout = parseInputs(varargin) 64 | I0 = varargin{1}; 65 | m = varargin{2}; 66 | u0 = varargin{3}; 67 | 68 | % convert I0 to double. Other datatypes will produce rounding errors 69 | I0 = cellfun(@double, I0, 'UniformOutput', false); 70 | 71 | varargout{ 1} = I0; 72 | varargout{end + 1} = m; 73 | varargout{end + 1} = u0; 74 | 75 | end 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The Fast Iterative Digital Volume Correlation Algorithm (FIDVC) is the next generation DVC algorithm providing significantly improved signal-to-noise, and large (finite) deformation (incl. large rotations and image stretches) capture capability at low computational cost (please see [Bar-Kochba, Toyjanova et al., Exp. Mechanics, 2014](http://link.springer.com/article/10.1007/s11340-014-9874-2?sa_campaign=email/event/articleAuthor/onlineFirst) for more details). 2 | 3 | ### Important pages 4 | * [Download latest version v1.2.4!](https://github.com/FranckLab/FIDVC/releases) 5 | * [Example data](https://app.globus.org/file-manager?origin_id=86401693-5974-4013-b498-eb4484e08eb4&origin_path=%2FFranckLab%2FFIDVC_example%2F) 6 | * [FAQ](https://github.com/FranckLab/FIDVC#faq) 7 | * [Questions/Issues](https://github.com/FranckLab/FIDVC/issues) 8 | * [Bug Fixes/history](https://github.com/FranckLab/FIDVC/wiki/Bug-Fixes!) 9 | * [Franck Lab](http://franck.engin.brown.edu) 10 | 11 | ## Purpose 12 | The following implementation contains the Matlab m-files for our FIDVC algorithm. The FIDVC algorithm determines the 3D displacement fields between consecutive volumetric image stacks. To provide maximum user versatility the current version is the CPU-based version, which runs a little bit slower (~ 5 - 15 minutes/per stack) than the GPU-based implementation. As Matlab’s GPU-based subroutines become more efficient we hope to provide the GPU-based version at a later release date. 13 | 14 | ## Running FIDVC 15 | 16 | ### C Compiler 17 | To run you need a compatible C compiler. Please see 18 | (http://www.mathworks.com/support/compilers/R2015a/index.html) 19 | 20 | ### Input 3D Image Stack Requirements 21 | * To check if the 3D image stack have the required speckle pattern and intensity values for correlation please use our [DVC simulator](https://github.com/FranckLab/DVC-Simulator). 22 | * The 3D image stack need to be saved in a 3 dimensional matrix (intensity values are stored at x, y and z position) in **vol*.mat** files. 23 | * We recommend that the input image stack at each dimension should have at least 1.5 times of the subset size as the number of pixels. The default subset size is 128x128x64, so we recommend that the minimum input volume size should be 192x192x96. 24 | * The size of the input image stack should be divisible by 0.5 times the size of the subset. 25 | 26 | ### Running included example case 27 | 1. Make sure that the main files and the supplemental m files (from file exchange) are added to the path in Matlab. 28 | 2. Download and save the [example volume data](https://app.globus.org/file-manager?origin_id=86401693-5974-4013-b498-eb4484e08eb4&origin_path=%2FFranckLab%2FFIDVC_example%2F) in the example folder. 29 | 3. Run the exampleRunFile.m file to and compare its displacement outputs to the contour plots in the referenced paper ([Bar-Kochba, Toyjanova et al., Exp. Mechanics, 2014](http://link.springer.com/article/10.1007/s11340-014-9874-2?sa_campaign=email/event/articleAuthor/onlineFirst)) 30 | 31 | ### Health warning! 32 | FIDVC requires a 3D stack to be read in, which depending on the volume size can require a **large amount of RAM** in Matlab. 33 | 34 | ## Files 35 | * Main files 36 | - addDisplacements.m 37 | - checkConvergenceSSD.m 38 | - DVC.m 39 | - filterDisplacements.m 40 | - funIDVC.m 41 | - IDVC.m 42 | - removeOutliers.m 43 | - volumeMapping.m 44 | 45 | * Supplement m files from the MATLAB file exchange: 46 | - gridfit.m 47 | - inpaint_nans.m 48 | - inpaint_nans3.m 49 | - mirt3D_mexinterp.cpp 50 | - mirt3D_mexinterp.m 51 | - mirt3D_mexinterp.mexw64 52 | 53 | * Example Run files 54 | - exampleRunFile.m 55 | - [example volume data](https://drive.google.com/folderview?id=0ByhZqlrbo5srSmU2ZW1TOXpfVkE&usp=sharing) (vol00.mat, vol01.mat, resultsFIDVC.mat, outputREsults.pdf, matlab_workspace.mat). 56 | 57 | ## FAQ 58 | **What are the requirements for the input 3D image stack?** 59 | 60 | Please refer to [input 3D Image Stack Requirements](https://github.com/FranckLab/FIDVC#input-3d-image-stack-requirements). 61 | 62 | **Can I use FIDVC for finding displacement fields in 2D images?** 63 | 64 | No. But you can use [qDIC](https://github.com/FranckLab/qDIC), this is 2D version of FIDVC for finding displacments in 2D images. 65 | 66 | ## Cite 67 | If used please cite: 68 | [Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast iterative digital volume correlation algorithm for large deformations. Experimental Mechanics. doi: 10.1007/s11340-014-9874-2](http://link.springer.com/article/10.1007/s11340-014-9874-2?sa_campaign=email/event/articleAuthor/onlineFirst) 69 | 70 | ```bibtex 71 | @article{bar2014fast, 72 | title={A fast iterative digital volume correlation algorithm for large deformations}, 73 | author={Bar-Kochba, E and Toyjanova, J and Andrews, E and Kim, K-S and Franck, C}, 74 | journal={Experimental Mechanics}, 75 | pages={1--14}, 76 | year={2014}, 77 | publisher={Springer} 78 | } 79 | ``` 80 | 81 | ## Contact and support 82 | For questions, please first refer to [FAQ](https://github.com/FranckLab/FIDVC#faq) and [Questions/Issues](https://github.com/FranckLab/FIDVC/issues) (make sure to look through the closed Issues too!). Add a new question if similar issue hasn't been reported. We shall help you at the earliest. The author's contact information can be found at [Franck Lab](http://franck.engin.brown.edu). 83 | --------------------------------------------------------------------------------