├── test_images ├── shear │ ├── DSC_1875.jpg │ └── DSC_1975.jpg ├── stress │ ├── uniaxial_stress_1000.tif │ ├── uniaxial_stress_1001.tif │ ├── uniaxial_stress_1002.tif │ ├── uniaxial_stress_1003.tif │ └── uniaxial_stress_1004.tif ├── translation │ ├── translation_00.tif │ ├── translation_01.tif │ └── translation_02.tif └── about.txt ├── mirt2D_mexinterp.m ├── img2mat.m ├── README.txt ├── filterDisplacements_2D.m ├── addDisplacements_2D.m ├── areaMapping_2D.m ├── image_eval.m ├── imageCropping.m ├── removeOutliers_2D.m ├── checkConvergenceSSD_2D.m ├── README.md ├── funIDIC.m ├── IDIC.m ├── exampleRunFile.m ├── FIDIC_plot.m ├── DIC.m └── inpaint_nans.m /test_images/shear/DSC_1875.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDIC/HEAD/test_images/shear/DSC_1875.jpg -------------------------------------------------------------------------------- /test_images/shear/DSC_1975.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDIC/HEAD/test_images/shear/DSC_1975.jpg -------------------------------------------------------------------------------- /test_images/stress/uniaxial_stress_1000.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDIC/HEAD/test_images/stress/uniaxial_stress_1000.tif -------------------------------------------------------------------------------- /test_images/stress/uniaxial_stress_1001.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDIC/HEAD/test_images/stress/uniaxial_stress_1001.tif -------------------------------------------------------------------------------- /test_images/stress/uniaxial_stress_1002.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDIC/HEAD/test_images/stress/uniaxial_stress_1002.tif -------------------------------------------------------------------------------- /test_images/stress/uniaxial_stress_1003.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDIC/HEAD/test_images/stress/uniaxial_stress_1003.tif -------------------------------------------------------------------------------- /test_images/stress/uniaxial_stress_1004.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDIC/HEAD/test_images/stress/uniaxial_stress_1004.tif -------------------------------------------------------------------------------- /test_images/translation/translation_00.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDIC/HEAD/test_images/translation/translation_00.tif -------------------------------------------------------------------------------- /test_images/translation/translation_01.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDIC/HEAD/test_images/translation/translation_01.tif -------------------------------------------------------------------------------- /test_images/translation/translation_02.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FranckLab/FIDIC/HEAD/test_images/translation/translation_02.tif -------------------------------------------------------------------------------- /test_images/about.txt: -------------------------------------------------------------------------------- 1 | <<<<<<< HEAD 2 | These test images are synthetically generated. The reference image is seeded with randomly placed "speckles" of gausian 3 | intensity profile. Progressive deformation mimicking uniaxial tension is then simulated. 4 | ======= 5 | Test images for stress and translation synthetically generated. The reference image is seeded with randomly placed "speckles" of gausian 6 | intensity profile. Progressive deformation is simulated by moving pixels approperiately. To test with a simple 7 | translation, place the images from the "translation" directory here, to test a uniaxial stress deformation place 8 | the images in the "stress" directory here. 9 | 10 | Shear images have been experimentally generated. 11 | >>>>>>> 6a19a61272cc489b5d99f972cb08b58dd8b8c7b2 12 | -------------------------------------------------------------------------------- /mirt2D_mexinterp.m: -------------------------------------------------------------------------------- 1 | %MIRT2D_MEXINTERP Fast 2D linear interpolation 2 | % 3 | % ZI = mirt2D_mexinterp(Z,XI,YI) interpolates 2D image Z at the points with coordinates XI,YI. 4 | % Z is assumed to be defined at regular spaced points 1:N, 1:M, where [M,N]=size(Z). 5 | % If XI,YI values are outside the image boundaries, put NaNs in ZI. 6 | % 7 | % The performance is similar to Matlab's ZI = INTERP2(Z,XI,YI,'linear',NaN). 8 | % If Z is a 3D matrix, then iteratively interpolates Z(:,:,1), Z(:,:,2),Z(:,:,3),.. etc. 9 | % This works faster than to interpolate each image independaently, such as 10 | % ZI(:,:,1) = INTERP2(Z(:,:,1),XI,YI); 11 | % ZI(:,:,2) = INTERP2(Z(:,:,2),XI,YI); 12 | % ZI(:,:,3) = INTERP2(Z(:,:,3),XI,YI); 13 | % 14 | % The speed gain is from the precomputation of coefficients for interpolation, which are the same for all images. 15 | % Interpolation of a set of images is useful, e.g. for image registration, when one has to interpolate image and its gradients 16 | % at the same positions. 17 | % 18 | % Andriy Myronenko, Feb 2008, email: myron@csee.ogi.edu, 19 | % homepage: http://www.bme.ogi.edu/~myron/ 20 | 21 | 22 | 23 | % The function below compiles the mirt2D_mexinterp.cpp file if you haven't done it yet. 24 | % It will be executed only once at the very first run. 25 | function Output_images = mirt2D_mexinterp(Input_images, XI,YI) 26 | 27 | pathtofile=which('mirt2D_mexinterp.cpp'); 28 | pathstr = fileparts(pathtofile); 29 | mex(pathtofile,'-outdir',pathstr); 30 | 31 | Output_images = mirt2D_mexinterp(Input_images, XI,YI); 32 | 33 | end -------------------------------------------------------------------------------- /img2mat.m: -------------------------------------------------------------------------------- 1 | function [cellIMG,filename,filt_opt] = img2mat(Folder,ext,smoothing,s) 2 | %Read images and write them out in .mat 3 | 4 | %Load all of the files directory information 5 | files = dir(strcat('.',filesep,Folder,filesep,'*',ext)); 6 | 7 | %Determine the number of files 8 | if nargin<4 9 | s = length(files); 10 | end 11 | 12 | if strcmp(smoothing,'on') 13 | filt_opt = {'gaussian',[3,3],0.5}; 14 | 15 | filter_gauss = fspecial(filt_opt{1},filt_opt{2},filt_opt{3}); 16 | 17 | % Loop through files, reading in alpha-numeric order 18 | for ii = 1:s 19 | READ = imread(strcat(Folder,filesep,files(ii).name)); 20 | %store the image, and do a small amount of gaussian blurring to 21 | %improve contrast gradients 22 | IMG(:,:,ii) = imfilter(double(READ(:,:,1)),filter_gauss,'replicate'); 23 | 24 | % Option to plot the images 25 | % imshow(IMG(:,:,ii)) 26 | % drawnow 27 | end 28 | 29 | else 30 | filt_opt = {'none',[nan,nan],nan}; 31 | % Loop through files, reading in alpha-numeric order 32 | for ii = 1:s 33 | READ = imread(strcat(Folder,filesep,files(ii).name)); 34 | %store the image, and do a small amount of gaussian blurring to 35 | %improve contrast gradients 36 | IMG(:,:,ii) = double(READ(:,:,1)); 37 | 38 | % Option to plot the images 39 | % imshow(IMG(:,:,ii)) 40 | % drawnow 41 | end 42 | 43 | end 44 | % cellIMG = cell(1); 45 | 46 | % 47 | for ii = 1:s 48 | cellIMG{1} = IMG(:,:,ii); %Make a new variable to hold the current 49 | %image, needed for "save" to work properly 50 | filename = strcat('IDIC_image_',num2str(ii+999)); 51 | save(filename,'cellIMG'); 52 | end 53 | 54 | filename = 'IDIC_image*'; 55 | 56 | end 57 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Date: June 24th, 2016 2 | Author: Alexander Landauer 3 | 4 | Description: This package contains files for the Fast Iterative 5 | Digital Image Correlation algorithm 6 | 7 | Adapted from: 8 | Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast 9 | iterative digital volume correlation algorithm for large deformations. 10 | Experimental Mechanics. doi: 10.1007/s11340-014-9874-2 11 | 12 | The main example file to see how the package runs is exampleRunFile.m. 13 | 14 | Notes: Please ensure that the only .mat files in the working directory are those pertaining to the current image set. 15 | Tested on MATLAB 2015a & 2015b for Windows x64, MATLAB 2013a for Windows x64 (algorithm only), and MATLAB 2015b for CentOS 7. For our purposes we have validated the code on the SEM 2014 DIC challenge images; use at your own risk. 16 | 17 | Core files: 18 | addDisplacements_2D.m 19 | areaMapping.m 20 | checkConvergenceSSD_2D.m 21 | DIC.m 22 | filterDisplacements_2D.m 23 | funIDIC.m 24 | IDIC.m 25 | removeOutliers_2D.m 26 | 27 | Example run files: 28 | exampleRunFile.m 29 | imageCropping.m 30 | FIDIC_plot.m 31 | image_eval.m 32 | img2mat.m 33 | 34 | 35 | Files from the MATLAB file exchange: 36 | inpaint_nans.m 37 | mirt2D_mexinterp.m %Optional, not currently in use. 38 | 39 | History: 40 | 0.14 - New image evaluation function: incorperates basic noise foor and displacement resolution assessment 41 | metices in the base workflow. 6/24/16 42 | 0.13 - Release with several improvements: new plotting routine used by default "FIDIC_plot.m" with 43 | built-in region of interest selction for rectangular areas, 44 | added a normalized cross-correlation option (with validation). 05/20/16 45 | 0.12 - New public release with minor updates: use interpn if griddedInterpolant is not found, 46 | tiff2mat is now img2mat. Updated example dataset. 01/08/16 47 | 0.11 - Update of example run file to delete the .mat file generated by tiff2mat.m after use, 07/14/15 48 | 0.10 - Beta release of DIC adaptation, 06/25/15 49 | -------------------------------------------------------------------------------- /filterDisplacements_2D.m: -------------------------------------------------------------------------------- 1 | function u = filterDisplacements_2D(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} = magnitude 17 | % filterSize: size of the filter 18 | % z: filter strength 19 | % 20 | % OUTPUTS 21 | % ------------------------------------------------------------------------- 22 | % u: cell containing the filtered displacement field 23 | % 24 | % NOTES 25 | % ------------------------------------------------------------------------- 26 | % none 27 | % 28 | % For more information please see section 2.2. 29 | % If used please cite: 30 | % Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast 31 | % iterative digital volume correlation algorithm for large deformations. 32 | % Experimental Mechanics. doi: 10.1007/s11340-014-9874-2 33 | % 34 | 35 | % Parse inputs and set defaults 36 | if nargin < 3, z = 0.0075; end 37 | if ~iscell(u0), u0 = {u0}; end 38 | u = cell(size(u0)); 39 | 40 | if z == 0, 41 | u = cellfun(@double, u0, 'UniformOutput',0); % no filter 42 | else 43 | rf = generateFilter(filterSize,z); 44 | 45 | % apply filter using convolution 46 | for i = 1:length(u0), u{i} = double(convn(u0{i}, rf,'same')); end 47 | end 48 | 49 | end 50 | 51 | %% ======================================================================== 52 | function rf = generateFilter(filterSize,z) 53 | % generates the filter convolution filter 54 | l = filterSize; 55 | 56 | [m{1}, m{2}] = ndgrid(-l(1)/2:l(1)/2,-l(2)/2:l(2)/2); 57 | m = cellfun(@abs, m, 'UniformOutput', 0); 58 | 59 | 60 | f1 = (l(1)/2)^z - (m{1}).^z; 61 | f2 = (l(2)/2)^z - (m{2}).^z; 62 | 63 | i{1} = (m{1} >= m{2});% ?? & m{1} >= m{3}); 64 | i{2} = (m{1} < m{2});% ?? & m{2} >= m{3}); 65 | 66 | rf0 = f1.*i{1}+f2.*i{2}; 67 | 68 | rf = rf0/sum(rf0(:)); 69 | 70 | end 71 | -------------------------------------------------------------------------------- /addDisplacements_2D.m: -------------------------------------------------------------------------------- 1 | function [u, du, cc] = addDisplacements_2D(u0,du0,cc0,m0,dm) 2 | % u = addDisplacements(u0,du0,cc0,m0,dm) adds displacements from previous 3 | % iteratations to the current iterate's displacement field (du). 4 | % 5 | % INPUTS 6 | % ------------------------------------------------------------------------- 7 | % u0: past displacement field vector defined at every meshgrid point with 8 | % spacing dm. Format: cell array, each containing a 2D matrix 9 | % (components in x,y) 10 | % u0{1} = displacement in x-direction 11 | % u0{2} = displacement in y-direction 12 | % u0{3} = magnitude 13 | % du0: current displacements as 14 | % du0{1} = displacement in x-direction 15 | % du0{2} = displacement in y-direction 16 | % cc0: cross correlation matrix used to define interpolant locations 17 | % m0: mesh grid parameter 18 | % dm: subset spacing paramter used to set up grids 19 | % 20 | % OUTPUTS 21 | % ------------------------------------------------------------------------- 22 | % u: cell containing the added displacement fields 23 | % du: cell containing interpolated incremental displacements 24 | % cc: cross-correlation matrix interpolated to fit with u and du 25 | % 26 | % NOTES 27 | % ------------------------------------------------------------------------- 28 | % 29 | % If used please cite: 30 | % Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast 31 | % iterative digital volume correlation algorithm for large deformations. 32 | % Experimental Mechanics. doi: 10.1007/s11340-014-9874-2 33 | 34 | for i = 1:2, du0{i} = inpaint_nans(du0{i}); end % remove NaNs if present 35 | 36 | idx = cell(1,2); 37 | for i = 1:2, idx{i} = m0{i}(1):dm:m0{i}(end); end % construct new meshgrid 38 | 39 | [m0_{1}, m0_{2}] = ndgrid(m0{1},m0{2}); 40 | [m{1}, m{2}] = ndgrid(idx{1},idx{2}); 41 | % sample to desired mesh spacing 42 | 43 | try 44 | %Try to interpolate the displacement field with griddedInterpolant. 45 | %This function does not exist on older versions of MATLAB (2011b and 46 | %earlier), so fail over to interpn, which is slower and prone to other 47 | %failures. 48 | du = cell(1,2); 49 | for i = 1:2 50 | F = griddedInterpolant(m0_{1}, m0_{2}, du0{i}, 'spline'); 51 | du{i} = F(m{1},m{2}); 52 | end 53 | 54 | F = griddedInterpolant(m0_{1}, m0_{2}, cc0, 'spline'); 55 | cc = F(m{1},m{2}); 56 | 57 | catch 58 | %Attempt to use interpn instead 59 | disp('Attempting interpn') 60 | du = cell(1,2); 61 | for i = 1:2 62 | du{i} = interpn(m0_{1}, m0_{2}, du0{i}, m{1}, m{2}, 'spline'); 63 | %du{i} = V(m{1},m{2}); 64 | end 65 | 66 | cc = interpn(m0_{1}, m0_{2},cc0,m{1},m{2}, 'linear'); 67 | 68 | end 69 | 70 | if sum(cellfun(@numel,u0)) == 2, u = du; % on first iteration u = du 71 | else 72 | u = cellfun(@plus,u0,du,'UniformOutput',0); % else u^(k) = sum(u^(k-1)) + du (see eq. 7) 73 | 74 | end 75 | 76 | end 77 | -------------------------------------------------------------------------------- /areaMapping_2D.m: -------------------------------------------------------------------------------- 1 | function I = areaMapping_2D(varargin) 2 | % I = areaMapping(I0,m,u0) symmetrically warps undeformed 3 | % and deformed 2-D images by the displacement field from previous iteration 4 | % using trilinear interpolation. 5 | % 6 | % INPUTS 7 | % ------------------------------------------------------------------------- 8 | % I0: cell containing the undeformed, I0{1}, and deformed, I0{2} images 9 | % m: meshgrid of the displacement field 10 | % u0: displacement field vector defined at every meshgrid point with 11 | % spacing dm. Format: cell array, each containing a 2D matrix 12 | % (components in x,y,z) 13 | % u0{1} = displacement in x-direction 14 | % u0{2} = displacement in y-direction\ 15 | % u0{3} = magnitude 16 | % 17 | % OUTPUTS 18 | % ------------------------------------------------------------------------- 19 | % I: cell containing the symmetrically warped images of I0{1} and I0{2} 20 | % 21 | % NOTES 22 | % ------------------------------------------------------------------------- 23 | % if interp2FastMex and mirt2D-mexinterp are used to perform the interpolation 24 | % since they are faster and less memory demanding than MATLAB's interp3 25 | % function. To run you need a compatible C compiler. Please see 26 | % (http://www.mathworks.com/support/compilers/R2014a/index.html) 27 | % 28 | % For more information please see section 2.2. 29 | % If used please cite: 30 | % Bar-Kochba E., Toyjanova J., Andrews E., Kim K., Franck C. (2014) A fast 31 | % iterative digital volume correlation algorithm for large deformations. 32 | % Experimental Mechanics. doi: 10.1007/s11340-014-9874-2 33 | 34 | 35 | [I0,m0,u0] = parseInputs(varargin{:}); 36 | 37 | idx = cell(1,2); idx_u0 = cell(1,2); 38 | for i = 1:2 39 | idx{i} = m0{i}(1):(m0{i}(end) - 1); % get index for valid indices of image 40 | idx_u0{i} = linspace(1,size(u0{1},i),length(idx{i})+1); % get index for displacement meshgrid 41 | idx_u0{i} = idx_u0{i}(1:length(idx{i})); 42 | u0{i} = double(u0{i}*0.5); 43 | end 44 | 45 | [m_u0{2}, m_u0{1}] = ndgrid(idx_u0{:}); 46 | [m{2}, m{1}] = ndgrid(idx{:}); 47 | 48 | u = cell(1,2); 49 | for i = 1:2 50 | u{i} = interp2(u0{i},m_u0{1}, m_u0{2},'spline',NaN); 51 | %u{i} = mirt2D_mexinterp(u0{i}, m_u0{1}, m_u0{2}); %Uses cpp for 52 | %interpolation, minor 53 | %performance gain at 54 | %the cost of using a mex 55 | end 56 | %% Warp images (see eq. 8) 57 | mForward = cellfun(@(x,y) x + y, m, u, 'UniformOutput',false); 58 | mBackward = cellfun(@(x,y) x - y, m, u, 'UniformOutput',false); 59 | 60 | % interpolate images based on the deformation 61 | 62 | I{1} = interp2(I0{1},mForward{1}, mForward{2},'spline',NaN); 63 | I{2} = interp2(I0{2},mBackward{1}, mBackward{2},'spline',NaN); 64 | %I{1} = mirt2D_mexinterp(I0{1}, mForward{1}, mForward{2}); %mex-interp fcns 65 | %I{2} = mirt2D_mexinterp(I0{2}, mBackward{1}, mBackward{2}); 66 | end 67 | 68 | %% ======================================================================== 69 | function varargout = parseInputs(varargin) 70 | I0 = varargin{1}; 71 | m = varargin{2}; 72 | u0 = varargin{3}; 73 | 74 | % convert I0 to double. Other datatypes will produce rounding errors 75 | I0 = cellfun(@double, I0, 'UniformOutput', false); 76 | 77 | varargout{ 1} = I0; 78 | varargout{end + 1} = m; 79 | varargout{end + 1} = u0; 80 | 81 | end 82 | -------------------------------------------------------------------------------- /image_eval.m: -------------------------------------------------------------------------------- 1 | function [noise_percent,spatial_res,CI_disp_mean,no_im] = image_eval(Folder,ext) 2 | %This function performs basic noise floor and spetial resolution analyses 3 | %for images used in the DIC. To use, take several (2+) completely static 4 | %images of the speckle pattern and label these with the keyword "static" in 5 | %the image directory. 6 | % 7 | % VARIABLES OPTIONS 8 | % ------------------------------------------------------------------------- 9 | % Folder: subdirectory containing the series of images on which to run 10 | % the evalution, there should be 2 or more images with "static" 11 | % as part of the filename 12 | % Ext: the file extension of the input images. All images must be of the 13 | % same type. 14 | % 15 | % OUTPUTS 16 | % ------------------------------------------------------------------------- 17 | % noise_percent: percentage of the full-scale range of the image format 18 | % greyscale that the sensor noise occupies 19 | % spatial_res: the spatial resolution that can be expected from the 20 | % algorithm, based on the noise, speckle pattern, optics, 21 | % and other error sources. 22 | % CI_disp_mean: mean confidence interval on the displacement measured. 23 | % This should be centered on zero, unless bias errors exist 24 | % no_im: flag indicating that no "static"-labeled images were found 25 | % 26 | % NOTES 27 | % - 28 | % ------------------------------------------------------------------------- 29 | 30 | %% Retrieve images 31 | 32 | %Load all of the file's directory information 33 | files = dir(strcat(Folder,'/*static*',ext)); 34 | l = length(files); 35 | 36 | %Only procede if evaluation images are present 37 | if l == 0 38 | %set failure flag 39 | no_im = 1; 40 | noise_percent = nan; 41 | spatial_res = nan; 42 | CI_disp_mean = nan; 43 | else 44 | 45 | no_im = 0; 46 | 47 | %read in the image sequence 48 | for ii = 1:l 49 | READ = imread(strcat(Folder,'/',files(ii).name)); 50 | full_images(:,:,ii) = double(READ(:,:,1)); 51 | end 52 | 53 | %find the bitdepth of the images 54 | S = whos('READ'); 55 | if strcmp(S.class,'uint8') 56 | depth = 256; 57 | elseif strcmp(S.class,'uint16') 58 | depth = 65536; 59 | else 60 | depth = max(full_images(:)); 61 | end 62 | clear READ 63 | 64 | %% Select eval region 65 | imagesc(full_images(:,:,1)) 66 | title('Select noise evaluation ROI. Define two points: top left and bottom right') 67 | axis('image'); colormap gray 68 | [X,Y] = ginput(2); 69 | X = ceil(X); 70 | Y = ceil(Y); 71 | close 72 | 73 | images = full_images(Y(1):Y(2),X(1):X(2),:); 74 | 75 | %% Noise level 76 | %mean_image = mean(image,3); 77 | std_image = std(images,0,3); 78 | 79 | noise_level = std(std_image(:)); 80 | 81 | noise_percent = noise_level/depth*100; 82 | 83 | %% Spatial resolution 84 | 85 | %set up parameters 86 | image_pair = cell(1,2); 87 | image_pair{1} = images(:,:,1); 88 | image_pair{2} = images(:,:,2); 89 | 90 | subset_size = [64,64]; 91 | 92 | u0 = cell(1,2); 93 | u0{1} = 0; 94 | u0{2} = 0; 95 | 96 | norm_xcc = 'u'; 97 | 98 | %Do itereative DIC between the identical images 99 | [u,~,~,~] = IDIC(image_pair,subset_size,u0,norm_xcc); 100 | 101 | %Compute spatial resolutions 102 | spatial_res(1) = std2(u{1}); 103 | spatial_res(2) = std2(u{2}); 104 | spatial_res(3) = std2(u{3}); 105 | 106 | z = 1.96; 107 | %Compute the confidence interval on the displacements 108 | CI_disp(:,:,1) = u{3} - z*spatial_res(3)/sqrt(2); 109 | CI_disp_mean(1) = mean2(CI_disp(:,:,1)); 110 | CI_disp(:,:,2) = u{3} + z*spatial_res(3)/sqrt(2); 111 | CI_disp_mean(2) = mean2(CI_disp(:,:,2)); 112 | 113 | end 114 | -------------------------------------------------------------------------------- /imageCropping.m: -------------------------------------------------------------------------------- 1 | function [crop_nw_loc,folder_out,fmt] = imageCropping(folder_in,ext_in,sSize,max_def_idx,crop) 2 | %This function crops the input images to include only the region of 3 | %interest 4 | % 5 | % INPUTS 6 | % ------------------------------------------------------------------------- 7 | % folder_in: folder containing orginal images 8 | % ext_in: image formate extention 9 | % folder_in: folder containing orginal images 10 | % sSize: interrogation window (subset) size 11 | % max_def_index: string specifying where the max deformation occurs 12 | % use 'center' or 'c' for the center image, 13 | % 'end' or 'e' for the last image, 14 | % 'beginning' or 'b' for the first, 15 | % or specific with an integer 16 | % crop: string to specify whether to crop images, set to 'y' or 'yes' to crop 17 | % OUTPUTS 18 | % ------------------------------------------------------------------------- 19 | % crop_nw_loc: location of the northwest corner of the cropped region 20 | % folder_out: the location where the images were placed 21 | 22 | 23 | %% Setup 24 | 25 | %Output variables 26 | fmt = 'tif'; 27 | folder_out = strcat(folder_in,filesep,'cropped_images',filesep); 28 | ext_out = strcat('.',fmt); 29 | 30 | %Make a new output folder if none exists 31 | if exist(folder_out,'dir') ~= 7 32 | mkdir(folder_out); 33 | end 34 | 35 | prefixes = cell(1,26^3); 36 | alphab = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',... 37 | 'p','q','r','s','t','u','v','w','x','y','z'}; 38 | 39 | kk = 0; 40 | for hh = 1:length(alphab) 41 | for ii = 1:length(alphab) 42 | for jj = 1:length(alphab) 43 | kk = kk + 1; 44 | prefixes{kk} = strcat(alphab{hh},alphab{ii},alphab{jj}); 45 | end 46 | end 47 | end 48 | 49 | %% Read in image filenames 50 | files = dir(strcat(folder_in,filesep,'*',ext_in)); 51 | l = length(files); 52 | 53 | %% Get Cropping Region 54 | %% Get Cropping Region 55 | if strcmp(crop, 'yes')||strcmp(crop, 'y') 56 | if strcmp(max_def_idx,'center')||strcmp(max_def_idx,'c') 57 | im_loc = ceil(l/2); 58 | elseif strcmp(max_def_idx,'end')||strcmp(max_def_idx,'e') 59 | im_loc = l; 60 | elseif strcmp(max_def_idx,'beginning')||strcmp(max_def_idx,'b') 61 | im_loc = 1; 62 | end 63 | figure 64 | imagesc(imread(strcat(folder_in,filesep,files(im_loc).name))) 65 | title('Click to select cropping region. Define two points: top left and bottom right') 66 | axis('image'); colormap gray 67 | [X,Y] = ginput(2); 68 | X = ceil(X); 69 | Y = ceil(Y); 70 | X_ss(1) = X(1) - mod(X(1),max(sSize))+max(sSize); %place the point such that an 71 | %interger number of subsets is used 72 | %Crop agressively. 73 | X_ss(2) = X(2) - mod(X(2),max(sSize)); 74 | Y_ss(1) = Y(1) - mod(Y(1),max(sSize))+max(sSize); 75 | Y_ss(2) = Y(2) - mod(Y(2),max(sSize)); 76 | close 77 | 78 | crop_nw_loc = [X_ss(1),Y_ss(1)]; 79 | 80 | else 81 | crop_nw_loc = [1,1]; 82 | X_ss(1) = 1; 83 | X_ss(2) = size(imread(strcat(folder_in,filesep,files(1).name)),2); 84 | Y_ss(1) = 1; 85 | Y_ss(2) = size(imread(strcat(folder_in,filesep,files(1).name)),1); 86 | 87 | end 88 | 89 | %% Crop and write out files 90 | 91 | image_idx = 1:l; 92 | % Loop through files 93 | for ii = 1:length(image_idx) 94 | READ = imread(strcat(folder_in,filesep,files(image_idx(ii)).name)); 95 | try 96 | READ = rgb2gray(READ); 97 | catch 98 | end 99 | IMG = READ(Y_ss(1):Y_ss(2),X_ss(1):X_ss(2),1); %Cropped size from ginput 100 | 101 | dir_filename = strcat(folder_out,prefixes{image_idx(ii)},'_image_number_',... 102 | num2str(image_idx(ii)),ext_out); %use prefix to ensure proper ordering 103 | imwrite(IMG,dir_filename,fmt); %Write the file with the specified settings 104 | end 105 | 106 | -------------------------------------------------------------------------------- /removeOutliers_2D.m: -------------------------------------------------------------------------------- 1 | function [u,normFluctValues] = removeOutliers_2D(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_nans(double(u{i}),0); 55 | end 56 | 57 | end 58 | 59 | %% ======================================================================== 60 | function [medianU, normFluct] = funRemoveOutliers(u,epsilon) 61 | 62 | nSize = 2*[1 1]; 63 | skipIdx = ceil(prod(nSize)/2); 64 | padOption = 'symmetric'; 65 | 66 | u = inpaint_nans(double(u),0); 67 | 68 | medianU = medFilt2(u,nSize,padOption,skipIdx); 69 | fluct = u - medianU; 70 | medianRes = medFilt2(abs(fluct),nSize,padOption,skipIdx); 71 | normFluct = abs(fluct./(medianRes + epsilon)); 72 | 73 | end 74 | 75 | %% ======================================================================== 76 | function Vr = medFilt2(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 = [2 2]; 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],padoption,'pre')); 92 | V = (padarray(V,padSize(2)*[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),nLength)); % all the neighbor 98 | 99 | %% 100 | % build the neighboor 101 | 102 | i = cell(1,nSize(1)); j = cell(1,nSize(2)); 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 | 106 | p = 1; 107 | for m = 1:nSize(1) 108 | for n = 1:nSize(2) 109 | if p ~= skipIdx || skipIdx == 0 110 | Vn(:,:,p) = V(i{m},j{n}); 111 | end 112 | p = p + 1; 113 | end 114 | end 115 | 116 | if skipIdx ~= 0, Vn(:,:,skipIdx) = []; end 117 | % perform the processing 118 | Vn = sort(Vn,3); 119 | 120 | if mod(nLength,2) == 1 % if odd get the middle element 121 | Vr = Vn(:,:,ceil(nLength/2)); 122 | else % if even get the mean of the two middle elements 123 | Vr = mean(cat(3,Vn(:,:,nLength/2),Vn(:,:,nLength/2+1)),4); 124 | end 125 | 126 | end 127 | -------------------------------------------------------------------------------- /checkConvergenceSSD_2D.m: -------------------------------------------------------------------------------- 1 | function [converged01, SSE1 , sSize1, sSpacing1] = checkConvergenceSSD_2D(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 pixels in length 64 | sSize1(sSize1 < 16) = 16; 65 | 66 | % window spacing refinement. Only do if the sSpacing > 8 pixels 67 | if (sSpacing0 > 8) 68 | sSpacing1 = sSize1/2; 69 | end 70 | 71 | if prod(single(sSpacing1 == 16)) % condition if spacing = 16 72 | 73 | idx = (find(prod(single(sSpacing == 16),2))-1):iteration; 74 | if length(idx) > 2 75 | dSSE = diff(SSE(idx)); % calculate difference 76 | dSSE = dSSE/dSSE(1); % normalize difference 77 | 78 | % if dSSE meets first convergence criteria then refine spacing 79 | % to the minimum value, 8 voxels. 80 | if dSSE(end) <= convergenceCrit(1) 81 | sSize1 = sSize0; sSpacing1 = [8 8]; 82 | 83 | end 84 | end 85 | 86 | % condition if spacing is the minimum, 8 voxels 87 | elseif prod(single(sSpacing1 == 8)) 88 | idx = (find(prod(single(sSpacing == 8),2))-1):iteration; 89 | 90 | if length(idx) > 2 91 | dSSE = diff(SSE(idx)); 92 | dSSE = dSSE/dSSE(1); 93 | 94 | % if dSSE meets first convergence criteria and spacing is the 95 | % mimumum then convergence has been met and stop all 96 | % iterations. 97 | if dSSE(end) <= convergenceCrit(2) 98 | sSize1 = sSize0; sSpacing1 = sSpacing0; 99 | converged01 = 1; 100 | end 101 | end 102 | end 103 | 104 | end 105 | 106 | % global threshold criteria 107 | if SSE(end)/SSE(1) < convergenceCrit(3), converged01 = 1; end 108 | 109 | end 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Notice: the most up-to-date and maintained version of FIDIC is [qDIC](https://github.com/FranckLab/qDIC)! 2 | 3 | The Fast Iterative Digital Image Correlation Algorithm (FIDIC) is a 2D version of FIDVC algorithm (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) to find dispalcements fields in a 2D image. 4 | 5 | * [Download latest version v1.1!](https://github.com/FranckLab/FIDIC/releases) 6 | * [FAQ](https://github.com/FranckLab/FIDIC/blob/master/README.md#faq) 7 | * [Questions/Issues](https://github.com/FranckLab/FIDIC/issues) 8 | * [Bug Fixes/history](https://github.com/FranckLab/FIDIC/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 FIDIC algorithm along with synthetic example images. The FIDIC algorithm determines the 2D displacement fields between consecutive images or from a static reference image to a current image. 13 | 14 | ## Running FIDIC 15 | 16 | ### Software Requirement 17 | MATLAB 2011b (for "griddedInterpolant") and the associated Image Processing Toolbox (for other miscellaneous function calls) are the minimum supported requirements to run this code. Under some circimstances older versions may function using "interpn", but performance and/or accuracy may suffer. Our development is currently under Matlab 2015b on CentOS 7 and Window 7 x64. 18 | 19 | ### Input Image Requirements 20 | * To check if the images have the required speckle pattern and intensity values for correlation please use our [DIC simulator](https://github.com/FranckLab/DIC-Simulator). 21 | * We recommend that the input image stack should have at least 3 times the subset size as the number of pixels in each dimension. The default subset size is 64x64, meaning the the minimum input image size should be 192x192. 22 | * Non-square images are acceptable 23 | * The fundamental image type used for input is .mat 24 | * Out-of-the-box FIDIC supports TIF images with `img2mat.m`, other file formats require simple modification 25 | 26 | ### Running including example case 27 | 1. Make sure that the main files and the supplemental m files (from file exchange) are in the current (working) directory for MATLAB. 28 | 2. Copy the desired test images from the `stress` or `translation` subdirectories to the `test_images` directory. 29 | 3. Run the `exampleRunFile.m` file to get 2D displacement fields between the two images. Note that the displacement output is in the form of either a three pixel translation or a generic uniaxial tension test, depending on the test image set selected. 30 | * We recommend that the input image size in each dimension be at least three times the size of the subset size. The default subset size is 64x64, so we recommend that the minimum input image size should be 192x192. 31 | 32 | ### Running including example case 33 | 1. Make sure that the main files and the supplemental m-files (from file exchange) are in the working directory on Matlab. 34 | 2. Run the exampleRunFile.m file to and compare its displacement outputs to the contour plots expectation for each image type. 35 | 36 | ## Files 37 | * Function files 38 | - addDisplacements_2D.m 39 | - checkConvergenceSSD_2D.m 40 | - DIC.m 41 | - filterDisplacements_2D.m 42 | - funIDIC.m 43 | - IDIC.m 44 | - removeOutliers_2D.m 45 | - areaMapping_2D.m 46 | 47 | * Supplemental .m files from the MATLAB file exchange: 48 | - inpaint_nans.m 49 | - mirt2D_mexinterp.m (Optional, not currently in use) 50 | 51 | * Example files to run basic DIC 52 | - exampleRunFile.m 53 | - img2mat.m 54 | - imageCropping.m 55 | - FIDIC_plot.m 56 | - image_eval.m 57 | - Example test images 58 | 59 | ## FAQ 60 | 61 | **What are the requirements for the input images?** 62 | 63 | Please refer to [input image requirement](https://github.com/FranckLab/FIDIC#input-image-requirements). 64 | 65 | **Can I use FIDIC for finding displacement fields in 3D images?** 66 | 67 | No. But you can use [FIDVC](https://github.com/FranckLab/FIDVC), this finds 3D displacements in 3D image stack. 68 | 69 | **Why does the example fail to run?** 70 | 71 | In many cases where the example images fail to run, the minium specifications for MATLAB have not been met. 72 | 73 | ## Cite 74 | If used please cite: 75 | [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) 76 | 77 | ```bibtex 78 | @article{bar2014fast, 79 | title={A fast iterative digital volume correlation algorithm for large deformations}, 80 | author={Bar-Kochba, E and Toyjanova, J and Andrews, E and Kim, K-S and Franck, C}, 81 | journal={Experimental Mechanics}, 82 | pages={1--14}, 83 | year={2014}, 84 | publisher={Springer} 85 | } 86 | ``` 87 | 88 | ## Contact and support 89 | For questions, please first refer to [FAQ](https://github.com/FranckLab/FIDIC#faq) and [Questions/Issues](https://github.com/FranckLab/FIDIC/issues). 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). 90 | -------------------------------------------------------------------------------- /funIDIC.m: -------------------------------------------------------------------------------- 1 | function [u, cc, dm] = funIDIC(varargin) 2 | % u = funIDIC(filename, sSize, incORcum) is the main function that performs 3 | % IDIC on a time increment of images. 4 | % 5 | % INPUTS 6 | % ------------------------------------------------------------------------- 7 | % filename: string for the filename prefix for the 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 IDIC 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 IDIC. 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) 37 | % u{time}{1} = displacement in x-direction 38 | % u{time}{2} = displacement in y-direction 39 | % u{time}{3} = magnitude 40 | % cc: peak values of the cross-correlation for each interrogation point 41 | % dm: meshgrid spacing (8 by default) 42 | % 43 | % NOTES 44 | % ------------------------------------------------------------------------- 45 | % none 46 | % 47 | % For more information please see 48 | % 49 | 50 | %% ---- Opening & Reading the First Image into CPU Memory ---- 51 | [fileInfo, sSize0, incORcum, u_, u_c, norm_xcc] = parseInputs(varargin{:}); 52 | 53 | I{1} = loadFile(fileInfo,1); 54 | 55 | %% ---- Opening and Reading Subsequent Images --- 56 | numImages = length(fileInfo.filename); 57 | u = cell(numImages-1,1); uc = cell(numImages-1,1); cc = cell(numImages-1,1); 58 | for i = 2:numImages % Reads images starting on the second image 59 | tStart = tic; 60 | I{2} = loadFile(fileInfo,i); 61 | 62 | %Start DIC 63 | disp(['Current file: ' fileInfo.filename{i}]) 64 | [u_, u_c, cc{i-1}, dm] = IDIC(I,sSize0,u_c,norm_xcc); 65 | 66 | % Saving iterations of the DIC 67 | u{i-1}{1} = -u_{1}; u{i-1}{2} = -u_{2}; u{i-1}{3} = u_{3}; 68 | uc{i-1}{1} = -u_c{1}; uc{i-1}{2} = -u_c{2}; 69 | 70 | u_ = num2cell(zeros(1,2)); 71 | if strcmpi(incORcum(1),'i') == 1 72 | I{1} = I{2}; 73 | else 74 | u_c = uc{i-1}(1:2); 75 | end 76 | 77 | disp(['Elapsed Time for all iterations: ',num2str(toc(tStart))]); 78 | 79 | end 80 | 81 | end 82 | 83 | %=================================================================== 84 | function I = loadFile(fileInfo,idx) 85 | 86 | I = load(fileInfo.filename{idx}); 87 | fieldName = fieldnames(I); 88 | I = getfield(I,fieldName{1}); 89 | if iscell(I), 90 | if numel(I), I = I{1}; 91 | else 92 | I = I{fileInfo.dataChannel}; 93 | end 94 | end 95 | end 96 | 97 | %================================================================ 98 | function varargout = parseInputs(varargin) 99 | % = parseInputs(filename, sSize, incORcum) 100 | 101 | 102 | % Parse filenames 103 | filename = varargin{1}; 104 | if iscell(filename) 105 | if length(filename) == 1, fileInfo.datachannel = 1; 106 | else fileInfo.datachannel = filename{2}; 107 | end 108 | filename = filename{1}; 109 | end 110 | 111 | 112 | [~,filename,~] = fileparts(filename); 113 | filename = dir([filename,'.mat']); 114 | fileInfo.filename = {filename.name}; 115 | 116 | if isempty(fileInfo), error('File name doesn''t exist'); end 117 | 118 | % Ensure dimensionality of the subset size 119 | sSize = varargin{2}; 120 | if numel(sSize) == 1, 121 | sSize = sSize*[1 1]; 122 | elseif numel(sSize) ~=2, 123 | error('Subset size must be a scalar or a two column array'); 124 | end 125 | 126 | % Ensure range of subset size 127 | if min(sSize) < 32 || max(sSize > 128) 128 | error('Subset size must be within 32 and 128 pixels'); 129 | end 130 | 131 | % Ensure even subset size 132 | % if sum(mod(sSize,4)) > 0 133 | % error('Subset size must be even'); 134 | % end 135 | 136 | if sum(mod(sSize,32)) ~= 0 137 | error('Subset size must be 32, 64, 96, or 128 pixels in each dimension'); 138 | end 139 | 140 | % Check run method input 141 | incORcum = varargin{3}; 142 | 143 | switch lower(incORcum) 144 | case 'cum', incORcum = 'cumulative'; 145 | case 'inc', incORcum = 'incremental'; 146 | case 'c', incORcum = 'cumulative'; 147 | case 'i', incORcum = 'incremental'; 148 | case 'incremental', incORcum = 'incremental'; 149 | case 'cumulative', incORcum = 'incremental'; 150 | otherwise, error('Run method must be incremental or cumulative'); 151 | end 152 | 153 | norm_xcc = varargin{4}; 154 | 155 | % Initial guess of displacement field = [0 0]; 156 | u0 = num2cell(zeros(1,2)); 157 | uc0 = num2cell(zeros(1,2)); 158 | % Outputs 159 | varargout{ 1} = fileInfo; 160 | varargout{end + 1} = sSize; 161 | varargout{end + 1} = incORcum; 162 | varargout{end+1} = u0; 163 | varargout{end+1} = uc0; 164 | varargout{end+1} = norm_xcc; 165 | 166 | end 167 | -------------------------------------------------------------------------------- /IDIC.m: -------------------------------------------------------------------------------- 1 | function [u, u_c, cc, dm] = IDIC(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} images 13 | % sSize: interrogation window (subset) size 14 | % u0: pre-estimated displacement field (typically zeros) 15 | % 16 | % OUTPUTS 17 | % ------------------------------------------------------------------------- 18 | % u: displacement field vector defined at every meshgrid point with 19 | % spacing dm. Format: cell array, each containing a 3D matrix 20 | % (components in x,y) 21 | % u{1} = displacement in x-direction 22 | % u{2} = displacement in y-direction 23 | % u{3} = magnitude 24 | % cc: peak values of the cross-correlation for each interrogation 25 | % dm: meshgrid spacing (8 by default) 26 | % 27 | % NOTES 28 | % ------------------------------------------------------------------------- 29 | % To run you need a compatible C compiler. Please see 30 | % (http://www.mathworks.com/support/compilers/R2014a/index.html) 31 | % 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 | wb = waitbar(0,'Parsing Inputs','Name','Running IDIC'); 39 | 40 | % PRESET CONSTANTS 41 | maxIterations = 20; % maximum number of iterations 42 | dm = 8; % desired output mesh spacing 43 | convergenceCrit = [0.25, 0.5, 0.0625]; % convergence criteria 44 | ccThreshold = 1e-4; % bad cross-correlation threshold 45 | 46 | [I0, sSize, sSpacing, padSize, DICPadSize, u, norm_xcc] = parseInputs(varargin{:}); 47 | 48 | % START ITERATING 49 | i = 2; converged01 = 0; SSE = []; I = I0; 50 | 51 | t0 = tic; 52 | while ~converged01 && i - 1 < maxIterations 53 | ti = tic; 54 | 55 | set(wb,'name',['Running IDIC (Iteration ',num2str(i-1),')']); 56 | waitbar(0/7,wb,'Checking Convergence'); 57 | % Check for convergence 58 | [converged01, SSE(i-1) , sSize(i,:), sSpacing(i,:)] = checkConvergenceSSD_2D(I,SSE,sSize,sSpacing,convergenceCrit); 59 | 60 | if ~converged01 61 | [I, m] = parseImages(I,sSize(i,:),sSpacing(i,:)); 62 | 63 | % run cross-correlation to get an estimate of the displacements 64 | [du, cc] = DIC(I,sSize(i,:),sSpacing(i,:),DICPadSize,ccThreshold,norm_xcc); 65 | 66 | % add the displacements from previous iteration to current 67 | waitbar(3/7,wb,'Adding displacements from previous iteration'); 68 | [u, ~, cc] = addDisplacements_2D(u,du,cc,m,dm); 69 | 70 | % filter the displacements using a predictor filter 71 | waitbar(4/7,wb,'Filtering Displacements'); 72 | u = filterDisplacements_2D(u,sSize(i,:)/dm); 73 | 74 | % remove outliers in displacement field 75 | waitbar(5/7,wb,'Removing Outliers'); 76 | u = removeOutliers_2D(u); 77 | 78 | % mesh and pad images based on new subset size and spacing 79 | [I, m] = parseImages(I0,sSize(i,:),sSpacing(i,:)); 80 | 81 | % map areas based on displacment field 82 | waitbar(6/7,wb,'Warping Images'); 83 | I = areaMapping_2D(I,m,u); 84 | 85 | disp(['Elapsed time (iteration ',num2str(i-1),'): ',num2str(toc(ti))]); 86 | i = i + 1; 87 | end 88 | 89 | end 90 | 91 | u_c = u; 92 | [u,cc] = parseOutputs(u,cc,dm,padSize); 93 | 94 | delete(wb); 95 | 96 | disp(['Convergence at iteration ',num2str(i)]); 97 | disp(['Total time: ',num2str(toc(t0))]); 98 | end 99 | 100 | 101 | 102 | %% ======================================================================== 103 | function varargout = parseImages(varargin) 104 | % pads images and creates meshgrid 105 | 106 | I{1} = single(varargin{1}{1}); 107 | I{2} = single(varargin{1}{2}); 108 | sSize = varargin{2}; 109 | sSpacing = varargin{3}; 110 | 111 | prePad = sSize/2; 112 | postPad = sSize/2; 113 | 114 | sizeI = size(I{1}); 115 | I{1} = padarray(I{1},prePad,0,'pre'); 116 | I{1} = padarray(I{1},postPad,0,'post'); 117 | 118 | I{2} = padarray(I{2},prePad,0,'pre'); 119 | I{2} = padarray(I{2},postPad,0,'post'); 120 | 121 | 122 | idx = cell(1,2); 123 | for i = 1:2, idx{i} = (1:sSpacing(i):(sizeI(i) + 1)) + sSize(i)/2; end 124 | 125 | % [m{1},m{2}] = meshgrid(idx{:}); 126 | 127 | varargout{ 1} = I; 128 | varargout{end+1} = idx; 129 | 130 | end 131 | 132 | %% ======================================================================== 133 | function varargout = parseInputs(varargin) 134 | % parses inputs and pads images so that there is an divisable meshgrid number. 135 | 136 | I0{1} = single(varargin{1}{1}); 137 | I0{2} = single(varargin{1}{2}); 138 | 139 | % I0{1} = permute(I0{1},[2 1]); 140 | % I0{2} = permute(I0{2},[2 1]); 141 | 142 | sSize = varargin{2}; 143 | sSize = [sSize(2), sSize(1)]; 144 | 145 | sSpacing = sSize/2; 146 | u0 = varargin{3}; 147 | 148 | norm_xcc = varargin{4}; 149 | 150 | DICPadSize = sSpacing/2; 151 | 152 | sizeI0 = size(I0{1}); 153 | sizeI = ceil(sizeI0./sSpacing).*sSpacing; 154 | prePad = ceil((sizeI - sizeI0)/2); 155 | postPad = floor((sizeI - sizeI0)/2); 156 | 157 | I{1} = padarray(I0{1},prePad,0,'pre'); 158 | I{1} = padarray(I{1},postPad,0,'post'); 159 | 160 | I{2} = padarray(I0{2},prePad,0,'pre'); 161 | I{2} = padarray(I{2},postPad,0,'post'); 162 | 163 | varargout{ 1} = I; 164 | varargout{end+1} = sSize; 165 | varargout{end+1} = sSpacing; 166 | varargout{end+1} = [prePad; postPad]; 167 | varargout{end+1} = DICPadSize; 168 | varargout{end+1} = u0; 169 | varargout{end+1} = norm_xcc; 170 | end 171 | 172 | 173 | function [u,cc] = parseOutputs(u,cc,filterSpacing,padSize) 174 | % parses outputs and unpads the displacment field and cc. 175 | 176 | unpadSize(1,:) = ceil(padSize(1,:)/filterSpacing); 177 | unpadSize(2,:) = floor((padSize(2,:)+1)/filterSpacing); 178 | % +1 from the extra meshgrid point during the meshing of the DIC algorithm. 179 | 180 | for i = 1:2 181 | u{i} = u{i}(1+unpadSize(1,1):end-unpadSize(2,1),... 182 | 1+unpadSize(1,2):end-unpadSize(2,2)); 183 | end 184 | u{3} = sqrt(u{1}.^2 + u{2}.^2); 185 | 186 | cc = cc(1+unpadSize(1,1):end-unpadSize(2,1),... 187 | 1+unpadSize(1,2):end-unpadSize(2,2)); 188 | 189 | end 190 | -------------------------------------------------------------------------------- /exampleRunFile.m: -------------------------------------------------------------------------------- 1 | %% Example run file for the IDIC 2 | % VARIABLES OPTIONS 3 | % ------------------------------------------------------------------------- 4 | % imagesFolder: subdirectory containing the series of images on which to run 5 | % FIDIC. Images are read in alphanumeric order, which is 6 | % assumed to be consistent with time steps. 7 | % Ext: the file extension of the input images. All images must be of the 8 | % same type. 9 | % numImage: optional parameter to pass to img2mat limiting the number of 10 | % images processed from the subdirectory identified in imagesFolder 11 | % sSize: interrogation window (subset) size for the first iterations. 12 | % Must be 32,64,96, or 128 pixels and a two column 13 | % array (one for each dimenision) or scalar (equal for all 14 | % dimensions). 15 | % incORcum: string that defines the method of running IDIC. Options: 16 | % cumulative (time0 -> time1, time0 -> time2, ...) 17 | % (Allowable inputs: 'c','cum','cumulative') 18 | % or 19 | % incremental (time0 -> time1, time1 -> time2, ...) 20 | % (Allowable inputs: 'i','inc','incremental') 21 | % 22 | % OUTPUTS 23 | % ------------------------------------------------------------------------- 24 | % u: displacement field vector calculated from FIDIC. Format: cell array, 25 | % which is a 3D vector (components in x,y) per each time point 26 | % (units are in pixels) 27 | % u{time}{1} = displacement in x-direction at t=time of size MxNxP 28 | % u{time}{2} = displacement in y-direction at t=time of size MxNxP 29 | % u{time}{3} = displacement magnitude at t=time of size MxNxP 30 | % cc: peak values of the cross-correlation for each interrogation 31 | % dm: final subset spacing in px 32 | % 33 | % NOTES 34 | % ------------------------------------------------------------------------- 35 | %% Set up workspace and images 36 | 37 | clear; close all; clc; 38 | %dbstop if error 39 | 40 | sSize = [64 64]; 41 | incORcum = 'c'; %use 'i' for incremental mode and 'c' for cumulative 42 | norm_xcc = 'u'; %use 'norm' for normalized cross-correlation, considerable time-cost 43 | ext_in = 'tif'; %Input image format 44 | folder_in = ['.',filesep,'test_images']; 45 | max_def_idx = 'b'; %Specify where the max deformation occurs 46 | yn = 'y'; 47 | %use 'center' or 'c' for the center image, 48 | %'end' or 'e' for the last image, 49 | %'beginning' or 'b' for the first, 50 | %or specific with an integer 51 | 52 | %Compute basic noise floor and measurement resultion metrics 53 | [noise_percent,meas_res,CI_disp_mean,no_im] = image_eval(folder_in,ext_in); 54 | 55 | %Write outputs to screen and confirm run 56 | if no_im == 0 57 | clc 58 | fprintf('IMAGE QUALITY DIAGNOSTICS\n-----------------------------------------\n') 59 | fprintf('Noise level: %0.2g%% \n',noise_percent) 60 | fprintf('Measurement resolution, x: %0.2gpx\n',meas_res(1)) 61 | fprintf('Measurement resolution, y: %0.2gpx\n',meas_res(2)) 62 | fprintf('Displacement confidence interval (95%%): %0.2g < %0.2gpx < %0.2g\n',... 63 | CI_disp_mean(1),(CI_disp_mean(1)-CI_disp_mean(2))/2,CI_disp_mean(2)) 64 | fprintf('-----------------------------------------\n') 65 | yn = input('Are these acceptable? (y/n)[y] \n','s'); 66 | 67 | if isempty(yn);yn='y';end 68 | if strcmp(yn(1),'n') || strcmp(yn(1),'N') 69 | return; 70 | end 71 | 72 | else 73 | fprintf('IMAGE QUALITY DIAGNOSTICS UNAVAILABLE\n') 74 | fprintf('NO STATIC IMAGES: NOISE AND RESOLUTION UNKNOWN\n') 75 | end 76 | 77 | %Image cropping to get a region of interest on the specimen 78 | [crop_nw_loc,folder_out] = imageCropping(folder_in,ext_in,sSize,max_def_idx,'on'); 79 | 80 | ext_crp = 'tif'; %output image file form, defined in image_cropping.m 81 | resultsFolder = ['.',filesep,'Results',filesep]; 82 | numImages = 3; 83 | 84 | %Convert input images to .mat and smooth 85 | [cellIMG,filename,filt_opt] = img2mat(folder_out,ext_crp,'on'); %All images in "imagesFolder" 86 | % [cellIMG,filename] = img2mat(folder_out,ext_crp,numImages); %Images 1 to 87 | %numImages only 88 | 89 | %% RUNNING DIC 90 | 91 | % Estimate displacements via IDIC 92 | [u, cc, dm] = funIDIC(filename, sSize, incORcum, norm_xcc); 93 | 94 | % Save the results 95 | if exist(resultsFolder,'dir') ~= 7 96 | mkdir(resultsFolder) 97 | end 98 | 99 | if no_im == 0 100 | %Build the reporting table struct array 101 | prefilt_str = strcat(filt_opt{1},', ',num2str(filt_opt{2}),', ',num2str(filt_opt{3})); 102 | reporting_table = struct('cameraNoise',noise_percent,'prefiltering',prefilt_str,... 103 | 'subset',sSize,'step',dm,'xcorrType',norm_xcc,'interpolent','spline',... 104 | 'numMeasurementPts',numel(u{1}{1}),'totalImages',length(u)+1,... 105 | 'displacementSpatialRes',mean(sSize),'displacementResX',meas_res(1),... 106 | 'displacementResY',meas_res(2)); 107 | %Save relavent workspace variables 108 | save(strcat(resultsFolder,'resultsFIDIC.mat'),'u','cc','cellIMG','dm','reporting_table'); 109 | else 110 | %Save relavent workspace variables 111 | save(strcat(resultsFolder,'resultsFIDIC.mat'),'u','cc','dm'); 112 | end 113 | 114 | %% PLOTTING 115 | close all; 116 | 117 | def_udef = 'udef'; % set to 'udef' to plot on undeformed images, 118 | % or 'def' to plot on deformed images 119 | 120 | % Run the plotting routine function 121 | FIDIC_plot(u,dm,def_udef,crop_nw_loc,folder_in,ext_in) 122 | 123 | %save the last figure 124 | saveas(gcf,strcat(resultsFolder,'FIDIC_plots.fig')) 125 | 126 | if no_im == 0 127 | %% Reporting Table 128 | fprintf('\n-----------------------------------------\n'); 129 | fprintf('Run Parameters and Measurement Specifications\n') 130 | fprintf('-----------------------------------------\n'); 131 | fprintf('Camera Noise \t\t %0.2g%%\n',noise_percent); 132 | fprintf('Prefiltering \t\t %s, %0.2gx%0.2g, %0.2g\n',filt_opt{1},... 133 | filt_opt{2}(1),filt_opt{2}(2),filt_opt{3}); 134 | fprintf('Subset \t\t %0.2g by %0.2gpx\n',sSize(1),sSize(2)); 135 | fprintf('Step \t\t %0.2gpx\n',dm); 136 | fprintf('Correlation type %s\n',norm_xcc); 137 | fprintf('Interpolation \t\t Spline\n'); 138 | fprintf('Measurement points \t %0.2g\n',numel(u{1}{1})); 139 | fprintf('Total images \t\t %0.2g\n',length(u)+1); 140 | fprintf('Displacement\n Spatial resolution \t %0.2gpx \n ',mean(sSize)); 141 | fprintf('Measurement res, x %0.2g\n ',meas_res(1)); 142 | fprintf('Measurement res, y %0.2g\n',meas_res(2)); 143 | fprintf('-----------------------------------------\n'); 144 | else 145 | end 146 | %% CLEAN UP 147 | %Clean up the current set of images from the cd 148 | delete *IDIC_image*.mat 149 | delete(strcat(folder_out,'*.',ext_crp)); 150 | -------------------------------------------------------------------------------- /FIDIC_plot.m: -------------------------------------------------------------------------------- 1 | function [] = FIDIC_plot(u,dm,def_udef,crop_nw_loc,imagesFolder,ext_in) 2 | %This function constructs overlay plots showing the displacement contours 3 | %on the undeformed or deformed images 4 | % 5 | % INPUTS 6 | % ------------------------------------------------------------------------- 7 | % u: cell array of the displacement fields 8 | % dm: final subset spacing from FIDIC 9 | % def_udef: flag to specify plotting on deformed or undeformed images 10 | % crop_nw_loc: northwest corner location of the crop 11 | % imagesFolder: Directory where the complete images reside 12 | 13 | %% Set up local vars 14 | numInc = max(size(u)); 15 | scrsz = get(0,'ScreenSize'); 16 | sizeI = size(u{1}{1}); 17 | 18 | if strcmp(def_udef,'def') 19 | im_idx_offset = 1; 20 | else 21 | im_idx_offset = 0; 22 | end 23 | 24 | %% Read in image filenames 25 | files = dir(strcat(imagesFolder,filesep,'*',ext_in)); 26 | l = length(files); 27 | 28 | %% plot IDIC results 29 | 30 | for jj = 1:numInc 31 | 32 | %read in the associated image 33 | cur_img = imread(strcat(imagesFolder,filesep,files(jj+im_idx_offset).name)); 34 | try 35 | cur_img = rgb2gray(cur_img); 36 | catch 37 | end 38 | img_size = size(cur_img); 39 | disp_img = nan*zeros(img_size); 40 | % disp_mag_ave = mean(u{jj}{3}(:)); 41 | % disp_mag_std = std(u{jj}{3}(:)); 42 | % disp_mag_range = max(max(medfilt2(u{jj}{3}))) - min(min(medfilt2(u{jj}{3}))); 43 | 44 | figure 45 | set(gcf,'position',[150,150,scrsz(3)*(7/8),scrsz(4)*3/4]) 46 | for ii = 3:-1:1 47 | try 48 | %Set up axes for the image and for the contour plot 49 | a1 = axes; 50 | set(a1,'Units','Pixels') 51 | 52 | a2 = axes; 53 | set(a2,'Units','Pixels') 54 | 55 | % if ii < 3 56 | subplot(1,3,ii,a1); 57 | subplot(1,3,ii,a2); 58 | % end 59 | 60 | format short 61 | 62 | %get the displacement data for the current case, filter and mask it 63 | u_ = medfilt2(u{jj}{ii}); 64 | u_([1:dm/2,(end-dm/2+1:end)],:) = nan; 65 | u_(:,[1:dm/2,(end-dm/2+1:end)]) = nan; %Crop (some) edge effects 66 | 67 | 68 | u_upscale = upsampleImage(u_,dm); 69 | %set up the image of the displacements, padded to the size of the 70 | %orginal images 71 | disp_img(crop_nw_loc(2):(crop_nw_loc(2)+size(u_upscale,1)-1),... 72 | crop_nw_loc(1):(crop_nw_loc(1)+size(u_upscale,2)-1)) = u_upscale; 73 | 74 | %Set up the alpha-belnding mask so that the contour underneath the 75 | %image can be seen 76 | alpha_data = ones(img_size); 77 | %alpha_data((crop_nw_loc(2)+dm*dm/2):(crop_nw_loc(2)+size(u_upscale,1)-dm*dm/2-1),... 78 | % (crop_nw_loc(1)+dm*dm/2):(crop_nw_loc(1)+size(u_upscale,2)-dm*dm/2)-1) = 0.5; 79 | 80 | alpha_data(alpha_data>0.5) = 0.65; 81 | 82 | %Find scaling parameters needed for the colorbar 83 | disp_mag_ave = nanmean(u_(:)); 84 | disp_mag_std = nanstd(u_(:)); 85 | if ii == 3 86 | disp_mag_range = max(u_(:)) - min(u_(:)); 87 | tick_labels = (disp_mag_ave-disp_mag_range):... 88 | disp_mag_range/2:(disp_mag_ave+disp_mag_range); 89 | end 90 | 91 | %Do the image plotting 92 | set(gcf,'currentaxes',a2) 93 | p = imshow(cur_img); 94 | set(p,'AlphaData',alpha_data); %use the predefined transparency mask 95 | axis image 96 | 97 | %Do the contour plotting 98 | set(gcf,'currentaxes',a1) 99 | [~,h] = contourf(disp_img); % flip the contour about x and y 100 | % to be in the same reference 101 | % frame as the image 102 | set(h,'linestyle','none'); 103 | colormap('parula') 104 | axis image 105 | 106 | %set appropriate colormasks 107 | colormap(a1,'parula') 108 | colormap(a2,'gray') 109 | 110 | %Save the current axis position 111 | set(gcf,'CurrentAxes',a1) 112 | pos_a1 = get(a1,'position'); 113 | pos_a2 = get(a2,'position'); 114 | pos_a1(3) = pos_a1(3) - 50; 115 | pos_a2(3) = pos_a2(3) - 50; 116 | 117 | %set up the plots with contours and colorbar tick scaled by the 118 | %magnitude of the range in the Disp. Mag. plot. 119 | lvl_step = disp_mag_range/6; %Set number of contour levels by step size 120 | if ii == 3 121 | title('Displacement magnitude') 122 | 123 | %Define and set the contour plot levels and colorbar levels 124 | lvls_3 = (disp_mag_ave-disp_mag_range):... 125 | lvl_step:(disp_mag_ave+disp_mag_range); 126 | h.LevelList = lvls_3; 127 | colorbar('Ticks',lvls_3,'Limits',[lvls_3(1),lvls_3(end)]) 128 | set(a1,'position',pos_a1); % restore position 129 | set(a2,'position',pos_a2); % restore position 130 | 131 | elseif ii == 2 132 | title(['Displacement component u_',num2str(ii)]) 133 | 134 | %Set the levels, using the step sizes from the magitude, but 135 | %center from the current plot 136 | lvls_2 = (disp_mag_ave-disp_mag_range):... 137 | lvl_step:(disp_mag_ave+disp_mag_range); 138 | h.LevelList = lvls_2; 139 | colorbar('Ticks',lvls_2) 140 | set(a1,'position',pos_a1); % restore position 141 | set(a2,'position',pos_a2); % restore position 142 | 143 | elseif ii == 1 144 | title(['Displacement component u_',num2str(ii)]) 145 | lvls_1 = (disp_mag_ave-disp_mag_range):... 146 | lvl_step:(disp_mag_ave+disp_mag_range); 147 | h.LevelList = lvls_1; 148 | colorbar('Ticks',lvls_1); 149 | set(a1,'position',pos_a1); % restore position 150 | set(a2,'position',pos_a2); % restore position 151 | end 152 | 153 | %label the axes 154 | xlabel('X_1 [px]'); ylabel('X_2 [px]'); 155 | 156 | % set(gcf,'CurrentAxes',a1) 157 | % p=getframe; %get a frame from the current axes. 158 | % image(p.cdata); % display the data as an image 159 | % axis image; 160 | % impixelinfo; % turn on pixel information 161 | % set(a1,'position',pos_a1); % restore position 162 | % set(a2,'position',pos_a2); % restore position 163 | catch 164 | end 165 | end 166 | end 167 | 168 | function [I] = upsampleImage(comp_image, samp_scale) 169 | %This function takes in an image and upsamples it by building blocks of 170 | %pixels. It takes in a compressed image and scaling factor to generate a 171 | %final image as output 172 | 173 | 174 | if nargin < 2 175 | samp_scale = 8; 176 | end 177 | 178 | l = size(comp_image,1); 179 | h = size(comp_image,2); 180 | L = l*samp_scale; 181 | H = h*samp_scale; 182 | I = zeros(L,H); 183 | 184 | for ii = 1:l 185 | for jj = 1:h 186 | 187 | I(((ii-1)*samp_scale+1):(ii*samp_scale),... 188 | ((jj-1)*samp_scale+1):(jj*samp_scale)) = comp_image(ii,jj); 189 | 190 | end 191 | end 192 | -------------------------------------------------------------------------------- /DIC.m: -------------------------------------------------------------------------------- 1 | function [u, cc] = DIC(varargin) 2 | % [du, cc] = DIC(I,sSize,sSpacing,ccThreshold) estimates 3 | % displacements between two images through digital image 4 | % correlation. 5 | % 6 | % INPUTS 7 | % ------------------------------------------------------------------------- 8 | % I: cell containing the undeformed, I{1}, and deformed, I{2} 2-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:2} = {u_x, u_y}) 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,norm_xcc] = parseInputs(varargin{:}); 30 | 31 | % Initialize variables 32 | mSize_ = prod(mSize); 33 | u12 = zeros(mSize_,2); 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 | tStart = tic; % begin timer 44 | %----------------------------------------------------------------------- 45 | % grab the moving subset from the images 46 | subst = I{1}(m{1}(k,:),m{2}(k,:)); 47 | B = I{2}(m{1}(k,:),m{2}(k,:)); 48 | 49 | % multiply by the modular transfer function to alter frequency content 50 | subst = MTF.*subst; B = MTF.*B; 51 | 52 | % run cross-correlation 53 | if strcmp(norm_xcc,'n')||strcmp(norm_xcc,'norm')||strcmp(norm_xcc,'normalized') 54 | 55 | subst(isnan(subst)) = 0; 56 | B(isnan(B)) = 0; 57 | 58 | A_ = normxcorr2(subst,B); 59 | A = imrotate(A_(size(B,1)/2:size(B,1)+size(B,1)/2-1, size(B,2)/2:size(B,2)+size(B,2)/2-1),180); 60 | 61 | % imagesc(A); pause(0.01) 62 | % find maximum index of the cross-correlaiton 63 | [cc(k), maxIdx] = max(A(:)); 64 | 65 | % compute pixel resolution displacements 66 | [u1, u2] = ind2sub(sSize,maxIdx); 67 | 68 | % gather the 3x3 pixel neighborhood around the peak 69 | try xCorrPeak = reshape(A(u1 + (-1:1), u2 + (-1:1)),9,1); 70 | % least squares fitting of the peak to calculate sub-pixel displacements 71 | du12 = lsqPolyFit2(xCorrPeak, M{1}, M{2}); 72 | u12(k,:) = [u1 u2] + du12' - (sSize/2); 73 | %----------------------------------------------------------------------- 74 | catch 75 | u12(k,:) = nan; 76 | end 77 | 78 | else 79 | 80 | A = xCorr2(subst,B,sSize); %Custom fft-based correllation - very fast, 81 | %but not normalized. Be careful using this 82 | %if there is a visable intensity gradient in 83 | %an image 84 | 85 | % imagesc(A); pause(0.01) 86 | % find maximum index of the cross-correlaiton 87 | [cc(k), maxIdx] = max(A(:)); 88 | 89 | % compute pixel resolution displacements 90 | [u1, u2] = ind2sub(sSize,maxIdx); 91 | 92 | % gather the 3x3 pixel neighborhood around the peak 93 | try xCorrPeak = reshape(A(u1 + (-1:1), u2 + (-1:1)),9,1); 94 | % least squares fitting of the peak to calculate sub-pixel displacements 95 | du12 = lsqPolyFit2(xCorrPeak, M{1}, M{2}); 96 | u12(k,:) = [u1 u2] + du12' - (sSize/2) - 1; 97 | %----------------------------------------------------------------------- 98 | catch 99 | u12(k,:) = nan; 100 | end 101 | 102 | end 103 | 104 | 105 | 106 | % waitbar calculations (update only every 100 iterations) 107 | if rem(k,100) == 0 108 | tRemaining = (toc(tStart)*(mSize_ - k)); % Time remaining for waitbar 109 | waitbar(1/7*(k/mSize_ + 1),wb,['Estimating Displacements (Time Remaining: ',... 110 | datestr(datenum(0,0,0,0,0,tRemaining),'MM:SS'),')']) 111 | end 112 | 113 | end 114 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 115 | % Reshape displacements and set bad correlations to zero 116 | waitbar(2/7,wb,'Removing Bad Correlations') 117 | 118 | cc = reshape(double(cc),mSize); 119 | [cc, ccMask] = removeBadCorrelations(I,cc,ccThreshold,norm_xcc); 120 | 121 | u{1} = reshape(double(u12(:,2)),mSize).*ccMask; 122 | u{2} = reshape(double(u12(:,1)),mSize).*ccMask; 123 | 124 | end 125 | 126 | %% ======================================================================== 127 | function varargout = parseInputs(varargin) 128 | % Parse inputs and create meshgrid 129 | 130 | I{1} = varargin{1}{1}; 131 | I{2} = varargin{1}{2}; 132 | sSize = varargin{2}; 133 | sSpacing = varargin{3}; 134 | padSize = varargin{4}; 135 | ccThreshold = varargin{5}; 136 | norm_xcc = varargin{6}; 137 | 138 | % pad images with zeros so that we don't grab any subset outside of the image 139 | % domain. This would produce an error 140 | I{1} = padarray(I{1},padSize,0,'both'); 141 | I{2} = padarray(I{2},padSize,0,'both'); 142 | sizeV = size(I{1}); 143 | 144 | % Initialize Mesh Variables 145 | idx = cell(1,2); 146 | for i = 1:2, idx{i} = (1+padSize(i)) : sSpacing(i) : (sizeV(i)-sSize(i)-padSize(i)+1); end 147 | [m{1},m{2}] = ndgrid(idx{:}); 148 | 149 | 150 | % sSize = [sSize(2) sSize(1)]; 151 | mSize = size(m{1}); 152 | mSize_ = prod(mSize); 153 | 154 | m_ = cell(1,2); 155 | for k = 1:2, 156 | m_{k} = zeros([mSize_,sSize(k)],'uint16'); 157 | repmat_ = repmat((1:sSize(k))-1,mSize_,1); 158 | m_{k} = bsxfun(@plus, repmat_,m{k}(:)); 159 | end 160 | 161 | % Initialize quadratic least squares fitting coefficients 162 | [mx, my] = meshgrid((-1:1),(-1:1)); 163 | m = [mx(:), my(:)]; 164 | 165 | M{1} = zeros(size(m,1),6); 166 | for i = 1:size(m,1) 167 | x = m(i,1); y = m(i,2); 168 | M{1}(i,:) = [1,x,y,x^2,x*y,y^2]; 169 | end 170 | 171 | M{2} = M{1}'*M{1}; 172 | 173 | % Generate Moduluar transfer function (see eq. 3) 174 | [~,~,MTF] = generateMTF(sSize); 175 | 176 | %% Parse outputs 177 | 178 | varargout{ 1} = I; 179 | varargout{end+1} = m_; 180 | varargout{end+1} = mSize; 181 | varargout{end+1} = sSize; 182 | varargout{end+1} = MTF; 183 | varargout{end+1} = M; 184 | varargout{end+1} = ccThreshold; 185 | varargout{end+1} = norm_xcc; 186 | end 187 | 188 | %% ======================================================================== 189 | function A = xCorr2(A,B,sSize) 190 | % performs fft based cross correlation of A and B (see equation 2) 191 | 192 | A = fftn(A,sSize); 193 | B = fftn(B,sSize); 194 | B = conj(B); 195 | A = A.*B; 196 | A = ifftn(A); 197 | A = real(A); 198 | A = fftshift(A); 199 | 200 | end 201 | 202 | %% ======================================================================== 203 | function duvw = lsqPolyFit2(b, M, trMM) 204 | % LeastSqPoly performs a polynomial fit in the least squares sense 205 | % Solves M*x = b, 206 | % trMM = transpose(M)*M 207 | % trMb = tranpose(M)*b 208 | % 209 | % If you need to generate the coefficients then uncomment the following 210 | % [mx, my, mz] = meshgrid(-1:1,-1:1,-1:1); 211 | % m = [mx(:), my(:), mz(:)]; 212 | % 213 | % for i = 1:size(m,1) 214 | % x = m(i,1); y = m(i,2); z = m(i,3); 215 | % M1(i,:) = [1,x,y,z,x^2,x*y,x*z,y^2,y*z,z^2]; %3D case 216 | % 1 2 3 4 5 6 7 8 9 10 217 | 218 | % M1(i,:) = [1,x,y,x^2,x*y,y^2]; %2D Case 219 | % 1 2 3 5 6 8 220 | % 1 2 3 4 5 6 221 | % end 222 | % 223 | % trMM1 = M'*M; 224 | 225 | % b = log(b); 226 | trMb = sum(bsxfun(@times, M, b)); 227 | 228 | x = trMM\trMb'; %solve for unknown coefficients 229 | 230 | A = [x(5), 2*x(4); 231 | 2*x(6), x(5)]; 232 | 233 | duvw = (A\(-x([2 3]))); 234 | 235 | end 236 | 237 | %% ======================================================================== 238 | function varargout = generateMTF(sSize) 239 | % MTF functions taken from 240 | % J. Nogueira, A Lecuona, P. A. Rodriguez, J. A. Alfaro, and A. Acosta. 241 | % Limits on the resolution of correlation PIV iterative methods. Practical 242 | % implementation and design of weighting functions. Exp. Fluids, 243 | % 39(2):314{321, July 2005. doi: 10.1007/s00348-005-1017-1 244 | 245 | %% equation 4 246 | 247 | if prod(single(sSize == 32)) 248 | sSize = sSize(1); 249 | 250 | x = cell(1,3); 251 | [x{1}, x{2}] = meshgrid(1:sSize,1:sSize); 252 | 253 | nu{1} = 1; 254 | for i = 1:2 255 | x{i} = x{i} - sSize/2 - 0.5; 256 | x{i} = abs(x{i}/sSize); 257 | nu{1} = nu{1}.*(3*(4*x{i}.^2-4*x{i}+1)); 258 | end 259 | 260 | %% equation 5 261 | [x{1}, x{2}] = meshgrid(1:sSize,1:sSize); 262 | 263 | for i = 1:2, x{i} = x{i} - sSize/2 - 0.5; end 264 | 265 | r = abs(sqrt(x{1}.^2 + x{2}.^2)/sSize); 266 | nu{2} = zeros(size(r)); 267 | nu{2}(r < 0.5) = 24/pi*(4*r(r < 0.5).^2-4*r(r < 0.5)+1); 268 | 269 | %% equation 6 270 | [x{1}, x{2}] = meshgrid(1:sSize,1:sSize); 271 | 272 | nu{3} = 1; 273 | for i = 1:2, 274 | x{i} = x{i} - sSize/2 - 0.5; 275 | x{i} = (x{i}/sSize); 276 | 277 | nu{3} = nu{3}.*(12*abs(x{i}).^2 - 12*abs(x{i}) + 3 + ... 278 | 0.15*cos(4*pi*x{i}) + 0.20*cos(6*pi*x{i}) + ... 279 | 0.10*cos(8*pi*x{i}) + 0.05*cos(10*pi*x{i})); 280 | 281 | end 282 | nu{3}(nu{3} < 0) = 0; 283 | 284 | else 285 | 286 | nu{1} = ones(sSize(1),sSize(2)); 287 | nu{2} = nu{1}; 288 | nu{3} = nu{1}; %==== Based on the usage and the paper this comes from 289 | %nu should remain 3-dims even for the 2D case 290 | 291 | end 292 | 293 | nu = cellfun(@(x) x/sum(x(:)), nu, 'UniformOutput',0); 294 | nu = cellfun(@sqrt, nu, 'UniformOutput',0); 295 | 296 | varargout = nu; 297 | 298 | end 299 | 300 | %% ======================================================================== 301 | function [cc, ccMask] = removeBadCorrelations(I,cc,ccThreshold,norm_xcc) 302 | % removes bad correlations. You can insert your own method here. 303 | if strcmp(norm_xcc,'n')||strcmp(norm_xcc,'norm')||strcmp(norm_xcc,'normalized') 304 | 305 | ccMask = ones(size(cc)); 306 | % use a threshold that's slightly less than (so that we can accept 307 | % all datapoints in a "good" image pair) two st devs less than the mean 308 | threshold1 = nanmean(cc(:)) - 2*nanstd(cc(:)) - 500*ccThreshold; 309 | threshold2 = 0.5; 310 | ccMask(cc < threshold1) = nan; 311 | ccMask(cc < threshold2) = nan; 312 | cc = cc.*ccMask; 313 | 314 | else 315 | minOS = 1; 316 | for i = 1:2 317 | zeroIdx = I{i} == 0; 318 | threshold = mean2(I{i}(~zeroIdx)); 319 | I_ = I{i}.*(I{i} < threshold); 320 | minOS = minOS*sum(I_(:))/sum(~zeroIdx(:)); 321 | end 322 | cc = cc - minOS; 323 | cc = cc/(max(I{1}(:))*max(I{2}(:))); 324 | ccMask = double(cc >= ccThreshold); 325 | 326 | CC = bwconncomp(~ccMask); 327 | if length(CC.PixelIdxList) > 0 328 | [~,idx] = max(cellfun(@numel,CC.PixelIdxList)); 329 | ccMask(CC.PixelIdxList{idx}) = inf; 330 | end 331 | ccMask(cc == 0) = nan; 332 | ccMask(~isfinite(ccMask)) = 0; 333 | cc = cc.*ccMask; 334 | 335 | end 336 | 337 | end 338 | -------------------------------------------------------------------------------- /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 --------------------------------------------------------------------------------