├── out.m ├── README.md ├── ncorr_util_isrealbb.m ├── ncorr_util_isintbb.m ├── ncorr_util_wrapcallbacktrycatch.m ├── ncorr_util_colormap.m ├── license.txt ├── ncorr_util_figpos.m ├── ncorr_util_properimgfmt.m ├── ncorr_util_loadsavedimg.m ├── ncorr_alg_interpqbs.m ├── ncorr_alg_formboundary.cpp ├── ncorr_alg_testopenmp.cpp ├── ncorr_alg_addanalysis.m ├── standard_datatypes.h ├── ncorr_datatypes.h ├── ncorr_lib.h ├── ncorr_alg_extrapdata.cpp ├── ncorr_gui_loadroi.m ├── ncorr_alg_formunion.cpp ├── ncorr_alg_formregions.cpp ├── ncorr_util_loadimgs.m ├── ncorr_util_formregionconstraint.m ├── ncorr_alg_seedanalysis.m ├── ncorr_alg_convertanalysis.m ├── standard_datatypes.cpp ├── ncorr_alg_formthreaddiagram.cpp ├── ncorr_alg_adddisp.cpp ├── ncorr_alg_dispgrad.cpp ├── ncorr_alg_formmask.cpp ├── ncorr_alg_dicanalysis.m ├── ncorr_gui_setrois.m └── ncorr_gui_setseeds.m /out.m: -------------------------------------------------------------------------------- 1 | classdef out 2 | % This is basically acting as an enumeration and should be called like: 3 | % (outstate == out.success) to see if output of a function was either 4 | % successful, failed, or was cancelled. 5 | 6 | properties(Constant,Access = private) 7 | s = 1; % success 8 | f = 0; % failed 9 | c = -1; % cancelled 10 | end 11 | 12 | methods(Static) 13 | function i = success() 14 | i = out.s; 15 | end 16 | 17 | function i = failed() 18 | i = out.f; 19 | end 20 | 21 | function i = cancelled() 22 | i = out.c; 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ncorr_2D_matlab 2 | 3 | This is the offical repo for: 4 | 5 | ``` 6 | Ncorr: open-source 2D digital image correlation matlab software 7 | J Blaber, B Adair, A Antoniou 8 | Experimental Mechanics 55 (6), 1105-1122 9 | ``` 10 | 11 | Please cite this paper if you use this software in your research. 12 | 13 | # Installation instructions 14 | 15 | ``` 16 | git clone https://github.com/justinblaber/ncorr_2D_matlab.git 17 | ``` 18 | 19 | Then, in MATLAB: 20 | 21 | ``` 22 | >> cd ncorr_2D_matlab 23 | >> addpath(pwd); 24 | >> handles_ncorr = ncorr; 25 | ``` 26 | 27 | If MEX is set up properly, the MEX files associated with the program *should* compile automagically and a GUI will appear. For more information, please visit the associated website at: [https://www.ncorr.com](https://www.ncorr.com) 28 | 29 | For convienience, a direct link to the manual is available [here](http://www.ncorr.com/download/ncorrmanual_v1_2_2.pdf) and an instructional step-by-step youtube video is linked below: 30 | 31 | [![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/cXfeiBXjN18/0.jpg)](http://www.youtube.com/watch?v=cXfeiBXjN18) 32 | 33 | 34 | -------------------------------------------------------------------------------- /ncorr_util_isrealbb.m: -------------------------------------------------------------------------------- 1 | function outstate = ncorr_util_isrealbb(x,low,high,name) 2 | % Tests if x is a real number between and including bounds low and high. 3 | % 4 | % Inputs -----------------------------------------------------------------% 5 | % x - double; can be NaN, inf, a special character, complex, or a regular 6 | % real number. 7 | % low - double; lower bound 8 | % high - double; upper bound 9 | % name - string; used to display errordlg if number is not between bounds 10 | % 11 | % Outputs ----------------------------------------------------------------% 12 | % outstate - integer; returns either out.cancelled, out.failed, or out.success. 13 | % 14 | % Note that x needs to be the output from the str2double function. 15 | 16 | % Initialize output 17 | outstate = out.failed; 18 | 19 | if (isfinite(x) && isreal(x) && x >= low && x <= high) 20 | outstate = out.success; 21 | else 22 | h_error = errordlg([name ' must be a real number greater than or equal to ' num2str(low) ' and less than or equal to ' num2str(high) '.'],'Error','modal'); 23 | uiwait(h_error); 24 | end 25 | end 26 | 27 | -------------------------------------------------------------------------------- /ncorr_util_isintbb.m: -------------------------------------------------------------------------------- 1 | function outstate = ncorr_util_isintbb(x,low,high,name) 2 | % Tests if x is an integer between and including bounds low and high. 3 | % 4 | % Inputs -----------------------------------------------------------------% 5 | % x - double; can be NaN, inf, a special character, complex, or a regular 6 | % real number. 7 | % low - integer; lower bound 8 | % high - integer; upper bound 9 | % name - string; used to display errordlg if number is not between bounds 10 | % 11 | % Outputs ----------------------------------------------------------------% 12 | % outstate - integer; returns either out.cancelled, out.failed, or out.success. 13 | % 14 | % Note that x needs to be the output from the str2double function. 15 | 16 | % Initialize output 17 | outstate = out.failed; 18 | 19 | if (isfinite(x) && isreal(x) && mod(x,1) == 0 && x >= low && x <= high) 20 | outstate = out.success; 21 | else 22 | h_error = errordlg([name ' must be an integer greater than or equal to ' num2str(low) ' and less than or equal to ' num2str(high) '.'],'Error','modal'); 23 | uiwait(h_error); 24 | end 25 | end 26 | 27 | -------------------------------------------------------------------------------- /ncorr_util_wrapcallbacktrycatch.m: -------------------------------------------------------------------------------- 1 | function handle_wrapcallbacktrycatch = ncorr_util_wrapcallbacktrycatch(handle_callback,handle_figure) 2 | % This function is a wrapper that wraps the callback in a try-catch 3 | % statement. If an error is thrown after the figure handle is closed, then 4 | % disregard the error; if not, then rethrow it. The reason for this function 5 | % is that closing a figure always interrupts a function, which can cause it 6 | % to throw an error. Make sure to only throw the error if the figure which 7 | % the function is attached to is still open. 8 | % 9 | % Inputs -----------------------------------------------------------------% 10 | % handle_callback - function handle; 11 | % handle_figure - figure handle; 12 | % 13 | % Outputs ----------------------------------------------------------------% 14 | % handle_wrapcallbacktrycatch - function handle; 15 | 16 | handle_wrapcallbacktrycatch = @wrapcallbacktrycatch; 17 | 18 | function wrapcallbacktrycatch(varargin) 19 | try 20 | handle_callback(varargin{:}); 21 | catch err 22 | if (ishandle(handle_figure)) 23 | rethrow(err); 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /ncorr_util_colormap.m: -------------------------------------------------------------------------------- 1 | function ncorr_util_colormap(handle_fig) 2 | % This function adjusts the colormap correctly. This was taken from some 3 | % matlab code. 4 | % 5 | % Inputs -----------------------------------------------------------------% 6 | % handle_fig - handle; handle of the figure; 7 | % 8 | % Outputs ----------------------------------------------------------------% 9 | % none; 10 | 11 | m = size(get(handle_fig,'colormap'),1); 12 | n = ceil(m/4); 13 | u = [(1:1:n)/n ones(1,n-1) (n:-1:1)/n]'; 14 | g = ceil(n/2) - (mod(m,4)==1) + (1:length(u))'; 15 | r = g + n; 16 | b = g - n; 17 | g(g>m) = []; 18 | r(r>m) = []; 19 | b(b<1) = []; 20 | J = zeros(m,3); 21 | J(r,1) = u(1:length(r)); 22 | J(g,2) = u(1:length(g)); 23 | J(b,3) = u(end-length(b)+1:end); 24 | 25 | if verLessThan('matlab','9.1') 26 | set(handle_fig,'Colormap',J); 27 | else 28 | % Get all axes and set their colormap. This is a fix for 2016b+ 29 | % (9.1+) which now sets colormaps on a per-axis basis for imshow() 30 | % instead of for the entire figure. 31 | handle_axes = findobj(handle_fig,'type','axes'); 32 | for i = 1:length(handle_axes) 33 | colormap(handle_axes(i),J); 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Justin Blaber 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in 12 | the documentation and/or other materials provided with the distribution 13 | * Neither the name of the Georgia Institute of Technology nor the names 14 | of its contributors may be used to endorse or promote products derived 15 | from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /ncorr_util_figpos.m: -------------------------------------------------------------------------------- 1 | function pos_child = ncorr_util_figpos(pos_parent,size_child) 2 | % This function receives the position of the parent, size of the child, and 3 | % then returns the position of the child. pos_parent should be obtained through 4 | % the outerposition property. Units for inputs and calculations are characters. 5 | % 6 | % Inputs -----------------------------------------------------------------% 7 | % pos_parent - integer array; position of parent figure - 8 | % [left bottom width height]. 9 | % size_child - integer array; size of the child figure - [height width]; 10 | % 11 | % Outputs ----------------------------------------------------------------% 12 | % pos_child - integer array; position of the child figure 13 | 14 | % Get size of screen in character units 15 | set(0,'units','characters'); % Set this every time incase user changes units of root 16 | pos_screen = get(0,'screensize'); 17 | 18 | % Get child figure position - Make it an offset from the parent 19 | offset_x = 5; 20 | offset_y = 6; 21 | 22 | pos_child(1) = pos_parent(1)+offset_x; 23 | pos_child(2) = pos_parent(2)+pos_parent(4)-size_child(1)-offset_y; 24 | pos_child(3) = size_child(2); 25 | pos_child(4) = size_child(1); 26 | 27 | % Check if right side extends beyond the screen 28 | % Only check right and bottom since figures are shifted to the bottom 29 | % right of their parent (assuming parent is on screen) 30 | right_screen = 10; % Cushion from right of the screen 31 | bottom_screen = 6; % Cushion from bottom of the screen 32 | 33 | % Right 34 | if (pos_child(1)+pos_child(3) > pos_screen(3)-right_screen) 35 | % Set so right side touches side of the screen minus the offset 36 | pos_child(1) = pos_screen(3)-size_child(2)-right_screen; 37 | end 38 | 39 | % Bottom 40 | if (pos_child(2) < bottom_screen) 41 | % Set so bottom side touches bottom of the screen plus the offset 42 | pos_child(2) = bottom_screen; 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /ncorr_util_properimgfmt.m: -------------------------------------------------------------------------------- 1 | function outstate = ncorr_util_properimgfmt(img,name) 2 | % This function determines if the input images (either a numeric array or a 3 | % string input of the filename with path) have the correct format. The 4 | % string allows us to test for saved images without explicitly loading them, 5 | % while the array allows us to test images which were loaded through the 6 | % matlab workspace and may not be explicitly saved. 7 | % 8 | % Inputs -----------------------------------------------------------------% 9 | % img - numerical array or string; 10 | % name - string; used to display errordlg if array is incorrect format 11 | % 12 | % Outputs ----------------------------------------------------------------% 13 | % outstate - integer; returns either out.cancelled, out.failed, or out.success. 14 | % 15 | % Limiting size of 5x5 is due to the fact that the b-spline coefficients 16 | % need a size of at least 5 in each direction to be convolved with the 17 | % kernel in ncorr_class_img.form_bcoef(). This is subject to change. 18 | % Also note that existance of the image must be checked (in the case a 19 | % filename is passed) before calling this function. 20 | 21 | % Initialize outputs 22 | outstate = out.failed; 23 | 24 | if (isnumeric(img)) 25 | % Test array directly 26 | if ((isa(img,'uint8') || isa(img,'uint16') || isa(img,'double')) && ... 27 | (length(size(img)) == 2 || length(size(img)) == 3) && (size(img,3) == 1 || size(img,3) == 3) && ... 28 | size(img,1) >= 5 && size(img,2) >= 5) 29 | % Image format is correct 30 | outstate = out.success; 31 | end 32 | elseif (ischar(img)) 33 | % Test using imfinfo 34 | info_img = imfinfo(img); 35 | if ((strcmp(info_img.ColorType,'truecolor') || strcmp(info_img.ColorType,'grayscale') || strcmp(info_img.ColorType,'indexed')) && ... 36 | info_img.Height >= 5 && info_img.Width >= 5) 37 | % Image format is correct 38 | outstate = out.success; 39 | end 40 | end 41 | 42 | % Display error dialogue if image format isnt correct 43 | if (outstate == out.failed) 44 | h_error = errordlg([name ' must be of class uint8, uint16, or double, have size of mxn or mxnx3, and must have a height and width greater than or equal to 5.'],'Error','modal'); 45 | uiwait(h_error); 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /ncorr_util_loadsavedimg.m: -------------------------------------------------------------------------------- 1 | function [img,outstate] = ncorr_util_loadsavedimg(img_save) 2 | % This function loads a saved image using info in img_save and returns the 3 | % corresponding ncorr_class_img. 4 | % 5 | % Inputs -----------------------------------------------------------------% 6 | % img_save - struct; struct('type',{},'gs',{},'name',{},'path',{},'roi',{}) 7 | % note that 'type' is either 'load', 'lazy', or 'file'. For 'lazy', gs is 8 | % empty; 9 | % 10 | % Outputs ----------------------------------------------------------------% 11 | % outstate - integer; returns either out.cancelled, out.failed, or out.success. 12 | % 13 | % Note that if image is 'lazy' or 'file', it will be loaded based on its 14 | % filename and path. When the image is not found on the path specified, 15 | % this function will also check the current directory. This allows saved 16 | % data to be used on other computers. 17 | % There is currently no checking done for the possibility of the user to 18 | % change the size of saved image. 19 | 20 | % Initialize outputs 21 | outstate = out.failed; 22 | img = ncorr_class_img.empty; 23 | 24 | img_prelim = ncorr_class_img; 25 | if (strcmp(img_save.type,'load')) 26 | % 'load' image; gs is specified 27 | img_prelim.set_img(img_save.type,struct('img',img_save.gs,'name',img_save.name,'path',img_save.path)); 28 | 29 | outstate = out.success; 30 | else 31 | % Must find image since it is either 'lazy' or 'file' 32 | if (exist(fullfile(img_save.path,img_save.name),'file')) 33 | % File exists in path specified 34 | if (strcmp(img_save.type,'lazy')) 35 | % This is a 'lazy' image 36 | img_prelim.set_img(img_save.type,struct('name',img_save.name,'path',img_save.path)); 37 | 38 | outstate = out.success; 39 | else 40 | % This is a 'file' image 41 | img_prelim.set_img(img_save.type,struct('img',imread(fullfile(img_save.path,img_save.name)),'name',img_save.name,'path',img_save.path)); 42 | 43 | outstate = out.success; 44 | end 45 | else 46 | % Try finding image in current directory 47 | if (exist(fullfile(pwd,img_save.name),'file')) 48 | % Filename exists in current directory, use 'pwd' as the path 49 | if (strcmp(img_save.type,'lazy')) 50 | % This is a 'lazy' image 51 | img_prelim.set_img(img_save.type,struct('name',img_save.name,'path',pwd)); 52 | 53 | outstate = out.success; 54 | else 55 | % This is a 'file' image 56 | img_prelim.set_img(img_save.type,struct('img',imread(fullfile(pwd,img_save.name)),'name',img_save.name,'path',pwd)); 57 | 58 | outstate = out.success; 59 | end 60 | end 61 | end 62 | end 63 | 64 | % Assign output 65 | if (outstate == out.success) 66 | img = img_prelim; 67 | end 68 | end 69 | 70 | -------------------------------------------------------------------------------- /ncorr_alg_interpqbs.m: -------------------------------------------------------------------------------- 1 | function vec_interp = ncorr_alg_interpqbs(coords,plot_bcoef,offset_x,offset_y,border_bcoef) 2 | % This function performs biquintic interpolation. 3 | % 4 | % Inputs -----------------------------------------------------------------% 5 | % coords - double array; nx2 array of coordinates. In the form of [x y] 6 | % plot_bcoef - double array; plot of bspline coefficients 7 | % offset_x - integer; x offset of bspline coef plot from the origin 8 | % offset_y - integer; y offset of bspline coef plot from the origin 9 | % border_bcoef - integer; border used when interpolating b-spline 10 | % coefficients. A border is usually applied to help mitigate the effects 11 | % of ringing near the edges since the bspline coefficient array is formed 12 | % using an FFT. 13 | % 14 | % Outputs ----------------------------------------------------------------% 15 | % vec_interp - vector of interpolated values the same length as the input 16 | % coords. Values which lie outside the b-spline coefficient plot are 17 | % returned as NaNs. 18 | % 19 | % Note that this function interpolates all coordinates that can be 20 | % interpolated within the b-spline array (i.e. no ROI or mask is used). 21 | 22 | % Initialize vec_interp - points that can't be interpolated will stay 23 | % NaNs 24 | vec_interp = nan(size(coords,1),1); 25 | 26 | % Biquintic Kernel Matrix 27 | QK = [1/120 13/60 11/20 13/60 1/120 0; 28 | -1/24 -5/12 0 5/12 1/24 0; 29 | 1/12 1/6 -1/2 1/6 1/12 0; 30 | -1/12 1/6 0 -1/6 1/12 0; 31 | 1/24 -1/6 1/4 -1/6 1/24 0; 32 | -1/120 1/24 -1/12 1/12 -1/24 1/120]; 33 | 34 | % Cycle over coordinates 35 | for i = 0:size(coords,1)-1 36 | x_tilda = coords(i+1,1); 37 | y_tilda = coords(i+1,2); 38 | 39 | y_tilda_floor = floor(y_tilda); 40 | x_tilda_floor = floor(x_tilda); 41 | 42 | % Make sure top, left, bottom, and right are within the b-spline 43 | % coefficient array. top, left, bottom and right are the bounding 44 | % box of the b-spline coefficients used for interpolation of this 45 | % point; 46 | top = y_tilda_floor-offset_y+border_bcoef-2; 47 | left = x_tilda_floor-offset_x+border_bcoef-2; 48 | bottom = y_tilda_floor-offset_y+border_bcoef+3; 49 | right = x_tilda_floor-offset_x+border_bcoef+3; 50 | if (top >= 0 && ... 51 | left >= 0 && ... 52 | bottom < size(plot_bcoef,1) && ... 53 | right < size(plot_bcoef,2)) 54 | % Set coords 55 | y_tilda_delta = y_tilda-y_tilda_floor; 56 | x_tilda_delta = x_tilda-x_tilda_floor; 57 | 58 | x_vec(1) = 1.0; 59 | x_vec(2) = x_tilda_delta; 60 | x_vec(3) = x_vec(2)*x_tilda_delta; 61 | x_vec(4) = x_vec(3)*x_tilda_delta; 62 | x_vec(5) = x_vec(4)*x_tilda_delta; 63 | x_vec(6) = x_vec(5)*x_tilda_delta; 64 | 65 | y_vec(1) = 1.0; 66 | y_vec(2) = y_tilda_delta; 67 | y_vec(3) = y_vec(2)*y_tilda_delta; 68 | y_vec(4) = y_vec(3)*y_tilda_delta; 69 | y_vec(5) = y_vec(4)*y_tilda_delta; 70 | y_vec(6) = y_vec(5)*y_tilda_delta; 71 | 72 | % Get interpolated value 73 | vec_interp(i+1) = y_vec*QK*plot_bcoef(top+1:bottom+1,left+1:right+1)*QK'*x_vec'; 74 | end 75 | end 76 | end -------------------------------------------------------------------------------- /ncorr_alg_formboundary.cpp: -------------------------------------------------------------------------------- 1 | // This function forms a boundary given a mask input 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "standard_datatypes.h" 8 | #include "ncorr_datatypes.h" 9 | #include "ncorr_lib.h" 10 | 11 | // ----------------------------------------------------// 12 | // Main Class -----------------------------------------// 13 | // ----------------------------------------------------// 14 | 15 | class class_formboundary { 16 | public: 17 | // Constructor 18 | class_formboundary(mxArray *plhs[ ],const mxArray *prhs [ ]); 19 | 20 | // Methods 21 | void analysis(); 22 | 23 | private: 24 | // Properties 25 | // Inputs: 26 | class_integer_array point_init; // standard datatype 27 | int direc; // standard datatype 28 | class_logical_array mask; // standard datatype 29 | 30 | // Outputs: 31 | mxArray *mat_boundary; 32 | class_double_array boundary; 33 | }; 34 | 35 | class_formboundary::class_formboundary(mxArray *plhs[ ],const mxArray *prhs[ ]) { 36 | // Get inputs -------------------------------------------------------// 37 | // input 1: point_init 38 | get_integer_array(point_init,prhs[0]); 39 | // input 2: direc 40 | get_integer_scalar(direc,prhs[1]); 41 | // input 3: mask 42 | get_logical_array(mask,prhs[2]); 43 | 44 | // Check inputs 45 | if (point_init.width == 2 && point_init.height == 1) { 46 | // Form/set outputs ---------------------------------------------// 47 | // output 1: boundary 48 | // Size isn't known beforehand, so just set to 0 for now. 49 | // This will be reset after boundary has been calculated 50 | mat_boundary = mxCreateDoubleMatrix(0, 0, mxREAL); 51 | plhs[0] = mat_boundary; 52 | 53 | // Get outputs --------------------------------------------------// 54 | // output 1: boundary 55 | get_double_array(boundary,mat_boundary); 56 | } else { 57 | mexErrMsgTxt("Initiation point must be size 1x2.\n"); 58 | } 59 | } 60 | 61 | // ----------------------------------------------------// 62 | // Main Class Methods ---------------------------------// 63 | // ----------------------------------------------------// 64 | 65 | void class_formboundary::analysis() { 66 | // Initialize vec_boundary and vec_point_init 67 | std::vector > vec_boundary; 68 | std::vector vec_point_init(2,0); 69 | vec_point_init[0] = point_init.value[0]; 70 | vec_point_init[1] = point_init.value[1]; 71 | 72 | // Call ncorr_lib form_boundary function 73 | form_boundary(vec_boundary,vec_point_init,mask,direc); 74 | 75 | // Resize boundary then transfer data 76 | boundary.alloc((int)vec_boundary.size(),2); 77 | 78 | // Copy elements and convert to double 79 | for (int i=0; i 4 | #include 5 | #include "standard_datatypes.h" 6 | #include "ncorr_datatypes.h" 7 | #include "ncorr_lib.h" 8 | 9 | #ifdef NCORR_OPENMP 10 | #include // openmp header 11 | #endif 12 | 13 | // ----------------------------------------------------// 14 | // Main Class -----------------------------------------// 15 | // ----------------------------------------------------// 16 | 17 | class class_testopenmp { 18 | public: 19 | // Constructor 20 | class_testopenmp(mxArray *plhs [ ],const mxArray *prhs [ ]); 21 | 22 | // Methods 23 | void analysis(); 24 | 25 | private: 26 | // Properties 27 | // Inputs: None 28 | 29 | // Outputs: 30 | bool *enabled_openmp; 31 | 32 | // Other variables: 33 | int total_threads; 34 | std::vector vec_enabled_thread; // Do NOT use vector - it is not safe to concurrently write to vector 35 | }; 36 | 37 | class_testopenmp::class_testopenmp(mxArray *plhs[ ],const mxArray *prhs[ ]) { 38 | // Get inputs ---------------------------------------------------// 39 | // None 40 | 41 | // Set total threads --------------------------------------------// 42 | total_threads = 4; // Any number greater than 1 should suffice 43 | 44 | // Thread enabled vector ----------------------------------------// 45 | vec_enabled_thread.resize(total_threads,0); // One for each thread, initialized to false 46 | 47 | // OpenMP Setup -------------------------------------------------// 48 | #ifdef NCORR_OPENMP 49 | // Set number of threads 50 | omp_set_num_threads(total_threads); 51 | #endif 52 | 53 | // Form/set outputs ---------------------------------------------// 54 | // output 1: enabled_openmp 55 | plhs[0] = mxCreateLogicalMatrix(1,1); 56 | 57 | // Get outputs --------------------------------------------------// 58 | // output 1: enabled_openmp 59 | enabled_openmp = mxGetLogicals(plhs[0]); 60 | } 61 | 62 | // ----------------------------------------------------// 63 | // Main Class Methods ---------------------------------// 64 | // ----------------------------------------------------// 65 | 66 | void class_testopenmp::analysis() { 67 | // Initialize enabled_openmp to true 68 | *enabled_openmp = true; 69 | 70 | // Enter parallel region - anything inside here needs to be threadsafe 71 | #ifdef NCORR_OPENMP 72 | #pragma omp parallel 73 | { 74 | #endif 75 | 76 | #ifdef NCORR_OPENMP 77 | // Get thread number 78 | int num_thread = omp_get_thread_num(); 79 | #else 80 | // Set to zero if openmp is not enabled 81 | int num_thread = 0; 82 | #endif 83 | 84 | // Each thread needs to set vec_enabled_thread to true 85 | vec_enabled_thread[num_thread] = true; 86 | 87 | #ifdef NCORR_OPENMP 88 | } 89 | #endif 90 | 91 | // Check vec_enabled_thread to make sure all seeds processed correctly 92 | for (int i=0; i 7 | 8 | // ----------------------------------------------------------------------// 9 | // This is for a string input -------------------------------------------// 10 | // ----------------------------------------------------------------------// 11 | 12 | // NOT THREAD SAFE 13 | void get_string(std::string &string,const mxArray *mat_buf); // potentially dangerous because user could change the length of the string 14 | 15 | // ----------------------------------------------------------------------// 16 | // This is for double scalar input --------------------------------------// 17 | // ----------------------------------------------------------------------// 18 | 19 | // NOT THREAD SAFE 20 | void get_double_scalar(double &scalar,const mxArray *mat_buf); 21 | 22 | // ----------------------------------------------------------------------// 23 | // This is for integer scalar input -------------------------------------// 24 | // ----------------------------------------------------------------------// 25 | 26 | // NOT THREAD SAFE 27 | void get_integer_scalar(int &scalar,const mxArray *mat_buf); 28 | 29 | // ----------------------------------------------------------------------// 30 | // This is for logical scalar input -------------------------------------// 31 | // ----------------------------------------------------------------------// 32 | 33 | // NOT THREAD SAFE 34 | void get_logical_scalar(bool &scalar,const mxArray *mat_buf); 35 | 36 | // ----------------------------------------------------------------------// 37 | // This is for a double array input -------------------------------------// 38 | // ----------------------------------------------------------------------// 39 | 40 | class class_double_array { 41 | public: 42 | // Constructor 43 | class_double_array(); // THREAD SAFE 44 | 45 | // Properties 46 | int width; 47 | int height; 48 | double *value; 49 | 50 | // Methods 51 | void reset(); // THREAD SAFE 52 | void alloc(const int &h,const int &w); // NOT THREAD SAFE 53 | void free(); // NOT THREAD SAFE 54 | }; 55 | 56 | // NOT THREAD SAFE 57 | void get_double_array(class_double_array &array,const mxArray *mat_buf); 58 | 59 | // ----------------------------------------------------------------------// 60 | // This is for a integer array input ------------------------------------// 61 | // ----------------------------------------------------------------------// 62 | 63 | class class_integer_array { 64 | public: 65 | // Constructor 66 | class_integer_array(); // THREAD SAFE 67 | 68 | // Properties 69 | int width; 70 | int height; 71 | int *value; 72 | 73 | // Methods 74 | void reset(); // THREAD SAFE 75 | void alloc(const int &h,const int &w); // NOT THREAD SAFE 76 | void free(); // NOT THREAD SAFE 77 | }; 78 | 79 | // NOT THREAD SAFE 80 | void get_integer_array(class_integer_array &array,const mxArray *mat_buf); 81 | 82 | // ----------------------------------------------------------------------// 83 | // This is for a logical array input ------------------------------------// 84 | // ----------------------------------------------------------------------// 85 | 86 | class class_logical_array { 87 | public: 88 | // Constructor 89 | class_logical_array(); // THREAD SAFE 90 | 91 | // Properties 92 | int width; 93 | int height; 94 | bool *value; 95 | 96 | // Methods 97 | void reset(); // THREAD SAFE 98 | void alloc(const int &h,const int &w); // NOT THREAD SAFE 99 | void free(); // NOT THREAD SAFE 100 | }; 101 | 102 | // NOT THREAD SAFE 103 | void get_logical_array(class_logical_array &array,const mxArray *mat_buf); 104 | 105 | #endif /* STANDARD_DATATYPES_H */ 106 | -------------------------------------------------------------------------------- /ncorr_datatypes.h: -------------------------------------------------------------------------------- 1 | // These are function declarations used to obtain ncorr-type inputs to mex files 2 | // Some functions are not thread safe. 3 | 4 | #ifndef NCORR_DATATYPES_H 5 | #define NCORR_DATATYPES_H 6 | 7 | #include 8 | #include "standard_datatypes.h" 9 | 10 | // ----------------------------------------------------------------------// 11 | // NCORR_CLASS_IMG STUFF ------------------------------------------------// 12 | // ----------------------------------------------------------------------// 13 | 14 | // This is for an ncorr_class_img input ---------------------------------// 15 | class ncorr_class_img { 16 | public: 17 | // Constructor 18 | ncorr_class_img(); // THREADSAFE 19 | 20 | // Properties 21 | std::string type; 22 | class_double_array gs; 23 | double max_gs; 24 | class_double_array bcoef; 25 | int border_bcoef; 26 | }; 27 | 28 | // NOT THREADSAFE 29 | void get_imgs(std::vector &images,const mxArray *prhs); 30 | 31 | // ----------------------------------------------------------------------// 32 | // NCORR_CLASS_ROI STUFF ------------------------------------------------// 33 | // ----------------------------------------------------------------------// 34 | 35 | class ncorr_class_region { 36 | public: 37 | // Constructor 38 | ncorr_class_region(); // THREADSAFE 39 | 40 | // Properties 41 | class_integer_array nodelist; 42 | class_integer_array noderange; 43 | int upperbound; 44 | int lowerbound; 45 | int leftbound; 46 | int rightbound; 47 | int totalpoints; 48 | 49 | // Methods 50 | void alloc(const int &h,const int &w); // NOT THREADSAFE 51 | void free(); // NOT THREADSAFE 52 | }; 53 | 54 | // NOT THREADSAFE 55 | void get_region(std::vector ®ion,const mxArray *prhs); 56 | 57 | // Cirroi Stuff ---------------------------------------------------------// 58 | struct struct_cirroi { 59 | // Constructor 60 | struct_cirroi(); // THREADSAFE 61 | 62 | // Properties 63 | ncorr_class_region region; 64 | class_logical_array mask; 65 | int radius; 66 | int x; 67 | int y; 68 | }; 69 | 70 | struct struct_info_cirroi { 71 | // Constructor 72 | struct_info_cirroi(); // THREADSAFE 73 | 74 | // Main properties 75 | ncorr_class_region region; 76 | class_logical_array mask; 77 | int radius; 78 | int x; 79 | int y; 80 | 81 | // Additional Properties 82 | class_logical_array mask_buffer; // Used as additional storage so you dont have to overwrite original mask 83 | class_integer_array circletemplate; 84 | class_integer_array queue_buffer; 85 | std::vector queue_nodelist; 86 | std::vector queue_nodeindex; 87 | std::vector activelines; 88 | }; 89 | 90 | // This is for an ncorr_class_roi input ---------------------------------// 91 | class ncorr_class_roi { 92 | public: 93 | // Constructor 94 | ncorr_class_roi(); // THREADSAFE 95 | 96 | // Properties 97 | class_logical_array mask; 98 | std::vector region; 99 | std::vector cirroi; 100 | 101 | // Methods 102 | void set_cirroi(const int &radius_i,const int &thread_total); // NOT THREADSAFE 103 | void update_cirroi(const int &num_region,const int &thread_num); // THREADSAFE 104 | void get_cirroi(const int &x_i,const int &y_i,const int &num_region,const bool &subsettrunc,const int &thread_num); // THREADSAFE 105 | bool withinregion(const int &x_i,const int &y_i,const int &num_region); // THREADSAFE 106 | 107 | private: 108 | // Properties 109 | std::vector info_cirroi; 110 | }; 111 | 112 | // NOT THREADSAFE 113 | void get_rois(std::vector &rois,const mxArray *prhs); 114 | 115 | // Inherited Types ------------------------------------------------------// 116 | 117 | class ncorr_class_inverseregion : public ncorr_class_region { 118 | public: 119 | // Constructor 120 | ncorr_class_inverseregion(ncorr_class_region ®ion,const int &border_extrap); // NOT THREADSAFE 121 | }; 122 | 123 | #endif /* NCORR_DATATYPES_H */ 124 | -------------------------------------------------------------------------------- /ncorr_lib.h: -------------------------------------------------------------------------------- 1 | // These are functions declarations used in ncorr. 2 | 3 | #ifndef NCORR_LIB_H 4 | #define NCORR_LIB_H 5 | 6 | #include 7 | #include 8 | #include "standard_datatypes.h" 9 | #include "ncorr_datatypes.h" 10 | 11 | #define WAITBAR_UPDATE 1000 // Number of waitbar increments until an update is shown 12 | #define LAMBDA 0.0000000001 // Cutoff for values approximately zero 13 | #define FILTERITERATIONS 50 // Number of average filters applied when extrapolating data 14 | 15 | enum OUT {CANCELLED = -1, FAILED = 0, SUCCESS = 1}; // These let ncorr know whether analysis was successful, failed, or was cancelled so it can be handled appropriately. 16 | 17 | // ----------------------------------------------------------------------// 18 | // WAITBAR STUFF --------------------------------------------------------// 19 | // ----------------------------------------------------------------------// 20 | 21 | class class_waitbar { 22 | public: 23 | // Constructor 24 | class_waitbar(); // THREADSAFE 25 | // Destructor 26 | ~class_waitbar(); // NOT THREADSAFE 27 | 28 | // Methods 29 | void start(const int &num_img,const int &total_imgs,const int &computepoints); // NOT THREADSAFE 30 | void increment(); // THREADSAFE 31 | bool updateandcheck(); // NOT THREADSAFE 32 | 33 | private: 34 | // Properties 35 | int computepoints; 36 | int counter_total; 37 | int counter_sub; 38 | mxArray *lhs_waitbar_create[1]; 39 | mxArray *lhs_getappdata[1]; 40 | mxArray *rhs_getappdata[2]; 41 | mxArray *rhs_waitbar_update[2]; 42 | mxArray *mat_waitbar_update_1; 43 | mxArray *mat_getappdata_1; 44 | double *fraction_waitbar_update; 45 | 46 | // Methods 47 | bool getappdata(); // NOT THREADSAFE 48 | }; 49 | 50 | // ----------------------------------------------------------------------// 51 | // FUNCTIONS ------------------------------------------------------------// 52 | // ----------------------------------------------------------------------// 53 | 54 | double ncorr_round(const double &r); // THREADSAFE 55 | int sign(const double &r); // THREADSAFE 56 | int mod_pos(const int &i,const int &n); // THREADSAFE 57 | void form_boundary(std::vector > &vec_boundary,const std::vector &point_topleft,const class_logical_array &mask,int &direc); // THREADSAFE 58 | 59 | // Use this as a threadsafe version of a region, since it uses vectors. 60 | struct vec_struct_region { 61 | public: 62 | // Constructor 63 | vec_struct_region(); // THREADSAFE 64 | 65 | // Properties 66 | std::vector nodelist; 67 | std::vector noderange; 68 | int height_nodelist; 69 | int width_nodelist; 70 | int upperbound; 71 | int lowerbound; 72 | int leftbound; 73 | int rightbound; 74 | int totalpoints; 75 | }; 76 | 77 | void form_regions(std::vector ®ion,bool &removed,const class_logical_array &mask,const int &cutoff,const bool &preservelength); // THREADSAFE 78 | void form_union(std::vector ®ion_union,const std::vector ®ion,const class_logical_array &mask,const bool &inplace); // THREADSAFE 79 | 80 | void cholesky(std::vector &mat,bool &positivedef,const int &size_mat); // THREADSAFE 81 | void forwardsub(std::vector &vec,const std::vector &mat,const int &size_mat); // THREADSAFE 82 | void backwardsub(std::vector &vec,const std::vector &mat,const int &size_mat); // THREADSAFE 83 | OUT interp_qbs(double &interp,const double &x_tilda,const double &y_tilda,const class_double_array &plot_interp,const class_logical_array &mask,const int &offset_x,const int &offset_y,const int &border_bcoef); // NOT THREADSAFE 84 | void expand_filt(class_double_array &plot_extrap,const ncorr_class_inverseregion &inverseregion); // THREADSAFE 85 | 86 | // Debugging ------------------------------------------------------------// 87 | 88 | void imshow(double *img,int width,int height); // NOT THREADSAFE 89 | void imshow(int *img,int width,int height); // NOT THREADSAFE 90 | void imshow(bool *img,int width,int height); // NOT THREADSAFE 91 | 92 | #endif /* NCORR_LIB_H */ 93 | -------------------------------------------------------------------------------- /ncorr_alg_extrapdata.cpp: -------------------------------------------------------------------------------- 1 | // This function extrapolates data by expanding and filtering it 2 | 3 | #include 4 | #include 5 | #include "standard_datatypes.h" 6 | #include "ncorr_datatypes.h" 7 | #include "ncorr_lib.h" 8 | 9 | // ----------------------------------------------------// 10 | // Main Class -----------------------------------------// 11 | // ----------------------------------------------------// 12 | 13 | class class_extrapdata { 14 | public: 15 | // Constructor 16 | class_extrapdata(mxArray *plhs[ ],const mxArray *prhs [ ]); 17 | 18 | // Methods: 19 | void analysis(); 20 | 21 | private: 22 | // Properties 23 | // Inputs: 24 | class_double_array plot_data; // standard datatype 25 | std::vector roi; // ncorr datatype 26 | int border_extrap; // standard datatype 27 | 28 | // Outputs: 29 | std::vector plot_extrap; 30 | }; 31 | 32 | class_extrapdata::class_extrapdata(mxArray *plhs[ ],const mxArray *prhs[ ]) { 33 | // Get inputs ---------------------------------------------------// 34 | // input 1: plot_data 35 | get_double_array(plot_data,prhs[0]); 36 | // input 2: ROI 37 | get_rois(roi,prhs[1]); 38 | // input 3: border_extrap 39 | get_integer_scalar(border_extrap,prhs[2]); 40 | 41 | // Form/set outputs ---------------------------------------------// 42 | // output 1: plot_extrap 43 | int numdims = 2; 44 | mwSize dims[2] = {1,(mwSize)roi[0].region.size()}; 45 | mxArray *mat_plot_extrap = mxCreateCellArray(numdims, dims); 46 | plhs[0] = mat_plot_extrap; 47 | // Form one plot_data per region 48 | for (int i=0; i<(int)roi[0].region.size(); i++) { 49 | // Get dimensions of plot_extrap 50 | int height_plot_extrap = 0; 51 | int width_plot_extrap = 0; 52 | if (roi[0].region[i].totalpoints > 0) { 53 | height_plot_extrap = (roi[0].region[i].lowerbound-roi[0].region[i].upperbound+1)+2*border_extrap; 54 | width_plot_extrap = (roi[0].region[i].rightbound-roi[0].region[i].leftbound+1)+2*border_extrap; 55 | } 56 | 57 | // Create array 58 | mxArray *mat_plot_extrap_buf = mxCreateDoubleMatrix(height_plot_extrap, width_plot_extrap, mxREAL); 59 | // Add array to cell 60 | mxSetCell(mat_plot_extrap,i,mat_plot_extrap_buf); 61 | 62 | // Get outputs -------------------------------------------------// 63 | // output 1: plot_extrap 64 | class_double_array plot_extrap_template; 65 | get_double_array(plot_extrap_template,mat_plot_extrap_buf); 66 | plot_extrap.push_back(plot_extrap_template); 67 | } 68 | } 69 | 70 | // ----------------------------------------------------// 71 | // Main Class Methods ---------------------------------// 72 | // ----------------------------------------------------// 73 | 74 | void class_extrapdata::analysis() { 75 | // Cycle over regions 76 | for (int i=0; i<(int)roi[0].region.size(); i++) { 77 | // Fill plot_extrap with values from plot_data. These points will not be altered. 78 | // Only the region outside this region will be altered/extrapolated. 79 | if (roi[0].region[i].totalpoints > 0) { 80 | for (int j=0; j 33 | % Set output 34 | outstate = out.success; 35 | 36 | % Exit 37 | close(handles_gui.figure); 38 | end 39 | 40 | function callback_button_cancel(hObject,eventdata) %#ok 41 | close(handles_gui.figure); 42 | end 43 | 44 | function update_axes(action) 45 | if (strcmp(action,'set')) 46 | % Overlay ROI over img 47 | preview_roi = img.get_gs(); 48 | preview_roi(roi.mask) = preview_roi(roi.mask)+img.max_gs; 49 | imshow(preview_roi,[img.min_gs 2*img.max_gs],'Parent',handles_gui.axes_preview); 50 | set(handles_gui.axes_preview,'Visible','off'); 51 | set(handles_gui.text_name,'String',['Name: ' img.name(1:min(end,22))]); 52 | end 53 | end 54 | 55 | function handles_gui = init_gui() 56 | % GUI controls -------------------------------------------------------% 57 | % Figure 58 | handles_gui.figure = figure( ... 59 | 'Tag', 'figure', ... 60 | 'Units', 'characters', ... 61 | 'Position', ncorr_util_figpos(pos_parent,[35 126.7]), ... 62 | 'Name', 'Load ROI', ... 63 | 'MenuBar', 'none', ... 64 | 'NumberTitle', 'off', ... 65 | 'Color', get(0,'DefaultUicontrolBackgroundColor'), ... 66 | 'handlevisibility','off', ... 67 | 'DockControls','off', ... 68 | 'Resize','off', ... 69 | 'WindowStyle','modal', ... 70 | 'Visible','off', ... 71 | 'IntegerHandle','off', ... 72 | 'Interruptible','off'); 73 | 74 | % Panels 75 | handles_gui.group_menu = uipanel( ... 76 | 'Parent', handles_gui.figure, ... 77 | 'Tag', 'group_menu', ... 78 | 'Units', 'characters', ... 79 | 'Position', [2 27.8 35 6.6], ... 80 | 'Title', 'Menu', ... 81 | 'Interruptible','off'); 82 | 83 | handles_gui.group_preview = uibuttongroup( ... 84 | 'Parent', handles_gui.figure, ... 85 | 'Tag', 'group_preview', ... 86 | 'Units', 'characters', ... 87 | 'Position', [38.7 0.75 85.8 33.6], ... 88 | 'Title', 'ROI Preview', ... 89 | 'Interruptible','off'); 90 | 91 | % Axes 92 | handles_gui.axes_preview = axes( ... 93 | 'Parent', handles_gui.group_preview, ... 94 | 'Tag', 'axes_preview', ... 95 | 'Units', 'characters', ... 96 | 'Visible','off', ... 97 | 'Position', [2.4 3 80 28.6], ... 98 | 'Interruptible','off'); 99 | 100 | % Static texts 101 | handles_gui.text_name = uicontrol( ... 102 | 'Parent', handles_gui.group_preview, ... 103 | 'Tag', 'text_name', ... 104 | 'Style', 'text', ... 105 | 'Units', 'characters', ... 106 | 'Position', [3 0.7 56.9 1.3], ... 107 | 'String', 'Name: ', ... 108 | 'HorizontalAlignment', 'left', ... 109 | 'Interruptible','off'); 110 | 111 | % Pushbuttons 112 | handles_gui.button_finish = uicontrol( ... 113 | 'Parent', handles_gui.group_menu, ... 114 | 'Tag', 'button_finish', ... 115 | 'Style', 'pushbutton', ... 116 | 'Units', 'characters', ... 117 | 'Position', [2.1 3 29.7 1.7], ... 118 | 'String', 'Finish', ... 119 | 'Callback', ncorr_util_wrapcallbacktrycatch(@callback_button_finish,handles_gui.figure), ... 120 | 'Interruptible','off'); 121 | 122 | handles_gui.button_cancel = uicontrol( ... 123 | 'Parent', handles_gui.group_menu, ... 124 | 'Tag', 'button_cancel', ... 125 | 'Style', 'pushbutton', ... 126 | 'Units', 'characters', ... 127 | 'Position', [2.1 1 29.7 1.7], ... 128 | 'String', 'Cancel', ... 129 | 'Callback', ncorr_util_wrapcallbacktrycatch(@callback_button_cancel), ... 130 | 'Interruptible','off'); 131 | end 132 | 133 | % Pause until figure is closed ---------------------------------------% 134 | waitfor(handles_gui.figure); 135 | end 136 | -------------------------------------------------------------------------------- /ncorr_alg_formunion.cpp: -------------------------------------------------------------------------------- 1 | // This function forms unioned region given a ROI and mask (logical array) input. 2 | // It calls the form_union function from ncorr_lib 3 | 4 | #include 5 | #include 6 | #include 7 | #include "standard_datatypes.h" 8 | #include "ncorr_datatypes.h" 9 | #include "ncorr_lib.h" 10 | 11 | // ----------------------------------------------------// 12 | // Main Class -----------------------------------------// 13 | // ----------------------------------------------------// 14 | 15 | class class_formunion { 16 | public: 17 | // Constructor 18 | class_formunion(mxArray *plhs [ ],const mxArray *prhs [ ]); 19 | 20 | // Methods 21 | void analysis(); 22 | 23 | private: 24 | // Properties 25 | // Inputs: 26 | std::vector roi; // ncorr datatype 27 | class_logical_array mask_union; // standard datatype 28 | 29 | // Outputs: 30 | mxArray *mat_region_union; 31 | 32 | // Other variables: 33 | int numfields; 34 | }; 35 | 36 | class_formunion::class_formunion(mxArray *plhs [ ],const mxArray *prhs[ ]) { 37 | // Get inputs -------------------------------------// 38 | // input 1: ROI 39 | get_rois(roi,prhs[0]); 40 | // input 2: mask_union 41 | get_logical_array(mask_union,prhs[1]); 42 | 43 | // Form/set Outputs -------------------------------// 44 | // output 1: region 45 | mwSize dims[2] = {0,0}; 46 | numfields = 7; 47 | const char *fieldnames[] = {"nodelist","noderange","leftbound","rightbound","upperbound","lowerbound","totalpoints"}; 48 | mat_region_union = mxCreateStructArray(2,dims,numfields,fieldnames); 49 | plhs[0] = mat_region_union; 50 | 51 | // Get Outputs ------------------------------------// 52 | // output 1: region 53 | // This output is a structure whose elements' size is not known beforehand, so get them in the analysis function 54 | } 55 | 56 | // ----------------------------------------------------// 57 | // Main Class Methods ---------------------------------// 58 | // ----------------------------------------------------// 59 | 60 | void class_formunion::analysis() { 61 | // Initialize region_union to send to form_union - use vec_struct_region 62 | std::vector region_union; 63 | region_union.resize(roi[0].region.size()); // Note that size is preserved, even if the unioned region is empty 64 | 65 | // Call ncorr_lib form_union function - Do not do this in-place since it's possible for unioned region to be larger 66 | form_union(region_union,roi[0].region,mask_union,false); 67 | 68 | // Resize structure and store info 69 | mwSize dims[2] = {1,(mwSize)roi[0].region.size()}; 70 | mxSetDimensions(mat_region_union,dims,2); 71 | mxSetData(mat_region_union,mxCalloc(dims[0]*dims[1]*numfields,sizeof(mxArray *))); // Allocate memory for resized structure 72 | for (int i=0; i<(int)roi[0].region.size(); i++) { 73 | // Form fields 74 | mxArray *mat_nodelist = mxCreateDoubleMatrix(region_union[i].height_nodelist,region_union[i].width_nodelist,mxREAL); 75 | mxArray *mat_noderange = mxCreateDoubleMatrix(region_union[i].height_nodelist,1,mxREAL); 76 | mxArray *mat_leftbound = mxCreateDoubleMatrix(1,1,mxREAL); 77 | mxArray *mat_rightbound = mxCreateDoubleMatrix(1,1,mxREAL); 78 | mxArray *mat_upperbound = mxCreateDoubleMatrix(1,1,mxREAL); 79 | mxArray *mat_lowerbound = mxCreateDoubleMatrix(1,1,mxREAL); 80 | mxArray *mat_totalpoints = mxCreateDoubleMatrix(1,1,mxREAL); 81 | 82 | // Get data 83 | class_double_array nodelist; 84 | class_double_array noderange; 85 | get_double_array(nodelist,mat_nodelist); 86 | get_double_array(noderange,mat_noderange); 87 | double *leftbound = mxGetPr(mat_leftbound); 88 | double *rightbound = mxGetPr(mat_rightbound); 89 | double *upperbound = mxGetPr(mat_upperbound); 90 | double *lowerbound = mxGetPr(mat_lowerbound); 91 | double *totalpoints = mxGetPr(mat_totalpoints); 92 | 93 | // Copy Data - make sure to cast to double since Matlab uses double 94 | for (int j=0; j 5 | #include 6 | #include "standard_datatypes.h" 7 | #include "ncorr_datatypes.h" 8 | #include "ncorr_lib.h" 9 | 10 | // ----------------------------------------------------// 11 | // Main Class -----------------------------------------// 12 | // ----------------------------------------------------// 13 | 14 | class class_formregions { 15 | public: 16 | // Constructor 17 | class_formregions(mxArray *plhs [ ],const mxArray *prhs [ ]); 18 | 19 | // Methods 20 | void analysis(); 21 | 22 | private: 23 | // Properties 24 | // Inputs: 25 | class_logical_array mask; // standard datatype 26 | int cutoff; // standard datatype 27 | bool preservelength; // standard datatype 28 | 29 | // Outputs: 30 | mxArray *mat_regions; 31 | bool *removed; 32 | 33 | // Other variables: 34 | int numfields; 35 | }; 36 | 37 | class_formregions::class_formregions(mxArray *plhs [ ],const mxArray *prhs[ ]) { 38 | // Get inputs -------------------------------------// 39 | // input 1: mask 40 | get_logical_array(mask,prhs[0]); 41 | // input 2: cutoff 42 | get_integer_scalar(cutoff,prhs[1]); 43 | // input 3: preservelength - this parameters tells whether to preserve the width of the mask when forming the noderange/nodelist 44 | get_logical_scalar(preservelength,prhs[2]); 45 | 46 | // Form/set Outputs -------------------------------// 47 | // output 1: regions 48 | mwSize dims[2] = {0,0}; 49 | numfields = 7; 50 | const char *fieldnames[] = {"nodelist","noderange","leftbound","rightbound","upperbound","lowerbound","totalpoints"}; 51 | mat_regions = mxCreateStructArray(2,dims,numfields,fieldnames); 52 | plhs[0] = mat_regions; 53 | 54 | // output 2: removed - tells user if small regions were removed due to the cutoff 55 | mxArray *mat_removed = mxCreateLogicalMatrix(1,1); 56 | plhs[1] = mat_removed; 57 | 58 | // Get outputs ------------------------------------// 59 | // output 1: regions 60 | // This output is a structure whose elements' size is not known beforehand, so get them in the analysis function 61 | 62 | // output 2: removed 63 | removed = mxGetLogicals(mat_removed); 64 | } 65 | 66 | // ----------------------------------------------------// 67 | // Main Class Methods ---------------------------------// 68 | // ----------------------------------------------------// 69 | 70 | void class_formregions::analysis() { 71 | // Initialize removed to false 72 | *removed = false; // Set to true if small regions were removed 73 | 74 | // Initialize regions to send to form_regions - used vec_struct_region 75 | std::vector regions; 76 | 77 | // Call ncorr_lib form_regions function 78 | form_regions(regions,*removed,mask,cutoff,preservelength); 79 | 80 | // Resize structure and store info 81 | mwSize dims[2] = {1,(mwSize)regions.size()}; 82 | mxSetDimensions(mat_regions,dims,2); 83 | mxSetData(mat_regions,mxCalloc(dims[0]*dims[1]*numfields,sizeof(mxArray *))); // Allocate memory for resized structure 84 | for (int i=0; i<(int)regions.size(); i++) { 85 | // Form fields 86 | mxArray *mat_nodelist = mxCreateDoubleMatrix(regions[i].height_nodelist, regions[i].width_nodelist, mxREAL); 87 | mxArray *mat_noderange = mxCreateDoubleMatrix(regions[i].height_nodelist, 1, mxREAL); 88 | mxArray *mat_leftbound = mxCreateDoubleMatrix(1, 1, mxREAL); 89 | mxArray *mat_rightbound = mxCreateDoubleMatrix(1, 1, mxREAL); 90 | mxArray *mat_upperbound = mxCreateDoubleMatrix(1, 1, mxREAL); 91 | mxArray *mat_lowerbound = mxCreateDoubleMatrix(1, 1, mxREAL); 92 | mxArray *mat_totalpoints = mxCreateDoubleMatrix(1, 1, mxREAL); 93 | 94 | // Get Data 95 | class_double_array nodelist; 96 | class_double_array noderange; 97 | get_double_array(nodelist,mat_nodelist); 98 | get_double_array(noderange,mat_noderange); 99 | double *leftbound = mxGetPr(mat_leftbound); 100 | double *rightbound = mxGetPr(mat_rightbound); 101 | double *upperbound = mxGetPr(mat_upperbound); 102 | double *lowerbound = mxGetPr(mat_lowerbound); 103 | double *totalpoints = mxGetPr(mat_totalpoints); 104 | 105 | // Copy Data - make sure to cast to double since Matlab uses double 106 | for (int j=0; j 102 | 103 | % At this point all images are correct; return the 104 | % sorted imgs list 105 | imgs = imgs_prelim(idx_sorted); 106 | 107 | outstate = out.success; 108 | else 109 | h_error = errordlg('Loading images failed. They were determined to have incorrect formatting. Make sure images are in the form of "name_#.ext" and that data has the correct format.','Error','modal'); 110 | uiwait(h_error); 111 | 112 | outstate = out.failed; 113 | end 114 | else 115 | h_error = errordlg('Loading images failed. Name or extension could not be obtained. Make sure images are in the form of "name_#.ext".','Error','modal'); 116 | uiwait(h_error); 117 | 118 | outstate = out.failed; 119 | end 120 | catch %#ok 121 | h_error = errordlg('Loading images failed, most likely because Ncorr ran out of memory.','Error','modal'); 122 | uiwait(h_error); 123 | 124 | outstate = out.failed; 125 | end 126 | end 127 | end 128 | -------------------------------------------------------------------------------- /ncorr_util_formregionconstraint.m: -------------------------------------------------------------------------------- 1 | function handle_constraintfcn = ncorr_util_formregionconstraint(region) 2 | % This function returns handle_constraintfcn which, given a point, will 3 | % return the closest point inside the region. This is mainly used for the 4 | % constrainfcn for impoints which are restricted to be inside a region. 5 | % 6 | % Inputs -----------------------------------------------------------------% 7 | % region - struct; region info used to determine the position of returned 8 | % point 9 | % 10 | % Outputs ----------------------------------------------------------------% 11 | % handle_constraintfcn - function handle; 12 | % 13 | % Returns error if region is empty. 14 | 15 | if (region.totalpoints == 0) 16 | error('Region cannot be empty when forming constraint function.'); 17 | end 18 | 19 | handle_constraintfcn = @constraintfcn; 20 | 21 | function pos_nearest = constraintfcn(pos) 22 | % This function will generally be called within a callback 23 | % automatically which will use 1 based indexing, so pos_nearest is 24 | % returned using 1 based indexing. However, for processing, 0 based 25 | % indexing is used, so convert back and forth as necessary. 26 | 27 | % Convert pos to 0 based indexing and round it: 28 | pos = round(pos)-1; 29 | 30 | % Find bounding box - must find this since left and right bounds 31 | % are based on the length of the noderange/nodelist which may not 32 | % necessarily be the bounding box. 33 | left = region.rightbound; 34 | right = 0; 35 | firstpoint = false; 36 | for i = 0:size(region.noderange,1)-1 37 | if (region.noderange(i+1) > 0 && ~firstpoint) 38 | firstpoint = true; 39 | left = i + region.leftbound; 40 | end 41 | 42 | if (region.noderange(i+1) > 0 && i+region.leftbound > right) 43 | right = i + region.leftbound; 44 | end 45 | end 46 | 47 | % Check if above, between, or beneath bounds 48 | if (pos(1) < left) 49 | % Left of bounds - set x to left and then find the nearest node 50 | % for y 51 | pos_nearest(1) = left+1; 52 | pos_nearest(2) = nearestnode(region.nodelist(left-region.leftbound+1,:),region.noderange(left-region.leftbound+1),pos(2))+1; 53 | elseif (pos(1) >= left && pos(1) <= right) 54 | % Between bounds - must check if nodes exist at this x position 55 | if (region.noderange(pos(1)-region.leftbound+1) > 0) 56 | pos_nearest(1) = pos(1)+1; 57 | else 58 | % Initialize 59 | leftfound = false; 60 | leftdist = 0; 61 | rightfound = false; 62 | rightdist = 0; 63 | 64 | % Cycle left until finding a positive noderange 65 | for i = pos(1)-1:-1:left 66 | if (region.noderange(i-region.leftbound+1) > 0) 67 | leftdist = pos(1)-i; 68 | leftfound = true; 69 | break; 70 | end 71 | end 72 | 73 | % Cycle right until finding a positive noderange 74 | for i = pos(1)+1:right 75 | if (region.noderange(i-region.leftbound+1) > 0) 76 | rightdist = i-pos(1); 77 | rightfound = true; 78 | break; 79 | end 80 | end 81 | 82 | % Now set x position 83 | if (leftfound && rightfound) 84 | if (leftdist < rightdist) 85 | pos_nearest(1) = pos(1)-leftdist+1; 86 | else 87 | pos_nearest(1) = pos(1)+rightdist+1; 88 | end 89 | elseif (leftfound) 90 | pos_nearest(1) = pos(1)-leftdist+1; 91 | else 92 | pos_nearest(1) = pos(1)+rightdist+1; 93 | end 94 | end 95 | 96 | pos_nearest(2) = nearestnode(region.nodelist((pos_nearest(1)-1)-region.leftbound+1,:),region.noderange((pos_nearest(1)-1)-region.leftbound+1),pos(2))+1; 97 | else 98 | % Right of bounds - set x to left and then find the nearest node 99 | % for y 100 | pos_nearest(1) = right+1; 101 | pos_nearest(2) = nearestnode(region.nodelist(right-region.leftbound+1,:),region.noderange(right-region.leftbound+1),pos(2))+1; 102 | end 103 | end 104 | end 105 | 106 | function y_nearest = nearestnode(nodelist,noderange,y_coord) 107 | % Finds the nearest y value given the nodelist, noderange and y_coord. Note 108 | % that nodelist and noderange are a single column for a specific x position. 109 | % Uses zero based indexing. 110 | 111 | % Initialize 112 | y_nearest = -1; 113 | for i = 0:2:noderange-1 114 | % Test if node is before, within, or after a node pair 115 | if (y_coord < nodelist(i+1)) 116 | if (i == 0) 117 | % Above first node 118 | y_nearest = nodelist(i+1); 119 | break; 120 | else 121 | % Above inner node pair 122 | % Find out if y_coord is closer to bottom node of previous 123 | % pair or top node of current pair 124 | if ((y_coord-nodelist(i)) < (nodelist(i+1)-y_coord)) 125 | % y_coord is closer to bottom node of previous pair 126 | y_nearest = nodelist(i); 127 | break; 128 | else 129 | % y_coord is closer to top node of current pair 130 | y_nearest = nodelist(i+1); 131 | break; 132 | end 133 | end 134 | elseif (y_coord >= nodelist(i+1) && y_coord <= nodelist(i+2)) 135 | % y_coord is between node pairs, so just return it 136 | y_nearest = y_coord; 137 | break; 138 | else 139 | % y_coord is after noderange - test to see if this is the last 140 | % node pair. 141 | if (i == noderange-2) 142 | % This is last node pair, so y_coord is the below node of 143 | % last node pair 144 | y_nearest = nodelist(i+2); 145 | break; 146 | else 147 | % Skip to next node pair 148 | continue; 149 | end 150 | end 151 | end 152 | end 153 | -------------------------------------------------------------------------------- /ncorr_alg_seedanalysis.m: -------------------------------------------------------------------------------- 1 | function [seedinfo,convergence,outstate] = ncorr_alg_seedanalysis(reference,current,roi,num_region,pos_seed,radius,cutoff_diffnorm,cutoff_iteration,enabled_stepanalysis,subsettrunc,num_img,total_imgs) 2 | % This function attempts to calculate seeds for the region specified. 3 | % 4 | % Inputs -----------------------------------------------------------------% 5 | % reference - ncorr_class_img; used for calculations. 6 | % current - ncorr_class_img; used for calculations. 7 | % roi - ncorr_class_roi; ROI corresponding to the reference image. 8 | % num_region - integer; number corresponding to the region. 9 | % pos_seed - integer array; 1st column is the x-position of the seed; 2nd 10 | % is the y-position. Each row corresponds to a thread in ascending order 11 | % (i.e. the first row is the first thread). 12 | % radius - integer; radius of subset 13 | % cutoff_diffnorm - double; cutoff of norm of the difference vector 14 | % cutoff_iteration - integer; cutoff for number of iterations 15 | % enabled_stepanalysis - logical; if true, then process as many seeds as 16 | % possible. If false, process all the seeds. 17 | % subsettrunc - logical; if true, then enabled subset truncation 18 | % num_img - integer; number of reference image being analyzed 19 | % total_imgs - integer; total number of images being analyzed 20 | % 21 | % Outputs ----------------------------------------------------------------% 22 | % seedinfo - struct; contains 23 | % struct('paramvector',{},'num_region',{},'num_thread',{},'computepoints',{}) 24 | % convergence - struct; contains 25 | % struct('num_iterations',{},'diffnorm',{}) 26 | % outstate - integer; returns either out.cancelled, out.failed, or 27 | % out.success. 28 | % 29 | % Note that if step analysis is enabled, this function will return at least 30 | % one seed if outstate is set to success. If step analysis is disabled then 31 | % all images will be seeded if outstate is set to success. 32 | 33 | % Initialze outputs 34 | outstate = out.cancelled; 35 | seedinfo = struct('paramvector',{},'num_region',{},'num_thread',{},'computepoints',{}); % Contains seed information for the RG-DIC analysis 36 | convergence = struct('num_iterations',{},'diffnorm',{}); % Contains convergence info 37 | 38 | % Prepare buffers 39 | seedinfo_prelim = struct('paramvector',{},'num_region',{},'num_thread',{},'computepoints',{}); % Contains seed information for the RG-DIC analysis 40 | convergence_prelim = struct('num_iterations',{},'diffnorm',{}); % Contains convergence info 41 | 42 | % Set up waitbar 43 | h = waitbar((num_img)/total_imgs,['Processing seed(s) for image ' num2str(num_img+1) ' of ' num2str(total_imgs) '...'],'Name','Calculating...','WindowStyle','modal'); 44 | set(h,'CloseRequestFcn','setappdata(gcbf,''canceling'',1)'); 45 | setappdata(h,'canceling',0); 46 | 47 | % Iterate over current images and place seeds 48 | tic 49 | for i = 0:length(current)-1 50 | % Calculate seeds for this image and region. Note that for 51 | % ncorr_alg_calcseeds, outstate will either return success if all 52 | % seeds are successfully calculated for this image/region, or it 53 | % will return failed. 54 | [seedinfo_buffer,convergence_buffer,outstate_seeds] = ncorr_alg_calcseeds(reference.formatted(), ... 55 | current(i+1).formatted(), ... 56 | roi.formatted(), ... 57 | int32(num_region), ... 58 | int32(pos_seed), ... 59 | int32(radius), ... 60 | cutoff_diffnorm, ... 61 | int32(cutoff_iteration), ... 62 | logical(enabled_stepanalysis), ... 63 | logical(subsettrunc)); 64 | 65 | if (enabled_stepanalysis) 66 | % If seed placement fails or if the diffnorm or corrcoef for any 67 | % of the seeds in this image and region are greater than the 68 | % absolute cutoff or if this is the 2nd+ image and the number 69 | % of iterations is saturated for any of the seeds, then break. 70 | % Choose the 2nd image because if the number of iterations is 71 | % saturated, that doesnt necessarily mean its analyzed 72 | % incorrectly, but it means that the match is probably poor. 73 | 74 | % Set absolute cutoff for diffnorm and corrcoef 75 | cutoff_max_diffnorm = 0.1; 76 | cutoff_max_corrcoef = 0.5; 77 | 78 | corrcoef_buffer = vertcat(seedinfo_buffer.paramvector); 79 | corrcoef_buffer = corrcoef_buffer(:,9); 80 | if (outstate_seeds ~= out.success || ... 81 | any([convergence_buffer.diffnorm] > cutoff_max_diffnorm) || ... 82 | any(corrcoef_buffer > cutoff_max_corrcoef) || ... 83 | (i > 0 && any([convergence_buffer.num_iterations] == cutoff_iteration))) 84 | % Check if the number of seeds is zero for step analysis; 85 | % if so, then fail the analysis 86 | if (isempty(seedinfo_prelim)) 87 | outstate = out.failed; 88 | end 89 | 90 | break; 91 | end 92 | else 93 | % Step analysis isn't enabled. If any seed placements fail then 94 | % fail the whole analysis 95 | if (outstate_seeds ~= out.success) 96 | h_error = errordlg('Some seeds could not be analyzed. If high strain is anticipated, then try enabling the high strain step analysis.','Error','modal'); 97 | uiwait(h_error); 98 | 99 | outstate = out.failed; 100 | break; 101 | end 102 | end 103 | 104 | % Append seeds 105 | seedinfo_prelim(:,1,i+1) = seedinfo_buffer; 106 | convergence_prelim(:,1,i+1) = convergence_buffer; 107 | 108 | % See if analysis was cancelled by user 109 | if (getappdata(h,'canceling')) 110 | delete(h); 111 | % Exit 112 | return; 113 | end 114 | 115 | % Update waitbar 116 | waitbar((num_img+i+1)/total_imgs,h,['Processing seed(s) for image ' num2str(num_img+i+1) ' of ' num2str(total_imgs) '...']); 117 | end 118 | toc 119 | 120 | % Close wait bar 121 | delete(h); 122 | 123 | if (outstate ~= out.failed) 124 | % Assign outputs 125 | seedinfo = seedinfo_prelim; 126 | convergence = convergence_prelim; 127 | outstate = out.success; 128 | end 129 | end -------------------------------------------------------------------------------- /ncorr_alg_convertanalysis.m: -------------------------------------------------------------------------------- 1 | function [plots_disp_new,rois_new,outstate] = ncorr_alg_convertanalysis(img_new,imgs_old,plots_u_old,plots_v_old,rois_old,spacing,num_img,total_imgs) 2 | % This function is used to convert displacements from the old configuration 3 | % to the new configuration. Can be Lagrangian to Eulerian or vice-versa, 4 | % which is why "old" and "new" are used instead of Lagrangian and Eulerian. 5 | % 6 | % Inputs -----------------------------------------------------------------% 7 | % img_new - struct; Contains struct('imginfo',{},'roi',{}) in the new 8 | % configuration. 9 | % imgs_old - struct; Contains struct('imginfo',{},'roi',{}) in the old 10 | % configuration. 11 | % plots_u_old - cell; contains "old" u displacement fields 12 | % plots_v_old - cell; contains "old" v displacement fields 13 | % rois_old - ncorr_class_roi; contains ROIs corresponding to the 14 | % u and v plots. Note these are reduced by default. 15 | % spacing - integer; spacing parameter 16 | % num_img - integer; reference image number 17 | % total_imgs - integer; total number of images 18 | % 19 | % Outputs ----------------------------------------------------------------% 20 | % plots_disp_new - struct; contains struct('plot_u_new',{},'plot_v_new',{}) 21 | % rois_new - struct; contains ROIs corresponding to the "new" u and v 22 | % displacement plots 23 | % outstate - integer; returns either out.cancelled, out.failed, or 24 | % out.success. 25 | % 26 | % Returns failed if seed placement fails. 27 | 28 | % Initialize outputs 29 | outstate = out.cancelled; 30 | plots_disp_new = struct('plot_u_new',{},'plot_v_new',{}); 31 | rois_new = ncorr_class_roi.empty; 32 | 33 | % Extrapolate displacement plots to improve interpolation near the 34 | % boundary points. 35 | % border_interp is the border added to the displacements when they are 36 | % extrapolated. 37 | border_interp = 20; % MUST BE GREATER THAN OR EQUAL TO 2 38 | plots_u_interp_old = cell(0); 39 | plots_v_interp_old = cell(0); 40 | % Note that ncorr_alg_extrapdata will return a separate extrapolated 41 | % array for each region within a ROI. This is done to prevent 42 | % displacements from adjacent regions from influencing each other. 43 | % Also note that the ordering returned forms a correspondence between 44 | % the regions stored in rois_old. 45 | for i = 0:length(imgs_old)-1 46 | plots_u_interp_old{i+1} = ncorr_alg_extrapdata(plots_u_old{i+1},rois_old(i+1).formatted(),int32(border_interp)); 47 | plots_v_interp_old{i+1} = ncorr_alg_extrapdata(plots_v_old{i+1},rois_old(i+1).formatted(),int32(border_interp)); 48 | end 49 | 50 | % Convert displacement plots to B-spline coefficients; these are used 51 | % for interpolation. Make sure to do this for each region. 52 | for i = 0:length(imgs_old)-1 53 | for j = 0:length(rois_old(i+1).region)-1 54 | plots_u_interp_old{i+1}{j+1} = ncorr_class_img.form_bcoef(plots_u_interp_old{i+1}{j+1}); 55 | plots_v_interp_old{i+1}{j+1} = ncorr_class_img.form_bcoef(plots_v_interp_old{i+1}{j+1}); 56 | end 57 | end 58 | 59 | % Set Seeds ----------------------------------------------------------% 60 | % This will automatically try to seed as many regions as possible. 61 | roi_new_reduced = img_new.roi.reduce(spacing); 62 | convertseedinfo = cell(0); 63 | seedwindow = 1; 64 | for i = 0:length(imgs_old)-1 65 | % Will return either success or failed. Returns success if at 66 | % least one region is seeded. 67 | [convertseedinfo{i+1},outstate_convertseeds] = ncorr_alg_convertseeds(plots_u_old{i+1}, ... 68 | plots_v_old{i+1}, ... 69 | plots_u_interp_old{i+1}, ... 70 | plots_v_interp_old{i+1}, ... 71 | rois_old(i+1), ... 72 | roi_new_reduced, ... 73 | seedwindow, ... 74 | spacing, ... 75 | border_interp); 76 | 77 | % Must check to make sure seedinfo isnt empty 78 | if (outstate_convertseeds ~= out.success) 79 | % For now just fail the whole analysis since this will result 80 | % in an empty plot. 81 | h_error = errordlg('Seeding failed for the conversion analysis. Please rerun DIC analysis and make sure regions have a large contiguous region.','Error','modal'); 82 | uiwait(h_error); 83 | 84 | outstate = out.failed; 85 | break; 86 | end 87 | end 88 | 89 | if (outstate ~= out.failed) 90 | % Find displacements in new configuration ------------------------% 91 | % Format seeds 92 | convertseedinfo_f = convertseedinfo; 93 | for i = 0:length(convertseedinfo)-1 94 | for j = 0:length(convertseedinfo{i+1})-1 95 | convertseedinfo_f{i+1}(j+1).num_region_new = int32(convertseedinfo{i+1}(j+1).num_region_new); 96 | convertseedinfo_f{i+1}(j+1).num_region_old = int32(convertseedinfo{i+1}(j+1).num_region_old); 97 | end 98 | end 99 | 100 | % Initialize structs for output 101 | plots_disp_new_prelim = struct('plot_u_new',{},'plot_v_new',{}); 102 | rois_new_prelim = ncorr_class_roi.empty; 103 | for i = 0:length(imgs_old)-1 104 | % ncorr_alg_convert returns either success or cancelled. 105 | [plot_disp_new_buffer,outstate_convert] = ncorr_alg_convert(plots_u_interp_old{i+1}, ... 106 | plots_v_interp_old{i+1}, ... 107 | rois_old(i+1).formatted(), ... 108 | roi_new_reduced.formatted(), ... 109 | convertseedinfo_f{i+1}, ... 110 | int32(spacing), ... 111 | int32(border_interp), ... 112 | int32(num_img+i), ... 113 | int32(total_imgs)); 114 | % Check if analysis was cancelled 115 | if (outstate_convert ~= out.success) 116 | return; 117 | end 118 | 119 | % Store new displacement plots and then get the union of valid 120 | % points with roi_new_reduced. 121 | plots_disp_new_prelim(i+1).plot_u_new = plot_disp_new_buffer.plot_u_new; 122 | plots_disp_new_prelim(i+1).plot_v_new = plot_disp_new_buffer.plot_v_new; 123 | rois_new_prelim(i+1) = roi_new_reduced.get_union(plot_disp_new_buffer.plot_validpoints,0); 124 | end 125 | 126 | % Set outputs 127 | for i = 0:length(plots_disp_new_prelim)-1 128 | plots_disp_new(i+1) = plots_disp_new_prelim(i+1); 129 | rois_new(i+1) = rois_new_prelim(i+1); 130 | end 131 | outstate = out.success; 132 | end 133 | end 134 | -------------------------------------------------------------------------------- /standard_datatypes.cpp: -------------------------------------------------------------------------------- 1 | // These are function definitions used to obtain standard inputs to mex files 2 | // Note that all mx* functions are not thread safe. Also note that there are 3 | // some "iffy" things in place here, namely how ints and logicals are passed, 4 | // but the implementation used is simple and works on the majority of systems. 5 | 6 | #include 7 | #include "standard_datatypes.h" 8 | 9 | // ----------------------------------------------------------------------// 10 | // This is for string input ---------------------------------------------// 11 | // ----------------------------------------------------------------------// 12 | 13 | // NOT THREAD SAFE 14 | void get_string(std::string &string,const mxArray *mat_buf) { 15 | // NOTE: This is potentially dangerous because user could change string length 16 | string = std::string(mxArrayToString(mat_buf)); 17 | } 18 | 19 | // ----------------------------------------------------------------------// 20 | // This is for a double scalar input ------------------------------------// 21 | // ----------------------------------------------------------------------// 22 | 23 | // NOT THREAD SAFE 24 | void get_double_scalar(double &scalar,const mxArray *mat_buf) { 25 | // Check input 26 | if (mxIsClass(mat_buf,"double")) { 27 | if (mxGetN(mat_buf) == 1 && mxGetM(mat_buf) == 1) { 28 | // At this point, input is correct 29 | scalar = *mxGetPr(mat_buf); 30 | } else { 31 | mexErrMsgTxt("Double scalar is not of size == [1 1].\n"); 32 | } 33 | } else { 34 | mexErrMsgTxt("Double scalar is not double.\n"); 35 | } 36 | } 37 | 38 | // ----------------------------------------------------------------------// 39 | // This is for an integer scalar input ----------------------------------// 40 | // ----------------------------------------------------------------------// 41 | 42 | // NOT THREAD SAFE 43 | void get_integer_scalar(int &scalar,const mxArray *mat_buf) { 44 | // Check input - NOTE: that most systems use 32 bit ints. 45 | // To my knowledge both 32 bit and 64 bit unix and windows 46 | // use ILP32 (32 bit systems), LP64 (64 bit unix systems), 47 | // and LLP64 (64 bit windows) which specify 32 bit ints. 48 | // This is from stackoverflow. However, be aware that 49 | // int doesnt necessarily have to be 32 bit. 50 | if (mxIsClass(mat_buf,"int32")) { 51 | if (mxGetN(mat_buf) == 1 && mxGetM(mat_buf) == 1) { 52 | // At this point, input is correct 53 | scalar = *((int *)mxGetData(mat_buf)); 54 | } else { 55 | mexErrMsgTxt("Integer scalar is not of size == [1 1].\n"); 56 | } 57 | } else { 58 | mexErrMsgTxt("Integer scalar is not int32.\n"); 59 | } 60 | } 61 | 62 | // ----------------------------------------------------------------------// 63 | // This is for a logical scalar input -----------------------------------// 64 | // ----------------------------------------------------------------------// 65 | 66 | // NOT THREAD SAFE 67 | void get_logical_scalar(bool &scalar,const mxArray *mat_buf) { 68 | // Check input - NOTE: that proper implementation would 69 | // technically use mxLogical, but in both 32/64 bit linux 70 | // and windows systems I've tested, bool has worked. 71 | // Maybe update this in the future. 72 | if (mxIsClass(mat_buf,"logical")) { 73 | if (mxGetN(mat_buf) == 1 && mxGetM(mat_buf) == 1) { 74 | // At this point, input is correct 75 | scalar = *mxGetLogicals(mat_buf); 76 | } else { 77 | mexErrMsgTxt("Logical scalar is not of size == [1 1].\n"); 78 | } 79 | } else { 80 | mexErrMsgTxt("Logical scalar is not boolean.\n"); 81 | } 82 | } 83 | 84 | // ----------------------------------------------------------------------// 85 | // This is for a double array input -------------------------------------// 86 | // ----------------------------------------------------------------------// 87 | 88 | // THREAD SAFE 89 | class_double_array::class_double_array() { 90 | width = 0; 91 | height = 0; 92 | value = NULL; 93 | } 94 | 95 | // THREAD SAFE 96 | void class_double_array::reset() { 97 | for (int i=0; i 5 | #include 6 | #include 7 | #include 8 | #include "standard_datatypes.h" 9 | #include "ncorr_datatypes.h" 10 | 11 | // ----------------------------------------------------// 12 | // Main Class -----------------------------------------// 13 | // ----------------------------------------------------// 14 | 15 | class class_formthreaddiagram { 16 | public: 17 | // Constructor 18 | class_formthreaddiagram(mxArray *plhs [ ],const mxArray *prhs [ ]); 19 | 20 | // Methods 21 | void analysis(); 22 | void analyzepoint(const int &x_new,const int &y_new,const int &num_queue); 23 | 24 | private: 25 | // Properties 26 | // Inputs: 27 | class_double_array threaddiagram; // standard datatype 28 | class_double_array preview_threaddiagram; // standard datatype 29 | class_integer_array generators; // standard datatype 30 | class_logical_array regionmask; // standard datatype 31 | std::vector img; // ncorr datatype 32 | 33 | // Other: 34 | std::vector > > queue; 35 | std::vector coords_buffer; 36 | }; 37 | 38 | class_formthreaddiagram::class_formthreaddiagram(mxArray *plhs [ ],const mxArray *prhs[ ]) { 39 | // Form inputs ------------------------------------// 40 | // input 1: threaddiagram 41 | get_double_array(threaddiagram,prhs[0]); 42 | // input 2: preview_threaddiagram 43 | get_double_array(preview_threaddiagram,prhs[1]); 44 | // input 3: generators 45 | get_integer_array(generators,prhs[2]); 46 | // input 4: regionmask 47 | get_logical_array(regionmask,prhs[3]); 48 | // input 5: image (reduced) 49 | get_imgs(img,prhs[4]); 50 | 51 | // Check inputs - these are rudimentary 52 | if (img[0].gs.height == threaddiagram.height && 53 | img[0].gs.width == threaddiagram.width && 54 | img[0].gs.height == preview_threaddiagram.height && 55 | img[0].gs.width == preview_threaddiagram.width && 56 | img[0].gs.height == regionmask.height && 57 | img[0].gs.width == regionmask.width && 58 | generators.width == 2 || (generators.width == 0 && generators.height == 0)) { 59 | // Resize queue - one per generator 60 | queue.resize(generators.height); 61 | 62 | // Resize coords_buffer - this is used to push coordinates into the queue 63 | coords_buffer.resize(2); 64 | 65 | // Form/set Outputs -------------------------------// 66 | // outputs: None - inputs are modified in-place 67 | 68 | // Get Outputs ------------------------------------// 69 | // outputs: None - inputs are modified in-place 70 | } else { 71 | mexErrMsgTxt("Inputs incorrect.\n"); 72 | } 73 | } 74 | 75 | // ----------------------------------------------------// 76 | // Main Class Methods ---------------------------------// 77 | // ----------------------------------------------------// 78 | 79 | void class_formthreaddiagram::analysis() { 80 | // Initialize threaddiagram to -1; Note: the entire overlay is modified so it does not need to be reset 81 | for (int i=0; i 0) { 104 | // Cycle over each queue and add/paint four neighbors (up, right, down, left) 105 | for (int i=0; i<(int)queue.size(); i++) { 106 | // Make sure queue is not empty 107 | if (queue[i].size() > 0) { 108 | // analyzepoint adds points using push_back, so this preserves the front 109 | analyzepoint(queue[i].front()[0]-1,queue[i].front()[1],i); 110 | analyzepoint(queue[i].front()[0],queue[i].front()[1]-1,i); 111 | analyzepoint(queue[i].front()[0]+1,queue[i].front()[1],i); 112 | analyzepoint(queue[i].front()[0],queue[i].front()[1]+1,i); 113 | 114 | // Pop front element since it's finished 115 | queue[i].pop_front(); 116 | } 117 | } 118 | 119 | // Cycle over queue and recalculate total_queue 120 | total_queue = 0; 121 | for (int i=0; i<(int)queue.size(); i++) { 122 | total_queue += (int)queue[i].size(); 123 | } 124 | } 125 | 126 | // Go over entire mask to form preview_threaddiagram. Add a double highlight for points on the boundary between regions, 127 | // and then add just a normal highlight for the interior regions 128 | for (int i=0; i= 0 && y_new < threaddiagram.height && 170 | x_new >= 0 && x_new < threaddiagram.width) { 171 | // Check to make sure value is still -1 and within the regionmask 172 | if (threaddiagram.value[y_new + x_new*threaddiagram.height] == -1.0 && 173 | regionmask.value[y_new + x_new*regionmask.height]) { 174 | // Paint 175 | threaddiagram.value[y_new + x_new*threaddiagram.height] = (double)num_queue; 176 | 177 | // Store coords 178 | coords_buffer[0] = x_new; // X-coord 179 | coords_buffer[1] = y_new; // Y-coord 180 | 181 | // Push back 182 | queue[num_queue].push_back(coords_buffer); 183 | } 184 | } 185 | } 186 | 187 | void mexFunction(int nlhs,mxArray *plhs[ ],int nrhs,const mxArray *prhs[ ]) { 188 | if (nrhs == 5 && nlhs == 0) { 189 | // Create formthreaddiagram 190 | class_formthreaddiagram formthreaddiagram(plhs,prhs); 191 | 192 | // Run analysis and assign outputs 193 | formthreaddiagram.analysis(); 194 | } else { 195 | mexErrMsgTxt("Only five inputs and no output arguments.\n"); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /ncorr_alg_adddisp.cpp: -------------------------------------------------------------------------------- 1 | // This function adds displacement plots together 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "standard_datatypes.h" 8 | #include "ncorr_datatypes.h" 9 | #include "ncorr_lib.h" 10 | 11 | // ----------------------------------------------------// 12 | // Interp plot input ----------------------------------// 13 | // ----------------------------------------------------// 14 | 15 | void get_plot_interp(std::vector > &plot_interp,const mxArray *prhs) { 16 | // Check input - it's a cells containing cells. The outer cell corresponds to the image, while the inner cells correspond to regions 17 | if (mxIsClass(prhs,"cell") && mxGetM(prhs) == 1) { 18 | for (int i=0; i<(int)mxGetN(prhs); i++) { 19 | // Get content of cell corresponding to image number 20 | mxArray *mat_plot_interp_img = mxGetCell(prhs,i); 21 | std::vector plot_interp_img_template; 22 | if (mat_plot_interp_img != 0 && mxIsClass(mat_plot_interp_img,"cell") && mxGetM(mat_plot_interp_img) == 1) { 23 | for (int j=0; j<(int)mxGetN(mat_plot_interp_img); j++) { 24 | // Get content of cell corresponding to region 25 | mxArray *mat_plot_interp_region = mxGetCell(mat_plot_interp_img,j); 26 | if (mat_plot_interp_region != 0) { 27 | // Form interp plot 28 | class_double_array plot_interp_region_template; 29 | get_double_array(plot_interp_region_template,mat_plot_interp_region); 30 | 31 | // Store interp plot per region 32 | plot_interp_img_template.push_back(plot_interp_region_template); 33 | } else { 34 | mexErrMsgTxt("Some cell contents are empty.\n"); 35 | } 36 | } 37 | // Store interp plot per img 38 | plot_interp.push_back(plot_interp_img_template); 39 | } else { 40 | mexErrMsgTxt("Some cell contents are empty.\n"); 41 | } 42 | } 43 | } else { 44 | mexErrMsgTxt("Interp data must be a row vector of class 'cell'.\n"); 45 | } 46 | } 47 | 48 | // ----------------------------------------------------// 49 | // Main Class -----------------------------------------// 50 | // ----------------------------------------------------// 51 | 52 | class class_adddisp { 53 | public: 54 | // Constructor 55 | class_adddisp(mxArray *plhs [ ],const mxArray *prhs [ ]); 56 | 57 | // Methods: 58 | void analysis(); 59 | 60 | private: 61 | // Properties 62 | // Inputs: 63 | std::vector > plots_u_interp; // local datatype 64 | std::vector > plots_v_interp; // local datatype 65 | std::vector rois_interp; // ncorr datatype 66 | int border_interp; // standard datatype 67 | int spacing; // standard datatype 68 | int num_img; // standard datatype 69 | int total_imgs; // standard datatype 70 | 71 | // Outputs: 72 | class_double_array plot_u_added; 73 | class_double_array plot_v_added; 74 | class_logical_array plot_validpoints; 75 | double *outstate; 76 | 77 | // Other variables: 78 | class_waitbar waitbar; 79 | }; 80 | 81 | class_adddisp::class_adddisp(mxArray *plhs[ ],const mxArray *prhs[ ]) { 82 | // Get inputs -------------------------------------------------// 83 | // input 1: plots_u_interp 84 | get_plot_interp(plots_u_interp,prhs[0]); 85 | // input 2: plots_v_interp 86 | get_plot_interp(plots_v_interp,prhs[1]); 87 | // input 3: rois_interp - used for determining bounds when forward propagating 88 | get_rois(rois_interp,prhs[2]); 89 | // input 4: border_interp 90 | get_integer_scalar(border_interp,prhs[3]); 91 | // input 5: spacing 92 | get_integer_scalar(spacing,prhs[4]); 93 | // input 6: num_img 94 | get_integer_scalar(num_img,prhs[5]); 95 | // input 7: total_imgs 96 | get_integer_scalar(total_imgs,prhs[6]); 97 | 98 | // Check inputs - check sizes - UPDATE THIS LATER!!!!! 99 | 100 | // Form/set outputs -------------------------------------------// 101 | // output 1: plot_adddisp 102 | // Form deformation structure 103 | mwSize def_dims[2] = {1,1}; 104 | int def_numfields = 3; 105 | const char *def_fieldnames[] = {"plot_u_added","plot_v_added","plot_validpoints"}; 106 | plhs[0] = mxCreateStructArray(2, def_dims, def_numfields, def_fieldnames); 107 | 108 | // Form fields 109 | mxArray *mat_plot_u_added = mxCreateDoubleMatrix(rois_interp[0].mask.height, rois_interp[0].mask.width, mxREAL); 110 | mxArray *mat_plot_v_added = mxCreateDoubleMatrix(rois_interp[0].mask.height, rois_interp[0].mask.width, mxREAL); 111 | mxArray *mat_plot_validpoints = mxCreateLogicalMatrix(rois_interp[0].mask.height, rois_interp[0].mask.width); 112 | 113 | // Add fields to structure 114 | // Add u: 115 | mxSetFieldByNumber(plhs[0],0,0,mat_plot_u_added); 116 | // Add v: 117 | mxSetFieldByNumber(plhs[0],0,1,mat_plot_v_added); 118 | // Add valid points: 119 | mxSetFieldByNumber(plhs[0],0,2,mat_plot_validpoints); 120 | 121 | // output 2: outstate 122 | plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL); 123 | 124 | // Get outputs -----------------------------------------------// 125 | // output 1: plot_adddisp 126 | // u: 127 | get_double_array(plot_u_added,mat_plot_u_added); 128 | // v: 129 | get_double_array(plot_v_added,mat_plot_v_added); 130 | // plot_validpoints 131 | get_logical_array(plot_validpoints,mat_plot_validpoints); 132 | // output 2: outstate 133 | outstate = mxGetPr(plhs[1]); 134 | } 135 | 136 | // ----------------------------------------------------// 137 | // Main Class Methods ---------------------------------// 138 | // ----------------------------------------------------// 139 | 140 | void class_adddisp::analysis() { 141 | // Initialize outstate to cancelled 142 | *outstate = (double)CANCELLED; 143 | 144 | // Set up waitbar ----------------------------------------------// 145 | int computepoints = 0; 146 | for (int i=0; i<(int)rois_interp[0].region.size(); i++) { 147 | computepoints += rois_interp[0].region[i].totalpoints; 148 | } 149 | waitbar.start(num_img,total_imgs,computepoints); 150 | 151 | // Cycle over each region in the first ROI 152 | for (int i=0; i<(int)rois_interp[0].region.size(); i++) { 153 | // Cycle over each point in each region in the first ROI 154 | for (int j=0; j 0 && plots_u_interp[n][i].width > 0 && plots_v_interp[n][i].height > 0 && plots_v_interp[n][i].width > 0 && 174 | interp_qbs(u_interp_buf,x_cur_reduced,y_cur_reduced,plots_u_interp[n][i],rois_interp[n].mask,rois_interp[n].region[i].leftbound,rois_interp[n].region[i].upperbound,border_interp) == SUCCESS && 175 | interp_qbs(v_interp_buf,x_cur_reduced,y_cur_reduced,plots_v_interp[n][i],rois_interp[n].mask,rois_interp[n].region[i].leftbound,rois_interp[n].region[i].upperbound,border_interp) == SUCCESS) { 176 | // Update displacements 177 | u_interp += u_interp_buf; 178 | v_interp += v_interp_buf; 179 | 180 | // Update x_cur and y_cur 181 | x_cur_reduced += u_interp_buf/(spacing+1); 182 | y_cur_reduced += v_interp_buf/(spacing+1); 183 | } else { 184 | propsuccess = false; 185 | break; 186 | } 187 | } 188 | 189 | // If point was successfully propagated then store it 190 | if (propsuccess) { 191 | // Store final u_interp and v_interp value 192 | plot_u_added.value[y_ref_reduced + x_ref_reduced*plot_u_added.height] = u_interp; 193 | plot_v_added.value[y_ref_reduced + x_ref_reduced*plot_v_added.height] = v_interp; 194 | 195 | // Set this is a valid point 196 | plot_validpoints.value[y_ref_reduced + x_ref_reduced*plot_validpoints.height] = true; 197 | } 198 | 199 | // Update, check, and increment waitbar 200 | if (!waitbar.updateandcheck()) { 201 | // Waitbar was cancelled 202 | return; 203 | } 204 | waitbar.increment(); 205 | } 206 | } 207 | } 208 | } 209 | 210 | // At this point analysis has been completed successfully 211 | *outstate = (double)SUCCESS; 212 | } 213 | 214 | void mexFunction(int nlhs,mxArray *plhs[ ],int nrhs,const mxArray *prhs[ ]) { 215 | if (nrhs == 7 && nlhs == 2) { 216 | // Creat adddisp 217 | class_adddisp adddisp(plhs,prhs); 218 | 219 | // Run analysis 220 | adddisp.analysis(); 221 | } else { 222 | mexErrMsgTxt("Incorrect number of inputs.\n"); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /ncorr_alg_dispgrad.cpp: -------------------------------------------------------------------------------- 1 | // This function calculates displacement gradients given displacement field inputs 2 | 3 | #include 4 | #include 5 | #include 6 | #include "standard_datatypes.h" 7 | #include "ncorr_datatypes.h" 8 | #include "ncorr_lib.h" 9 | 10 | // ----------------------------------------------------// 11 | // Main Class -----------------------------------------// 12 | // ----------------------------------------------------// 13 | 14 | class class_dispgrad { 15 | public: 16 | // Constructor 17 | class_dispgrad(mxArray *plhs [ ],const mxArray *prhs [ ]); 18 | 19 | // Methods 20 | void analysis(); 21 | 22 | private: 23 | // Properties 24 | // Inputs: 25 | class_double_array plot_u; // standard datatype 26 | class_double_array plot_v; // standard datatype 27 | std::vector roi; // ncorr datatype 28 | int radius_strain; // standard datatype 29 | double pixtounits; // standard datatype 30 | int spacing; // standard datatype 31 | bool subsettrunc; // standard datatype 32 | int num_img; // standard datatype 33 | int total_imgs; // standard datatype 34 | 35 | // Outputs: 36 | class_double_array plot_dudx; 37 | class_double_array plot_dudy; 38 | class_double_array plot_dvdx; 39 | class_double_array plot_dvdy; 40 | class_logical_array plot_validpoints; 41 | double *outstate; 42 | 43 | // Other variables: 44 | class_waitbar waitbar; 45 | }; 46 | 47 | class_dispgrad::class_dispgrad(mxArray *plhs[ ],const mxArray *prhs[ ]){ 48 | // Get inputs ------------------------------------------------------// 49 | // input 1: u plot 50 | get_double_array(plot_u,prhs[0]); 51 | // input 2: v plot 52 | get_double_array(plot_v,prhs[1]); 53 | // input 3: ROI 54 | get_rois(roi,prhs[2]); 55 | // input 4: strain radius 56 | get_integer_scalar(radius_strain,prhs[3]); 57 | // input 6: pixtounits - this is units/pixel used as a conversion 58 | get_double_scalar(pixtounits,prhs[4]); 59 | // input 5: spacing 60 | get_integer_scalar(spacing,prhs[5]); 61 | // input 7: subsettrunc 62 | get_logical_scalar(subsettrunc,prhs[6]); 63 | // input 8: image id 64 | get_integer_scalar(num_img,prhs[7]); 65 | // input 9: total images 66 | get_integer_scalar(total_imgs,prhs[8]); 67 | 68 | // Check inputs - rudimentary check 69 | if (plot_u.width == roi[0].mask.width && plot_u.height == roi[0].mask.height && 70 | plot_v.width == roi[0].mask.width && plot_v.height == roi[0].mask.height) { 71 | // Set cirroi ---------------------------------------------------// 72 | // Only allocate one cirroi 73 | roi[0].set_cirroi(radius_strain,1); 74 | 75 | // Form/set outputs ---------------------------------------------// 76 | // output 1: plot_dispgrad 77 | // Form displacement gradient structure 78 | mwSize dims[2] = {1,1}; 79 | int numfields = 5; 80 | const char *fieldnames[] = {"plot_dudx","plot_dudy","plot_dvdx","plot_dvdy","plot_validpoints"}; 81 | plhs[0] = mxCreateStructArray(2,dims,numfields,fieldnames); 82 | 83 | // Form fields 84 | mxArray *mat_plot_dudx = mxCreateDoubleMatrix(roi[0].mask.height,roi[0].mask.width,mxREAL); 85 | mxArray *mat_plot_dudy = mxCreateDoubleMatrix(roi[0].mask.height,roi[0].mask.width,mxREAL); 86 | mxArray *mat_plot_dvdx = mxCreateDoubleMatrix(roi[0].mask.height,roi[0].mask.width,mxREAL); 87 | mxArray *mat_plot_dvdy = mxCreateDoubleMatrix(roi[0].mask.height,roi[0].mask.width,mxREAL); 88 | mxArray *mat_plot_validpoints = mxCreateLogicalMatrix(roi[0].mask.height,roi[0].mask.width); 89 | 90 | // Add fields to structure 91 | // add dudx: 92 | mxSetFieldByNumber(plhs[0],0,0,mat_plot_dudx); 93 | // add dudy: 94 | mxSetFieldByNumber(plhs[0],0,1,mat_plot_dudy); 95 | // add dvdx: 96 | mxSetFieldByNumber(plhs[0],0,2,mat_plot_dvdx); 97 | // add dvdy: 98 | mxSetFieldByNumber(plhs[0],0,3,mat_plot_dvdy); 99 | // add validpoints: 100 | mxSetFieldByNumber(plhs[0],0,4,mat_plot_validpoints); 101 | 102 | // output 2: outstate 103 | plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL); 104 | 105 | // Get outputs --------------------------------------------------// 106 | // output 1: plot_dispgrad 107 | // dudx: 108 | get_double_array(plot_dudx,mat_plot_dudx); 109 | // dudy: 110 | get_double_array(plot_dudy,mat_plot_dudy); 111 | // dvdx: 112 | get_double_array(plot_dvdx,mat_plot_dvdx); 113 | // dvdy: 114 | get_double_array(plot_dvdy,mat_plot_dvdy); 115 | // validpoints: 116 | get_logical_array(plot_validpoints,mat_plot_validpoints); 117 | // output 2: outstate 118 | outstate = mxGetPr(plhs[1]); 119 | } else { 120 | mexErrMsgTxt("Mask and displacements are not the same size.\n"); 121 | } 122 | } 123 | 124 | // ----------------------------------------------------// 125 | // Main Class Methods ---------------------------------// 126 | // ----------------------------------------------------// 127 | 128 | void class_dispgrad::analysis() { 129 | // Initialize outstate to cancelled 130 | *outstate = (double)CANCELLED; 131 | 132 | // Set up waitbar ----------------------------------------------// 133 | int computepoints = 0; 134 | for (int i=0; i<(int)roi[0].region.size(); i++) { 135 | computepoints += roi[0].region[i].totalpoints; 136 | } 137 | waitbar.start(num_img,total_imgs,computepoints); 138 | 139 | // Begin Analysis ----------------------------------------------// 140 | std::vector mat_LS(9,0); 141 | std::vector u_vec_LS(3,0); 142 | std::vector v_vec_LS(3,0); 143 | 144 | // Cycle over each region and calculate displacement gradients 145 | for (int i=0; i<(int)roi[0].region.size(); i++) { 146 | // Update cirroi 147 | roi[0].update_cirroi(i,0); 148 | for (int j=0; j G'x = y 208 | // Step 1: solve for y with forward substitution; y is stored in gradient_buffer 209 | forwardsub(u_vec_LS,mat_LS,3); 210 | forwardsub(v_vec_LS,mat_LS,3); 211 | 212 | // Step 2: solve for x with back substitution 213 | backwardsub(u_vec_LS,mat_LS,3); 214 | backwardsub(v_vec_LS,mat_LS,3); 215 | 216 | // Normalize values to account for spacing and displacement unit conversion 217 | for (int i=0; i<3; i++) { 218 | u_vec_LS[i] /= (double)(spacing+1)*pixtounits; 219 | v_vec_LS[i] /= (double)(spacing+1)*pixtounits; 220 | } 221 | 222 | // Now store displacement gradients 223 | plot_dudx.value[y+x*plot_dudx.height] = u_vec_LS[0]; 224 | plot_dudy.value[y+x*plot_dudy.height] = u_vec_LS[1]; 225 | plot_dvdx.value[y+x*plot_dvdx.height] = v_vec_LS[0]; 226 | plot_dvdy.value[y+x*plot_dvdy.height] = v_vec_LS[1]; 227 | plot_validpoints.value[y+x*plot_validpoints.height] = true; 228 | } 229 | 230 | // Update, check, and increment waitbar 231 | if (!waitbar.updateandcheck()) { 232 | // Waitbar was cancelled 233 | return; 234 | } 235 | waitbar.increment(); 236 | } 237 | } 238 | } 239 | } 240 | 241 | // At this point analysis has been completed successfully 242 | *outstate = (double)SUCCESS; 243 | } 244 | 245 | void mexFunction(int nlhs,mxArray *plhs[ ],int nrhs,const mxArray *prhs[ ]) { 246 | if (nrhs == 9 && nlhs == 2) { 247 | // Create dispgrad 248 | class_dispgrad dispgrad(plhs,prhs); 249 | 250 | // Run analysis 251 | dispgrad.analysis(); 252 | } else { 253 | mexErrMsgTxt("Incorrect number of inputs or outputs.\n"); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /ncorr_alg_formmask.cpp: -------------------------------------------------------------------------------- 1 | // This function creates a mask (logical array) basked on drawobject inputs, which can be a rectangle, ellipse, or polygon 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "standard_datatypes.h" 8 | #include "ncorr_datatypes.h" 9 | #include "ncorr_lib.h" 10 | 11 | // ----------------------------------------------------// 12 | // Drawobjects input ----------------------------------// 13 | // ----------------------------------------------------// 14 | 15 | struct local_struct_drawobjects { 16 | // Constructor 17 | local_struct_drawobjects() { 18 | } 19 | 20 | // Properties 21 | class_double_array pos_imroi; 22 | std::string type; 23 | std::string addorsub; 24 | }; 25 | 26 | void get_drawobjects(std::vector &drawobjects,const mxArray *prhs) { 27 | // Check input 28 | if ((mxGetM(prhs) == 1 || (mxGetM(prhs) == 0 && mxGetN(prhs) == 0)) && mxIsClass(prhs,"struct")) { 29 | for (int i=0; i<(int)mxGetN(prhs); i++) { 30 | mxArray *mat_pos_imroi = mxGetField(prhs,i,"pos_imroi"); 31 | mxArray *mat_type = mxGetField(prhs,i,"type"); 32 | mxArray *mat_addorsub = mxGetField(prhs,i,"addorsub"); 33 | if (mat_pos_imroi != 0 && 34 | mat_type != 0 && 35 | mat_addorsub != 0) { 36 | // Form drawobject 37 | local_struct_drawobjects drawobject_template; 38 | get_double_array(drawobject_template.pos_imroi,mat_pos_imroi); 39 | get_string(drawobject_template.type,mat_type); 40 | get_string(drawobject_template.addorsub,mat_addorsub); 41 | 42 | // Test inputs 43 | if (drawobject_template.type.compare(0,4,"poly") == 0) { 44 | // Input is 'poly' - width must be 2 and height must not be zero. If height is zero it causes a crash 45 | if (drawobject_template.pos_imroi.width != 2 || drawobject_template.pos_imroi.height == 0) { 46 | mexErrMsgTxt("'poly' width must be 2 with a height of at least 1.\n"); 47 | } 48 | } else if (drawobject_template.type.compare(0,4,"rect") == 0) { 49 | // Input is 'rect' - width must be 4 and height must be 1 50 | if ((drawobject_template.pos_imroi.width != 4) || (drawobject_template.pos_imroi.height != 1)) { 51 | mexErrMsgTxt("'rect' width must be 4 and height must be 1.\n"); 52 | } 53 | } else if (drawobject_template.type.compare(0,7,"ellipse") == 0) { 54 | // Input is 'ellipse' - width must be 4 and height must be 1 55 | if ((drawobject_template.pos_imroi.width != 4) || (drawobject_template.pos_imroi.height != 1)) { 56 | mexErrMsgTxt("'ellipse' width must be 4 and height must be 1.\n"); 57 | } 58 | } else { 59 | mexErrMsgTxt("'type' field must be either 'poly', 'rect' or 'ellipse'.\n"); 60 | } 61 | if (drawobject_template.addorsub.compare(0,3,"add") != 0 && drawobject_template.addorsub.compare(0,3,"sub") != 0) { 62 | mexErrMsgTxt("'addorsub' field must be either 'add' or 'sub'.\n"); 63 | } 64 | 65 | // Store drawobject_template 66 | drawobjects.push_back(drawobject_template); 67 | } else { 68 | mexErrMsgTxt("Some fields are missing for drawobjects.\n"); 69 | } 70 | } 71 | } else { 72 | mexErrMsgTxt("drawobjects must be a row vector or empty of class 'struct'.\n"); 73 | } 74 | } 75 | 76 | // ----------------------------------------------------// 77 | // Other Structures -----------------------------------// 78 | // ----------------------------------------------------// 79 | 80 | struct local_struct_node { 81 | // Constructor 82 | local_struct_node(const int length_buffer) { 83 | length = 0; 84 | values.resize(length_buffer); 85 | } 86 | 87 | // Properties 88 | std::vector values; 89 | int length; 90 | }; 91 | 92 | // ----------------------------------------------------// 93 | // Main Class -----------------------------------------// 94 | // ----------------------------------------------------// 95 | 96 | class class_formmask { 97 | public: 98 | // Constructor 99 | class_formmask(mxArray *plhs[ ],const mxArray *prhs [ ]); 100 | 101 | // Methods 102 | void analysis(); 103 | 104 | private: 105 | // Properties 106 | // Inputs: 107 | std::vector drawobjects; // local datatype 108 | class_logical_array mask; // standard datatype 109 | 110 | // Outputs: None 111 | }; 112 | 113 | class_formmask::class_formmask(mxArray *plhs[ ],const mxArray *prhs[ ]) { 114 | // Get inputs ------------------------------------// 115 | // input 1: drawobjects 116 | get_drawobjects(drawobjects,prhs[0]); 117 | // input 2: mask 118 | get_logical_array(mask,prhs[1]); 119 | 120 | // Form/set Outputs -------------------------------// 121 | // outputs: None - inputs are modified in-place 122 | 123 | // Get Outputs ------------------------------------// 124 | // outputs: None - inputs are modified in-place 125 | } 126 | 127 | // ----------------------------------------------------// 128 | // Main Class Methods ---------------------------------// 129 | // ----------------------------------------------------// 130 | 131 | void class_formmask::analysis() { 132 | // Reset values 133 | mask.reset(); 134 | 135 | // Find maximum possible number of nodes per column for polygonal regions. 136 | // Check each drawobject and take the max over all of them. This buffer 137 | // holds the nodes to paint for a column. I'm finding the max here so 138 | // I don't have to constantly resize it. 139 | int length_polynode_max = 0; 140 | for (int i=0; i<(int)drawobjects.size(); i++) { 141 | if (drawobjects[i].type.compare(0,4,"poly") == 0) { 142 | // The number of points in the polygon is the absolute upperbound for the number of nodes 143 | if (drawobjects[i].pos_imroi.height > length_polynode_max) { 144 | length_polynode_max = drawobjects[i].pos_imroi.height; 145 | } 146 | } 147 | } 148 | // Form node buffer - this buffer will hold the nodes to paint in a column for polygonal regions 149 | local_struct_node polynode_buffer(length_polynode_max); 150 | 151 | // "add" drawobjects will paint the area with 1's while "sub" drawobjects will paint the area with 0's 152 | // Cycle over drawobjects and paint mask depending on fields 153 | for (int i=0; i<(int)drawobjects.size(); i++) { 154 | if (drawobjects[i].type.compare(0,4,"poly") == 0) { 155 | // Get bounds from max and min of x coordinates in pos_imroi. Make sure they do not go outside the mask 156 | // Note if polygon is empty then this part will freeze. Possibly fix later. 157 | int leftbound = (int)std::max(ceil(*std::min_element(drawobjects[i].pos_imroi.value,drawobjects[i].pos_imroi.value+drawobjects[i].pos_imroi.height)),0.0); 158 | int rightbound = (int)std::min(floor(*std::max_element(drawobjects[i].pos_imroi.value,drawobjects[i].pos_imroi.value+drawobjects[i].pos_imroi.height)),(double)mask.width-1.0); 159 | 160 | if (drawobjects[i].addorsub.compare(0,3,"add") == 0) { 161 | for (int j=leftbound; j<=rightbound; j++) { 162 | int x = j; 163 | polynode_buffer.length = 0; 164 | 165 | // Find nodes for polygon - code from alienryderflex.com 166 | int idx_node = drawobjects[i].pos_imroi.height-1; // Subtract one since the points given are closed 167 | for (int k=0; k= x) || drawobjects[i].pos_imroi.value[idx_node] < x && drawobjects[i].pos_imroi.value[k] >= x) { 169 | polynode_buffer.values[polynode_buffer.length] = (int)ncorr_round(drawobjects[i].pos_imroi.value[k+drawobjects[i].pos_imroi.height]+(x-drawobjects[i].pos_imroi.value[k])/(drawobjects[i].pos_imroi.value[idx_node]-drawobjects[i].pos_imroi.value[k])*(drawobjects[i].pos_imroi.value[idx_node+drawobjects[i].pos_imroi.height]-drawobjects[i].pos_imroi.value[k+drawobjects[i].pos_imroi.height])); 170 | ++polynode_buffer.length; 171 | } 172 | idx_node = k; 173 | } 174 | 175 | // Sort nodes 176 | std::sort(polynode_buffer.values.begin(),polynode_buffer.values.begin()+polynode_buffer.length); 177 | 178 | // Paint nodes - Must check to make sure y coords do not exceed mask 179 | for (int k=0; k= mask.height) { 181 | // top node is beyond the bottom of the mask 182 | break; 183 | } 184 | if (polynode_buffer.values[k+1] > 0) { 185 | // bottom node comes after the top of the mask 186 | if (polynode_buffer.values[k] < 0) { 187 | // top node comes before the top of the mask 188 | polynode_buffer.values[k] = 0; 189 | } 190 | if (polynode_buffer.values[k+1] >= mask.height) { 191 | // bottom node comes after the bottom of the mask 192 | polynode_buffer.values[k+1] = mask.height-1; 193 | } 194 | // At this point the nodes are within the mask bounds, so paint them 195 | for (int l=polynode_buffer.values[k]; l<=polynode_buffer.values[k+1]; l++) { 196 | int y = l; 197 | mask.value[y + x*mask.height] = true; 198 | } 199 | } 200 | } 201 | } 202 | } else if (drawobjects[i].addorsub.compare(0,3,"sub") == 0) { 203 | for (int j=leftbound; j<=rightbound; j++) { 204 | int x = j; 205 | polynode_buffer.length = 0; 206 | 207 | //Find nodes for polygon - code from alienryderflex.com 208 | int idx_node = drawobjects[i].pos_imroi.height-1; // Subtract one since the points given are closed 209 | for (int k=0; k= x) || drawobjects[i].pos_imroi.value[idx_node] < x && drawobjects[i].pos_imroi.value[k] >= x) { 211 | polynode_buffer.values[polynode_buffer.length] = (int)ncorr_round(drawobjects[i].pos_imroi.value[k+drawobjects[i].pos_imroi.height]+(x-drawobjects[i].pos_imroi.value[k])/(drawobjects[i].pos_imroi.value[idx_node]-drawobjects[i].pos_imroi.value[k])*(drawobjects[i].pos_imroi.value[idx_node+drawobjects[i].pos_imroi.height]-drawobjects[i].pos_imroi.value[k+drawobjects[i].pos_imroi.height])); 212 | ++polynode_buffer.length; 213 | } 214 | idx_node = k; 215 | } 216 | 217 | // Sort nodes 218 | std::sort(polynode_buffer.values.begin(),polynode_buffer.values.begin()+polynode_buffer.length); 219 | 220 | // Paint nodes - Must check to make sure y coords do not exceed mask 221 | for (int k=0; k= mask.height) { 223 | // top node is beyond the bottom of the mask 224 | break; 225 | } 226 | if (polynode_buffer.values[k+1] > 0) { 227 | // bottom node comes after the top of the mask 228 | if (polynode_buffer.values[k] < 0) { 229 | // top node comes before the top of the mask 230 | polynode_buffer.values[k] = 0; 231 | } 232 | if (polynode_buffer.values[k+1] >= mask.height) { 233 | // bottom node comes after the bottom of the mask 234 | polynode_buffer.values[k+1] = mask.height-1; 235 | } 236 | // At this point the nodes are within the mask bounds, so paint them 237 | for (int l=polynode_buffer.values[k]; l<=polynode_buffer.values[k+1]; l++) { 238 | int y = l; 239 | mask.value[y + x*mask.height] = false; 240 | } 241 | } 242 | } 243 | } 244 | } 245 | } else if (drawobjects[i].type.compare(0,4,"rect") == 0) { 246 | // WIDTH IS NOT TRADITIONAL PIXEL WIDTH - it is "already subtracted by 1" 247 | int upperbound = (int)std::max(ceil(drawobjects[i].pos_imroi.value[1]),0.0); 248 | int lowerbound = (int)std::min(floor(drawobjects[i].pos_imroi.value[1]+drawobjects[i].pos_imroi.value[3]),(double)mask.height-1.0); 249 | int leftbound = (int)std::max(ceil(drawobjects[i].pos_imroi.value[0]),0.0); 250 | int rightbound = (int)std::min(floor(drawobjects[i].pos_imroi.value[0]+drawobjects[i].pos_imroi.value[2]),(double)mask.width-1.0); 251 | 252 | if (drawobjects[i].addorsub.compare(0,3,"add") == 0) { 253 | for (int j=leftbound; j<=rightbound; j++) { 254 | int x = j; 255 | // Paint nodes 256 | for (int k=upperbound; k<=lowerbound; k++) { 257 | int y = k; 258 | mask.value[y + x*mask.height] = true; 259 | } 260 | } 261 | } else if (drawobjects[i].addorsub.compare(0,3,"sub") == 0) { 262 | for (int j=leftbound; j<=rightbound; j++) { 263 | int x = j; 264 | // Paint nodes 265 | for (int k=upperbound; k<=lowerbound; k++) { 266 | int y = k; 267 | mask.value[y + x*mask.height] = false; 268 | } 269 | } 270 | } 271 | } else if (drawobjects[i].type.compare(0,7,"ellipse") == 0) { 272 | // WIDTH IS NOT TRADITIONAL PIXEL WIDTH - it is already subtracted by 1 273 | int leftbound = (int)std::max(ceil(drawobjects[i].pos_imroi.value[0]),0.0); 274 | int rightbound = (int)std::min(floor(drawobjects[i].pos_imroi.value[0]+drawobjects[i].pos_imroi.value[2]),(double)mask.width-1.0); 275 | double a = drawobjects[i].pos_imroi.value[2]/2.0; // half-width 276 | double b = drawobjects[i].pos_imroi.value[3]/2.0; // half-height 277 | 278 | if (drawobjects[i].addorsub.compare(0,3,"add") == 0) { 279 | for (int j=leftbound; j<=rightbound; j++) { 280 | int x = j; 281 | int upperbound = (int)std::max(ceil(drawobjects[i].pos_imroi.value[1]+b-b*sqrt(1.0-pow((((double)j-drawobjects[i].pos_imroi.value[0]-a)/a),2))),0.0); 282 | int lowerbound = (int)std::min(floor(drawobjects[i].pos_imroi.value[1]+b+b*sqrt(1.0-pow((((double)j-drawobjects[i].pos_imroi.value[0]-a)/a),2))),double(mask.height)-1.0); 283 | 284 | //Paint nodes 285 | for (int k=upperbound; k<=lowerbound; k++) { 286 | int y = k; 287 | mask.value[y + x*mask.height] = true; 288 | } 289 | } 290 | } else if (drawobjects[i].addorsub.compare(0,3,"sub") == 0) { 291 | for (int j=leftbound; j<=rightbound; j++) { 292 | int x = j; 293 | int upperbound = (int)std::max(ceil(drawobjects[i].pos_imroi.value[1]+b-b*sqrt(1.0-pow((((double)j-drawobjects[i].pos_imroi.value[0]-a)/a),2))),0.0); 294 | int lowerbound = (int)std::min(floor(drawobjects[i].pos_imroi.value[1]+b+b*sqrt(1.0-pow((((double)j-drawobjects[i].pos_imroi.value[0]-a)/a),2))),double(mask.height)-1.0); 295 | 296 | //Paint nodes 297 | for (int k=upperbound; k<=lowerbound; k++) { 298 | int y = k; 299 | mask.value[y + x*mask.height] = false; 300 | } 301 | } 302 | } 303 | } 304 | } 305 | } 306 | 307 | void mexFunction(int nlhs,mxArray *plhs[ ],int nrhs,const mxArray *prhs[ ]) { 308 | // Make sure number of input and output arguments are correct 309 | if (nrhs == 2 && nlhs == 0) { 310 | // Create formmask 311 | class_formmask formmask(plhs,prhs); 312 | 313 | // Run analysis and assign outputs 314 | formmask.analysis(); 315 | } else { 316 | mexErrMsgTxt("Only two inputs and no output arguments allowed.\n"); 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /ncorr_alg_dicanalysis.m: -------------------------------------------------------------------------------- 1 | function [displacements,rois_dic,seedinfo,outstate] = ncorr_alg_dicanalysis(imgs,radius,spacing,cutoff_diffnorm,cutoff_iteration,total_threads,enabled_stepanalysis,subsettrunc,num_img,total_imgs,pos_parent,params_init) 2 | % This function performs RG-DIC. 3 | % 4 | % Inputs -----------------------------------------------------------------% 5 | % imgs - struct; Contains struct('imginfo',{},'roi',{}). Contains the 6 | % reference and current images packaged together. The imginfo field 7 | % contains an ncorr_class_img and the ROI field contains an ncorr_class_roi. 8 | % radius - integer; subset radius 9 | % spacing - integer; subset spacing 10 | % cutoff_diffnorm - double; cutoff for the norm of difference vector 11 | % cutoff_iteration - integer; cutoff for the number of IC-GN iterations 12 | % total_threads - integer; total number of threads 13 | % enabled_stepanalysis - logical; if true, then process as many seeds as 14 | % possible. If false, process all the seeds. 15 | % subsettrunc - logical; if true, then subset truncation is enabled 16 | % num_img - integer; reference image number 17 | % total_imgs - integer; total number of images 18 | % pos_parent - integer array; this is the position of the parent figure 19 | % which determines where to position this figure 20 | % params_init - struct; contains struct('paramvector',{},'num_region',{}, 21 | % 'num_thread',{},'computepoints',{}). If its not empty, it contains last 22 | % set of seeds used for the previous iteration 23 | % 24 | % Outputs ----------------------------------------------------------------% 25 | % displacements - struct; contains 26 | % struct('plot_u',{},'plot_v',{},'plot_corrcoef',{},'plot_validpoints',{}) 27 | % rois_dic - ncorr_class_img; ROIs after being unioned with reference roi 28 | % and validpoints from DIC analysis. 29 | % seedinfo - struct; contains struct('paramvector',{},'num_region',{}, 30 | % 'num_thread',{},'computepoints',{}) 31 | % outstate - integer; returns either out.cancelled, out.failed, or 32 | % out.success. 33 | % 34 | % outstate will only return failed if ncorr_alg_rgdic throws an exception. 35 | 36 | % Initialize outputs 37 | outstate = out.cancelled; 38 | displacements = struct('plot_u',{},'plot_v',{},'plot_corrcoef',{},'plot_validpoints',{}); 39 | rois_dic = ncorr_class_roi.empty; 40 | seedinfo = struct('paramvector',{},'num_region',{},'num_thread',{},'computepoints',{}); 41 | 42 | % --------------------------------------------------------------------% 43 | % Get Seeds ----------------------------------------------------------% 44 | % --------------------------------------------------------------------% 45 | 46 | if (isempty(params_init)) 47 | % Get new seeds manually through GUI. outstate_seeds will either be 48 | % success or cancelled. 49 | [seedinfo_prelim,threaddiagram,outstate_seeds] = ncorr_gui_seedanalysis(imgs(1).imginfo, ... 50 | [imgs(2:end).imginfo], ... 51 | imgs(1).roi, ... 52 | radius, ... 53 | spacing, ... 54 | cutoff_diffnorm, ... 55 | cutoff_iteration, ... 56 | total_threads, ... 57 | enabled_stepanalysis, ... 58 | subsettrunc, ... 59 | num_img, ... 60 | total_imgs, ... 61 | pos_parent); 62 | % See if analysis was cancelled 63 | if (outstate_seeds ~= out.success) 64 | return; 65 | end 66 | else 67 | % Use params init to place seeds directly 68 | % Initialize seedinfo_prelim 69 | seedinfo_prelim = struct('paramvector',{},'num_region',{},'num_thread',{},'computepoints',{}); 70 | 71 | % Initialize Thread diagram buffers - some buffers are unused 72 | ref_reduced = imgs(1).imginfo.reduce(spacing); % Unused 73 | roi_reduced = imgs(1).roi.reduce(spacing); % Unused 74 | preview_threaddiagram = zeros(size(roi_reduced.mask)); % Unused 75 | threaddiagram_buffer = -ones(size(roi_reduced.mask)); 76 | threaddiagram = -ones(size(roi_reduced.mask)); % This must be negative 1 77 | 78 | % Calculate seeds - cycle over regions 79 | num_imgs_success = inf; % Initialize to impossibly high value 80 | manualseed = false; % Set this parameter true if there is trouble. This causes the user to reselect the seeds manually through a GUI. 81 | % Cycle over regions 82 | for i = 0:size(params_init,2)-1 83 | % Get updated seed positions based on seed locations of the 84 | % last successfully seeded image for this region 85 | % Initialize 86 | pos_seed = zeros(size(params_init,1),2); 87 | % Cycle over threads 88 | for j = 0:size(params_init,1)-1 89 | % Must round displacements so that the updated position 90 | % lies properly on "spaced grid" - THIS IS IMPORTANT!!! 91 | pos_seed(j+1,1) = params_init(j+1,i+1,end).paramvector(1) + round(params_init(j+1,i+1,end).paramvector(3)/(spacing+1))*(spacing+1); 92 | pos_seed(j+1,2) = params_init(j+1,i+1,end).paramvector(2) + round(params_init(j+1,i+1,end).paramvector(4)/(spacing+1))*(spacing+1); 93 | end 94 | 95 | % Get num_region - just use seed from the first thread 96 | num_region = params_init(1,i+1,end).num_region; 97 | 98 | % Make sure seed positions are within the ROI and are unique. 99 | % If they arent then prompt the user and have them replace the 100 | % seeds manually. 101 | regionmask = roi_reduced.get_regionmask(num_region); 102 | if (size(pos_seed,1) == size(unique(pos_seed,'rows'),1) && ... 103 | all(pos_seed(:,1)./(spacing+1) >= 0) && all(pos_seed(:,1)./(spacing+1) < size(regionmask,2)) && ... 104 | all(pos_seed(:,2)./(spacing+1) >= 0) && all(pos_seed(:,2)./(spacing+1) < size(regionmask,1)) && ... 105 | all(regionmask(sub2ind(size(regionmask),pos_seed(:,2)/(spacing+1)+1,pos_seed(:,1)/(spacing+1)+1)))) 106 | % Get seeds - outstate will be successful if at least one 107 | % image is seeded 108 | [seedinfo_buffer,convergence_buffer,outstate_seeds] = ncorr_alg_seedanalysis(imgs(1).imginfo, ... 109 | [imgs(2:end).imginfo], ... 110 | imgs(1).roi, ... 111 | num_region, ... 112 | pos_seed, ... 113 | radius, ... 114 | cutoff_diffnorm, ... 115 | cutoff_iteration, ... 116 | enabled_stepanalysis, ... 117 | subsettrunc, ... 118 | num_img, ... 119 | total_imgs); %#ok 120 | % See if analysis was cancelled 121 | if (outstate_seeds == out.cancelled) 122 | return; 123 | end 124 | 125 | % See if analysis was successful or failed 126 | if (outstate_seeds == out.success) 127 | % Form thread diagram for these seeds 128 | ncorr_alg_formthreaddiagram(threaddiagram_buffer,preview_threaddiagram,int32(pos_seed/(spacing+1)),regionmask,ref_reduced.formatted()); 129 | 130 | % Get compute points 131 | for j = 0:size(seedinfo_buffer,3)-1 132 | for k = 0:size(seedinfo_buffer,1)-1 133 | seedinfo_buffer(k+1,1,j+1).computepoints = length(find(threaddiagram_buffer == k)); 134 | end 135 | end 136 | else 137 | % Seed analysis failed. Alert user and then ask him/her 138 | % to manually place seeds. 139 | h_error = errordlg('Not a single image was seeded correctly; please replace seeds manually.','Error','modal'); 140 | uiwait(h_error); 141 | 142 | manualseed = true; 143 | end 144 | else 145 | % Alert user 146 | h_error = errordlg('One or more seeds went outside of the ROI **OR** converged on top of each other, please replace manually. If this happens often then dont place seeds near each other or near the boundary.','Error','modal'); 147 | uiwait(h_error); 148 | manualseed = true; 149 | end 150 | 151 | if (~manualseed) 152 | % Take minimum of num_imgs_success and 153 | % seedinfo. Buffer can be more or less than 154 | % num_imgs_success 155 | num_imgs_success = min(num_imgs_success, size(seedinfo_buffer,3)); 156 | 157 | % Clear out other images in buffer and prelim 158 | if (~isempty(seedinfo_prelim)) 159 | seedinfo_prelim = seedinfo_prelim(:,:,1:num_imgs_success); 160 | end 161 | seedinfo_buffer = seedinfo_buffer(:,:,1:num_imgs_success); 162 | 163 | % Append seedinfo_buffer - append along 2nd dimension 164 | seedinfo_prelim = horzcat(seedinfo_prelim,seedinfo_buffer); %#ok 165 | 166 | % Merge threaddiagram from previous iteration 167 | threaddiagram(threaddiagram_buffer ~= -1) = threaddiagram_buffer(threaddiagram_buffer ~= -1); 168 | else 169 | % If there's a problem, just ask user to manually reset 170 | % seeds. Outstate will either be success or cancelled. 171 | [seedinfo_prelim,threaddiagram,outstate_seeds] = ncorr_gui_seedanalysis(imgs(1).imginfo, ... 172 | [imgs(2:end).imginfo], ... 173 | imgs(1).roi, ... 174 | radius, ... 175 | spacing, ... 176 | cutoff_diffnorm, ... 177 | cutoff_iteration, ... 178 | total_threads, ... 179 | enabled_stepanalysis, ... 180 | subsettrunc, ... 181 | num_img, ... 182 | total_imgs, ... 183 | pos_parent); 184 | 185 | % Check if analysis was cancelled 186 | if (outstate_seeds ~= out.success) 187 | return; 188 | end 189 | 190 | % Break since seeds for all regions are placed in GUI. 191 | break; 192 | end 193 | end 194 | 195 | % Debug ----------------------------------------------------------% 196 | %{ 197 | figure, imshow(threaddiagram,[]); hold on; 198 | for i = 0:size(seedinfo_prelim,2)-1 199 | for j = 0:size(seedinfo_prelim,1)-1 200 | plot(seedinfo_prelim(j+1,i+1,1).paramvector(1)/(spacing+1),seedinfo_prelim(j+1,i+1,1).paramvector(2)/(spacing+1),'ro'); 201 | end 202 | end 203 | hold off; 204 | %} 205 | % ----------------------------------------------------------------% 206 | end 207 | 208 | % --------------------------------------------------------------------% 209 | % Perform DIC Analysis -----------------------------------------------% 210 | % --------------------------------------------------------------------% 211 | 212 | % Format Seeds ---------------------------------------------------% 213 | seedinfo_prelim_f = seedinfo_prelim; 214 | for i = 0:size(seedinfo_prelim,1)-1 215 | for j = 0:size(seedinfo_prelim,2)-1 216 | for k = 0:size(seedinfo_prelim,3)-1 217 | seedinfo_prelim_f(i+1,j+1,k+1).num_region = int32(seedinfo_prelim(i+1,j+1,k+1).num_region); 218 | seedinfo_prelim_f(i+1,j+1,k+1).num_thread = int32(seedinfo_prelim(i+1,j+1,k+1).num_thread); 219 | seedinfo_prelim_f(i+1,j+1,k+1).computepoints = int32(seedinfo_prelim(i+1,j+1,k+1).computepoints); 220 | end 221 | end 222 | end 223 | 224 | % Begin DIC analysis ---------------------------------------------% 225 | displacements_prelim = struct('plot_u',{},'plot_v',{},'plot_corrcoef',{},'plot_validpoints',{}); 226 | rois_dic_prelim = ncorr_class_roi.empty; 227 | for i = 0:size(seedinfo_prelim,3)-1 228 | % Put in try block because DIC can run 229 | % out of memory 230 | try 231 | tic 232 | % No need to send in the number of regions or total number 233 | % of threads because this is encoded in the size of the 234 | % seeds. 235 | % FYI: seeinfo_prelim(i,j,k) where i refers to the thread 236 | % number, j refers to the region, and k refers to the image. 237 | % ncorr_alg_rgdic will either return success or cancelled or 238 | % return an exception. 239 | [displacements_prelim(i+1),outstate_dic] = ncorr_alg_rgdic(imgs(1).imginfo.formatted(), ... 240 | imgs(i+2).imginfo.formatted(), ... 241 | imgs(1).roi.formatted(), ... 242 | seedinfo_prelim_f(:,:,i+1), ... 243 | int32(threaddiagram), ... 244 | int32(radius), ... 245 | int32(spacing), ... 246 | cutoff_diffnorm, ... 247 | int32(cutoff_iteration), ... 248 | logical(subsettrunc), ... 249 | int32(num_img+i), ... 250 | int32(total_imgs)); 251 | toc 252 | catch %#ok 253 | % Only time an exception is returned is either if rgdic has 254 | % incorrect inputs, ran out of memory, or there is a bug in 255 | % the code. The first option never happens if rgdic 256 | % is called through the program. The second option can 257 | % happen and is thus the only real handleable exception 258 | % which will get thrown by rgdic. The last case was not 259 | % intended, so it is not handled here. 260 | h_error = errordlg('Ncorr most likely ran out of memory while performing DIC. Please clear memory in workspace, restart Ncorr, or crop/use smaller images before doing analysis.','Error','modal'); 261 | uiwait(h_error); 262 | return; 263 | end 264 | 265 | % See if analysis was cancelled 266 | if (outstate_dic ~= out.success) 267 | return; 268 | end 269 | 270 | % Take union of reference ROI with validpoints to get the new 271 | % ROI. 272 | rois_dic_prelim(i+1) = imgs(1).roi.get_union(displacements_prelim(i+1).plot_validpoints,spacing); 273 | end 274 | 275 | % Set outputs 276 | for i = 0:length(displacements_prelim)-1 277 | displacements(i+1) = displacements_prelim(i+1); 278 | rois_dic(i+1) = rois_dic_prelim(i+1); 279 | end 280 | for i = 0:size(seedinfo_prelim,1)-1 281 | for j = 0:size(seedinfo_prelim,2)-1 282 | for k = 0:size(seedinfo_prelim,3)-1 283 | seedinfo(i+1,j+1,k+1) = seedinfo_prelim(i+1,j+1,k+1); 284 | end 285 | end 286 | end 287 | outstate = out.success; 288 | end 289 | -------------------------------------------------------------------------------- /ncorr_gui_setrois.m: -------------------------------------------------------------------------------- 1 | function [rois,outstate] = ncorr_gui_setrois(imgs,pos_parent,params_init) 2 | % This is a GUI for setting the ROIs for either the reference or current 3 | % image(s). 4 | % 5 | % Inputs -----------------------------------------------------------------% 6 | % imgs - ncorr_class_img; the input images(s); Generally, it's a single 7 | % image, although multiple image are supported. 8 | % pos_parent - integer array; this is the position of the parent figure 9 | % which determines where to position this figure 10 | % params_init - ncorr_class_roi; contains ROI(s) if they have been set 11 | % before; otherwise it's empty. Number of input ROI(s) must be the same 12 | % length as imgs. 13 | % 14 | % Outputs ----------------------------------------------------------------% 15 | % rois - ncorr_class_roi; contains the ROI(s) for the analysis 16 | % outstate - integer; returns either out.cancelled, out.failed, or 17 | % out.success. 18 | 19 | % Data ---------------------------------------------------------------% 20 | % Initialize outputs 21 | outstate = out.cancelled; 22 | rois = ncorr_class_roi.empty; 23 | % Get GUI handles 24 | handles_gui = init_gui(); 25 | % Run c-tor 26 | feval(ncorr_util_wrapcallbacktrycatch(@constructor,handles_gui.figure)); 27 | 28 | % Callbacks and functions --------------------------------------------% 29 | function constructor() 30 | % Initialize buffers 31 | if (isempty(params_init)) 32 | rois_prelim(1:length(imgs)) = ncorr_class_roi; 33 | list_rois = false(1,length(imgs)); % Used to keep track of which rois are set 34 | else 35 | rois_prelim = params_init; 36 | list_rois = true(1,length(imgs)); % Used to keep track of which rois are set 37 | end 38 | 39 | % Set Data 40 | setappdata(handles_gui.figure,'num_img',length(imgs)-1); 41 | setappdata(handles_gui.figure,'rois_prelim',rois_prelim); 42 | setappdata(handles_gui.figure,'list_rois',list_rois); 43 | 44 | % Update 45 | update_axes('set'); 46 | update_sidemenu(); 47 | 48 | % Format window 49 | set(handles_gui.figure,'Visible','on'); 50 | end 51 | 52 | function callback_edit_imgnum(hObject,eventdata) %#ok 53 | freeze_menu(); 54 | 55 | % Get data 56 | num_img = getappdata(handles_gui.figure,'num_img'); 57 | 58 | % Get img num 59 | num_img_prelim = str2double(get(handles_gui.edit_imgnum,'string')); 60 | if (ncorr_util_isintbb(num_img_prelim,1,length(imgs),'Image number') == out.success) 61 | % num_img_prelim will be zero based indexed 62 | num_img = num_img_prelim-1; 63 | end 64 | 65 | % Set data 66 | setappdata(handles_gui.figure,'num_img',num_img); 67 | 68 | % Update 69 | update_axes('set'); 70 | update_sidemenu(); 71 | 72 | unfreeze_menu(); 73 | end 74 | 75 | function callback_button_loadroi(hObject,evendata) %#ok 76 | freeze_menu(); 77 | 78 | % Get Data 79 | num_img = getappdata(handles_gui.figure,'num_img'); 80 | rois_prelim = getappdata(handles_gui.figure,'rois_prelim'); 81 | list_rois = getappdata(handles_gui.figure,'list_rois'); 82 | 83 | % Get ROI 84 | [mask_img_prelim,outstate_roi] = ncorr_util_loadimgs(false); 85 | 86 | if (outstate_roi == out.success) 87 | % Convert to binary mask 88 | mask_load_prelim = im2bw(mask_img_prelim.get_gs()); %#ok 89 | 90 | % Make sure ROI and image are the same size and that the ROI 91 | % is nonempty: 92 | if (isequal(size(mask_load_prelim),[imgs(num_img+1).height imgs(num_img+1).width]) && ~isempty(find(mask_load_prelim,1))) 93 | % Form roi_load_prelim 94 | roi_load_prelim = ncorr_class_roi; 95 | roi_load_prelim.set_roi('load',struct('mask',mask_load_prelim,'cutoff',2000)); 96 | if (roi_load_prelim.get_fullregions() > 0) 97 | % At this point, roi_load_prelim fits the critera for a 98 | % ROI. Display it to the user. 99 | outstate_roi = ncorr_gui_loadroi(imgs(num_img+1), ... 100 | roi_load_prelim, ... 101 | get(handles_gui.figure,'OuterPosition')); 102 | 103 | if (outstate_roi == out.success) 104 | % Set ROI 105 | rois_prelim(num_img+1) = roi_load_prelim; 106 | list_rois(num_img+1) = true; 107 | end 108 | else 109 | h_error = errordlg('ROI must contain a large contiguous region. Small regions are filtered out by default.','Error','modal'); 110 | uiwait(h_error); 111 | end 112 | else 113 | h_error = errordlg('ROI was invalid - either not the same size as the reference images or empty.','Error','modal'); 114 | uiwait(h_error); 115 | end 116 | end 117 | 118 | % Set Data 119 | setappdata(handles_gui.figure,'rois_prelim',rois_prelim); 120 | setappdata(handles_gui.figure,'list_rois',list_rois); 121 | 122 | % Update 123 | update_axes('set'); 124 | update_sidemenu(); 125 | 126 | unfreeze_menu(); 127 | end 128 | 129 | function callback_button_drawroi(hObject,evendata) %#ok 130 | freeze_menu(); 131 | 132 | % Get Data 133 | num_img = getappdata(handles_gui.figure,'num_img'); 134 | rois_prelim = getappdata(handles_gui.figure,'rois_prelim'); 135 | list_rois = getappdata(handles_gui.figure,'list_rois'); 136 | 137 | % Check if ROI was drawn before 138 | params_init_sub = []; % must use sub postfix to prevent overwriting of params_init input 139 | if (list_rois(num_img+1) && strcmp(rois_prelim(num_img+1).type,'draw')) 140 | params_init_sub = rois_prelim(num_img+1).data.drawobjects; 141 | end 142 | 143 | [roi_draw_prelim,outstate_roi] = ncorr_gui_drawroi(imgs(num_img+1), ... 144 | get(handles_gui.figure,'OuterPosition'), ... 145 | params_init_sub); 146 | 147 | if (outstate_roi == out.success) 148 | % Set ROI 149 | rois_prelim(num_img+1) = roi_draw_prelim; 150 | list_rois(num_img+1) = true; 151 | end 152 | 153 | % Set Data 154 | setappdata(handles_gui.figure,'rois_prelim',rois_prelim); 155 | setappdata(handles_gui.figure,'list_rois',list_rois); 156 | 157 | % Update 158 | update_axes('set'); 159 | update_sidemenu(); 160 | 161 | unfreeze_menu(); 162 | end 163 | 164 | function callback_button_finish(hObject,evendata) %#ok 165 | % Get Data 166 | rois_prelim = getappdata(handles_gui.figure,'rois_prelim'); 167 | 168 | % Set outputs 169 | for i = 0:length(imgs)-1 170 | rois(i+1) = rois_prelim(i+1); 171 | end 172 | outstate = out.success; 173 | 174 | % Exit 175 | close(handles_gui.figure); 176 | end 177 | 178 | function callback_button_cancel(hObject,evendata) %#ok 179 | close(handles_gui.figure); 180 | end 181 | 182 | function callback_button_left(hObject,evendata) %#ok 183 | freeze_menu(); 184 | 185 | % Get Data 186 | num_img = getappdata(handles_gui.figure,'num_img'); 187 | 188 | % Check for overshoot 189 | if (num_img > 0) 190 | num_img = num_img-1; 191 | end 192 | 193 | % Set Data 194 | setappdata(handles_gui.figure,'num_img',num_img); 195 | 196 | % Update 197 | update_axes('set'); 198 | update_sidemenu(); 199 | 200 | unfreeze_menu(); 201 | end 202 | 203 | function callback_button_right(hObject,evendata) %#ok 204 | freeze_menu(); 205 | 206 | % Get Data 207 | num_img = getappdata(handles_gui.figure,'num_img'); 208 | 209 | % Check for overshoot 210 | if (num_img < length(imgs)-1) 211 | num_img = num_img+1; 212 | end 213 | 214 | % Set Data 215 | setappdata(handles_gui.figure,'num_img',num_img); 216 | 217 | % Update 218 | update_axes('set'); 219 | update_sidemenu(); 220 | 221 | unfreeze_menu(); 222 | end 223 | 224 | function freeze_menu() 225 | set(handles_gui.button_loadroi,'Enable','off'); 226 | set(handles_gui.button_drawroi,'Enable','off'); 227 | set(handles_gui.button_cancel,'Enable','off'); 228 | set(handles_gui.button_finish,'Enable','off'); 229 | end 230 | 231 | function unfreeze_menu() 232 | set(handles_gui.button_loadroi,'Enable','on'); 233 | set(handles_gui.button_drawroi,'Enable','on'); 234 | set(handles_gui.button_cancel,'Enable','on'); 235 | end 236 | 237 | function update_sidemenu() 238 | % Get data 239 | list_rois = getappdata(handles_gui.figure,'list_rois'); 240 | 241 | % Enable finish button if all ROIs have been set 242 | if (all(list_rois)) 243 | set(handles_gui.button_finish,'Enable','on'); 244 | end 245 | 246 | % Update text 247 | set(handles_gui.text_menu,'String',[num2str(sum(list_rois)) ' of ' num2str(length(list_rois)) ' ROIs set.']); 248 | end 249 | 250 | function update_axes(action) 251 | % Get Data 252 | num_img = getappdata(handles_gui.figure,'num_img'); 253 | rois_prelim = getappdata(handles_gui.figure,'rois_prelim'); 254 | list_rois = getappdata(handles_gui.figure,'list_rois'); 255 | 256 | if (strcmp(action,'set')) 257 | % Show preview 258 | if (list_rois(num_img+1)) 259 | % Paint ROI over images 260 | preview_roi = imgs(num_img+1).get_gs(); 261 | preview_roi(rois_prelim(num_img+1).mask) = preview_roi(rois_prelim(num_img+1).mask)+imgs(num_img+1).max_gs; 262 | imshow(preview_roi,[imgs(num_img+1).min_gs 2*imgs(num_img+1).max_gs],'Parent',handles_gui.axes_preview); 263 | set(handles_gui.axes_preview,'Visible','off'); 264 | else 265 | % Paint only images 266 | imshow(imgs(num_img+1).get_gs(),[imgs(num_img+1).min_gs 2*imgs(num_img+1).max_gs],'Parent',handles_gui.axes_preview); 267 | set(handles_gui.axes_preview,'Visible','off'); 268 | end 269 | 270 | % Format/show text 271 | set(handles_gui.text_name,'String',['Name: ' imgs(num_img+1).name(1:min(end,22))]); 272 | 273 | % Set buttons/editboxes 274 | set(handles_gui.edit_imgnum,'String',num2str(num_img+1)); 275 | if (length(imgs) == 1) 276 | set(handles_gui.button_left,'Enable','off'); 277 | set(handles_gui.button_right,'Enable','off'); 278 | set(handles_gui.edit_imgnum,'Enable','off'); 279 | elseif (num_img == 0) 280 | set(handles_gui.button_left,'Enable','off'); 281 | set(handles_gui.button_right,'Enable','on'); 282 | set(handles_gui.edit_imgnum,'Enable','on'); 283 | elseif (num_img == length(imgs)-1) 284 | set(handles_gui.button_left,'Enable','on'); 285 | set(handles_gui.button_right,'Enable','off'); 286 | set(handles_gui.edit_imgnum,'Enable','on'); 287 | else 288 | set(handles_gui.button_left,'Enable','on'); 289 | set(handles_gui.button_right,'Enable','on'); 290 | set(handles_gui.edit_imgnum,'Enable','on'); 291 | end 292 | end 293 | end 294 | 295 | function handles_gui = init_gui() 296 | % GUI controls -------------------------------------------------------% 297 | % Figure 298 | handles_gui.figure = figure( ... 299 | 'Tag', 'figure', ... 300 | 'Units', 'characters', ... 301 | 'Position', ncorr_util_figpos(pos_parent,[35 126.7]), ... 302 | 'Name', 'Draw ROI', ... 303 | 'MenuBar', 'none', ... 304 | 'NumberTitle', 'off', ... 305 | 'Color', get(0,'DefaultUicontrolBackgroundColor'), ... 306 | 'handlevisibility','off', ... 307 | 'DockControls','off', ... 308 | 'Resize','off', ... 309 | 'WindowStyle','modal', ... 310 | 'Visible','off', ... 311 | 'IntegerHandle','off', ... 312 | 'Interruptible','off'); 313 | 314 | % Panels 315 | handles_gui.group_roioptions = uipanel( ... 316 | 'Parent', handles_gui.figure, ... 317 | 'Tag', 'group_roioptions', ... 318 | 'Units', 'characters', ... 319 | 'Position', [2 26.1 35 8.3], ... 320 | 'Title', 'ROI Options', ... 321 | 'Interruptible','off'); 322 | 323 | handles_gui.group_menu = uipanel( ... 324 | 'Parent', handles_gui.figure, ... 325 | 'Tag', 'group_menu', ... 326 | 'Units', 'characters', ... 327 | 'Position', [2 18.6 35 6.7], ... 328 | 'Title', 'Menu', ... 329 | 'Interruptible','off'); 330 | 331 | handles_gui.group_preview = uipanel( ... 332 | 'Parent', handles_gui.figure, ... 333 | 'Tag', 'group_preview', ... 334 | 'Units', 'characters', ... 335 | 'Position', [38.7 0.75 85.8 33.6], ... 336 | 'Title', 'ROI', ... 337 | 'Interruptible','off'); 338 | 339 | % Axes 340 | handles_gui.axes_preview = axes( ... 341 | 'Parent', handles_gui.group_preview, ... 342 | 'Tag', 'axes_preview', ... 343 | 'Units', 'characters', ... 344 | 'Position', [2.4 4 79.7 27.6]', ... 345 | 'Visible','off', ... 346 | 'Interruptible','off'); 347 | 348 | % Static Texts 349 | handles_gui.text_menu = uicontrol( ... 350 | 'Parent', handles_gui.group_roioptions, ... 351 | 'Tag', 'text_menu', ... 352 | 'Style', 'text', ... 353 | 'Units', 'characters', ... 354 | 'Position', [2.3 5.0 23.5 1.3], ... 355 | 'String', ['0 of ' num2str(length(imgs)) ' ROIs set.'], ... 356 | 'HorizontalAlignment', 'left', ... 357 | 'Interruptible','off'); 358 | 359 | handles_gui.text_name = uicontrol( ... 360 | 'Parent', handles_gui.group_preview, ... 361 | 'Tag', 'text_name', ... 362 | 'Style', 'text', ... 363 | 'Units', 'characters', ... 364 | 'Position', [4 1.3 56.9 1.3], ... 365 | 'String', 'Name: ', ... 366 | 'HorizontalAlignment', 'left', ... 367 | 'Interruptible','off'); 368 | 369 | % Editbox 370 | handles_gui.edit_imgnum = uicontrol( ... 371 | 'Parent', handles_gui.group_preview, ... 372 | 'Tag', 'edit_imgnum', ... 373 | 'Style', 'edit', ... 374 | 'Units', 'characters', ... 375 | 'Position', [68.1 1.3 7 1.6], ... 376 | 'String', '', ... 377 | 'Callback', ncorr_util_wrapcallbacktrycatch(@callback_edit_imgnum,handles_gui.figure), ... 378 | 'Enable', 'off', ... 379 | 'Interruptible','off'); 380 | 381 | % Push Buttons 382 | handles_gui.button_loadroi = uicontrol( ... 383 | 'Parent', handles_gui.group_roioptions, ... 384 | 'Tag', 'loadroi', ... 385 | 'Style', 'pushbutton', ... 386 | 'Units', 'characters', ... 387 | 'Position', [2.1 3 29.7 1.7], ... 388 | 'String', 'Load ROI', ... 389 | 'Callback', ncorr_util_wrapcallbacktrycatch(@callback_button_loadroi,handles_gui.figure), ... 390 | 'Interruptible','off'); 391 | 392 | handles_gui.button_drawroi = uicontrol( ... 393 | 'Parent', handles_gui.group_roioptions, ... 394 | 'Tag', 'draw', ... 395 | 'Style', 'pushbutton', ... 396 | 'Units', 'characters', ... 397 | 'Position', [2.1 1 29.7 1.7], ... 398 | 'String', 'Draw ROI', ... 399 | 'Callback', ncorr_util_wrapcallbacktrycatch(@callback_button_drawroi,handles_gui.figure), ... 400 | 'Interruptible','off'); 401 | 402 | handles_gui.button_finish = uicontrol( ... 403 | 'Parent', handles_gui.group_menu, ... 404 | 'Tag', 'finish', ... 405 | 'Style', 'pushbutton', ... 406 | 'Units', 'characters', ... 407 | 'Position', [2.1 3 29.7 1.7], ... 408 | 'String', 'Finish', ... 409 | 'Enable', 'off', ... 410 | 'Callback', ncorr_util_wrapcallbacktrycatch(@callback_button_finish,handles_gui.figure), ... 411 | 'Interruptible','off'); 412 | 413 | handles_gui.button_cancel = uicontrol( ... 414 | 'Parent', handles_gui.group_menu, ... 415 | 'Tag', 'draw', ... 416 | 'Style', 'pushbutton', ... 417 | 'Units', 'characters', ... 418 | 'Position', [2.1 1 29.7 1.7], ... 419 | 'String', 'Cancel', ... 420 | 'Callback', ncorr_util_wrapcallbacktrycatch(@callback_button_cancel,handles_gui.figure), ... 421 | 'Interruptible','off'); 422 | 423 | handles_gui.button_left = uicontrol( ... 424 | 'Parent', handles_gui.group_preview, ... 425 | 'Tag', 'button_left', ... 426 | 'Style', 'pushbutton', ... 427 | 'Units', 'characters', ... 428 | 'Position', [61.1 1.2 6 1.8], ... 429 | 'String', '<', ... 430 | 'Callback', ncorr_util_wrapcallbacktrycatch(@callback_button_left,handles_gui.figure), ... 431 | 'Interruptible','off', ... 432 | 'Enable', 'off'); 433 | 434 | handles_gui.button_right = uicontrol( ... 435 | 'Parent', handles_gui.group_preview, ... 436 | 'Tag', 'button_right', ... 437 | 'Style', 'pushbutton', ... 438 | 'Units', 'characters', ... 439 | 'Position', [76.1 1.2 6 1.8], ... 440 | 'String', '>', ... 441 | 'Callback', ncorr_util_wrapcallbacktrycatch(@callback_button_right,handles_gui.figure), ... 442 | 'Interruptible','off', ... 443 | 'Enable', 'off'); 444 | end 445 | 446 | % Pause until figure is closed ---------------------------------------% 447 | waitfor(handles_gui.figure); 448 | end 449 | -------------------------------------------------------------------------------- /ncorr_gui_setseeds.m: -------------------------------------------------------------------------------- 1 | function [seedinfo,threaddiagram,outstate] = ncorr_gui_setseeds(reference,current,roi,num_region,spacing,radius,cutoff_diffnorm,cutoff_iteration,total_threads,enabled_stepanalysis,subsettrunc,num_img,total_imgs,pos_parent) 2 | % This is a GUI for setting the rgdic seeds. The number of seeds placed in 3 | % each region depends on the number of threads. 4 | % 5 | % Inputs -----------------------------------------------------------------% 6 | % reference - ncorr_class_img; used for displaying the background 7 | % image and calculations 8 | % current - ncorr_class_img(s); used for displaying the background image 9 | % and calculations 10 | % roi - ncorr_class_roi; roi corresponding to the reference image. 11 | % num_region - integer; number corresponding to the region; 12 | % spacing - integer; spacing between subsets 13 | % radius - integer; radius of subsets 14 | % cutoff_diffnorm - double; cutoff of norm of the difference vector 15 | % cutoff_iteration - integer; cutoff for the number of iterations 16 | % total_threads - integer; total number of threads 17 | % enabled_stepanalysis - logical; if true, then process as many images as 18 | % possible. If false, process all images. 19 | % subsettrunc - logical; if true, then subset truncation is enabled 20 | % num_img - integer; current number of reference image 21 | % total_imgs - integer; total number of images being processed 22 | % pos_parent - integer array; this is the position of the parent figure 23 | % which determines where to position this figure 24 | % 25 | % Outputs ----------------------------------------------------------------% 26 | % seedinfo - struct; contains 27 | % struct('paramvector',{},'num_region',{},'num_thread',{},'computepoints' 28 | % ,{}) 29 | % threaddiagram - integer array; array the same size as the reduced 30 | % ROI. The elements indicate which points will be computed by which 31 | % threads 32 | % outstate - integer; returns either out.cancelled, out.failed, or 33 | % out.success. 34 | % 35 | % Note that if step analysis is enabled, it is gauranteed that at least one 36 | % image will be succesfully seeded if outstate returns true. If step 37 | % analysis is disabled, it is gauranteed that all images will be seeded or 38 | % if outstate returns true. 39 | 40 | % Data ---------------------------------------------------------------% 41 | % Initialize Outputs 42 | outstate = out.cancelled; 43 | threaddiagram = []; 44 | seedinfo = struct('paramvector',{},'num_region',{},'num_thread',{},'computepoints',{}); % paramvector = [x y u v du/dx du/dy dv/dx dv/dy corrcoef] 45 | % Get GUI handles 46 | handles_gui = init_gui(); 47 | % Run c-tor 48 | feval(ncorr_util_wrapcallbacktrycatch(@constructor,handles_gui.figure)); 49 | 50 | % Callbacks and functions --------------------------------------------% 51 | function constructor() 52 | % Initialize buffers 53 | % Initialize reduced ref 54 | ref_reduced = reference.reduce(spacing); 55 | % Initialize roi_reduced 56 | roi_reduced = roi.reduce(spacing); 57 | % Get region mask 58 | regionmask = roi_reduced.get_regionmask(num_region); 59 | % Initialize threaddiagram_prelim and threaddiagram preview - these 60 | % are modified in-place 61 | threaddiagram_prelim = -ones(size(roi_reduced.mask)); 62 | preview_threaddiagram = zeros(size(roi_reduced.mask)); 63 | 64 | % Store data 65 | setappdata(handles_gui.figure,'ref_reduced',ref_reduced); 66 | setappdata(handles_gui.figure,'roi_reduced',roi_reduced); 67 | setappdata(handles_gui.figure,'handle_preview',[]); 68 | setappdata(handles_gui.figure,'handle_points',[]); 69 | setappdata(handles_gui.figure,'regionmask',regionmask); 70 | setappdata(handles_gui.figure,'threaddiagram_prelim',threaddiagram_prelim); 71 | setappdata(handles_gui.figure,'preview_threaddiagram',preview_threaddiagram); 72 | 73 | % Update thread diagram and preview - must do this after setting the data 74 | get_threaddiagram([]); 75 | 76 | % Update 77 | update_axes('set'); 78 | update_sidemenu(); 79 | 80 | % Set visible 81 | set(handles_gui.figure,'Visible','on'); 82 | end 83 | 84 | function callback_button_setseeds(hObject,eventdata) %#ok 85 | freeze_menu(); 86 | 87 | % Get data 88 | roi_reduced = getappdata(handles_gui.figure,'roi_reduced'); 89 | 90 | % Set seeds ------------------------------------------------------% 91 | % Initialize point handle 92 | handle_points = impoint.empty; 93 | for i = 0:total_threads-1 94 | % Place seed and make sure it's within region 95 | seedfinished = false; 96 | while (~seedfinished) 97 | handle_point_buffer = impoint(handles_gui.axes_setseeds); 98 | 99 | % impoint will be empty if esc is pressed 100 | if (~isempty(handle_point_buffer)) 101 | % Get seed position - convert to zero based indexing 102 | % and round 103 | pos_seed = round(getPosition(handle_point_buffer))-1; 104 | 105 | % Make sure point is within the correct region 106 | if (roi_reduced.get_num_region(pos_seed(1),pos_seed(2),false(1,length(roi_reduced.region))) == num_region) 107 | % Store and format impoint 108 | handle_points(i+1) = handle_point_buffer; 109 | setColor(handle_points(i+1),'g'); 110 | set(handle_points(i+1),'UIContextMenu',''); 111 | 112 | % Set constraint function 113 | api_point = iptgetapi(handle_points(i+1)); 114 | constrainfcn = ncorr_util_formregionconstraint(roi_reduced.region(num_region+1)); 115 | api_point.setPositionConstraintFcn(constrainfcn); 116 | 117 | % Assign Position callback 118 | addNewPositionCallback(handle_points(i+1),ncorr_util_wrapcallbacktrycatch(@(pos)callback_impoint(pos),handles_gui.figure)); 119 | 120 | % Update threaddiagram and threaddiagram preview 121 | get_threaddiagram(handle_points); 122 | 123 | % Set data 124 | setappdata(handles_gui.figure,'handle_points',handle_points); 125 | 126 | % Update here to ensure preview is updated as the seeds 127 | % are being placed. 128 | update_sidemenu(); 129 | update_axes('update'); 130 | 131 | % Exit 132 | seedfinished = true; 133 | else 134 | delete(handle_point_buffer); 135 | 136 | h_error = errordlg('Seed point not within region, try again.','Error','modal'); 137 | uiwait(h_error); 138 | end 139 | else 140 | % Escape was pressed - delete all points 141 | delete(handle_points); 142 | handle_points = []; 143 | 144 | % Update threaddiagram and threaddiagram preview 145 | get_threaddiagram(handle_points); 146 | 147 | % Set data 148 | setappdata(handles_gui.figure,'handle_points',handle_points); 149 | 150 | % Update 151 | update_sidemenu(); 152 | update_axes('update'); 153 | 154 | % Return since analysis was cancelled 155 | return; 156 | end 157 | end 158 | end 159 | 160 | % Update 161 | update_sidemenu(); 162 | 163 | unfreeze_menu(); 164 | end 165 | 166 | function callback_button_finish(hObject,eventdata) %#ok 167 | % Get data 168 | handle_points = getappdata(handles_gui.figure,'handle_points'); 169 | threaddiagram_prelim = getappdata(handles_gui.figure,'threaddiagram_prelim'); 170 | 171 | % Get position - and make sure no two seeds overlap 172 | pos_seed = []; 173 | for i = 0:length(handle_points)-1 174 | % Convert to zero based indexing and round 175 | pos_buffer = round(getPosition(handle_points(i+1)))-1; 176 | pos_seed = vertcat(pos_seed,pos_buffer); %#ok 177 | end 178 | if (size(unique(pos_seed,'rows'),1) == size(pos_seed,1)) 179 | % Convert pos_seed to regular coordinates since they are WRT to 180 | % the reduced ROI at this point 181 | pos_seed = pos_seed*(spacing+1); 182 | 183 | % Get seedinfo and preview of seeds. Note that if step analysis 184 | % is enabled, then ncorr_gui_seedpreview will return at least one 185 | % seed if it returns success. If step analysis is disabled, 186 | % then ncorr_gui_seedpreview will provide seeds for 187 | % all images if it returns true. 188 | [seedinfo_prelim,outstate_preview] = ncorr_gui_seedpreview(reference, ... 189 | current, ... 190 | roi, ... 191 | num_region, ... 192 | pos_seed, ... 193 | radius, ... 194 | cutoff_diffnorm, ... 195 | cutoff_iteration, ... 196 | enabled_stepanalysis, ... 197 | subsettrunc, ... 198 | num_img, ... 199 | total_imgs, ... 200 | get(handles_gui.figure,'OuterPosition')); 201 | if (outstate_preview == out.success) 202 | % Set outputs 203 | seedinfo = seedinfo_prelim; 204 | % Determine number of compute points and append this to 205 | % seedinfo_buffer 206 | for i = 0:size(seedinfo,3)-1 207 | for j = 0:size(seedinfo,1)-1 208 | seedinfo(j+1,1,i+1).computepoints = length(find(threaddiagram_prelim == j)); 209 | end 210 | end 211 | % Assign thread diagram 212 | threaddiagram = threaddiagram_prelim; 213 | outstate = out.success; 214 | 215 | % Exit 216 | close(handles_gui.figure); 217 | end 218 | else 219 | % Two seeds occupy same location, tell user to move 220 | % generators until they occupy unique locations 221 | h_error = errordlg('Two or more seeds occupy the same location, please move all seeds to unique locations.','Error','modal'); 222 | uiwait(h_error); 223 | end 224 | end 225 | 226 | function callback_button_cancel(hObject,eventdata) %#ok 227 | close(handles_gui.figure); 228 | end 229 | 230 | function callback_impoint(pos) %#ok 231 | % Get data 232 | handle_points = getappdata(handles_gui.figure,'handle_points'); 233 | 234 | % Update threaddiagram and threaddiagram preview 235 | get_threaddiagram(handle_points); 236 | 237 | % Update 238 | update_axes('update'); 239 | end 240 | 241 | function freeze_menu(hObject,eventdata) %#ok 242 | % Disable setseeds button 243 | set(handles_gui.button_setseeds,'Enable','off'); 244 | end 245 | 246 | function unfreeze_menu(hObject,eventdata) %#ok 247 | end 248 | 249 | function update_sidemenu() 250 | % Get data 251 | handle_points = getappdata(handles_gui.figure,'handle_points'); 252 | 253 | % Update text 254 | set(handles_gui.text_seedcount,'String',['Seed(s) Set: ' num2str(length(handle_points)) ' of ' num2str(total_threads)]) 255 | 256 | % Set buttons 257 | if (length(handle_points) == total_threads) 258 | % Seeds are finished 259 | set(handles_gui.button_setseeds,'Enable','off'); 260 | set(handles_gui.button_finish,'Enable','on'); 261 | elseif (~isempty(handle_points)) 262 | % Seeds are being placed 263 | set(handles_gui.button_setseeds,'Enable','off'); 264 | set(handles_gui.button_finish,'Enable','off'); 265 | else 266 | % Seeds are not being placed 267 | set(handles_gui.button_setseeds,'Enable','on'); 268 | set(handles_gui.button_finish,'Enable','off'); 269 | end 270 | end 271 | 272 | function update_axes(action) 273 | % Get data 274 | handle_preview = getappdata(handles_gui.figure,'handle_preview'); 275 | preview_threaddiagram = getappdata(handles_gui.figure,'preview_threaddiagram'); 276 | 277 | if (strcmp(action,'set')) 278 | handle_preview = imshow(preview_threaddiagram,[reference.min_gs 2*reference.max_gs],'Parent',handles_gui.axes_setseeds); 279 | set(handles_gui.axes_setseeds,'Visible','off'); 280 | set(handles_gui.text_name,'String',['Name: ' reference.name(1:min(end,22))]); 281 | end 282 | 283 | if (strcmp(action,'set') || strcmp(action,'update')) 284 | % Update the overlay image 285 | set(handle_preview,'CData',preview_threaddiagram) 286 | end 287 | 288 | % Set data 289 | setappdata(handles_gui.figure,'handle_preview',handle_preview); 290 | end 291 | 292 | function get_threaddiagram(handle_points) 293 | % Get data 294 | ref_reduced = getappdata(handles_gui.figure,'ref_reduced'); 295 | regionmask = getappdata(handles_gui.figure,'regionmask'); 296 | threaddiagram_prelim = getappdata(handles_gui.figure,'threaddiagram_prelim'); 297 | preview_threaddiagram = getappdata(handles_gui.figure,'preview_threaddiagram'); 298 | 299 | % Draw thread diagram - get generators first 300 | generators = []; 301 | for i = 0:length(handle_points)-1 302 | % Convert to zero based indexing and round 303 | pos_buffer = round(getPosition(handle_points(i+1)))-1; 304 | generators = vertcat(generators,pos_buffer); %#ok 305 | end 306 | 307 | % This function modifies the arrays in-place 308 | ncorr_alg_formthreaddiagram(threaddiagram_prelim,preview_threaddiagram,int32(generators),regionmask,ref_reduced.formatted()); 309 | end 310 | 311 | function handles_gui = init_gui() 312 | % GUI controls -------------------------------------------------------% 313 | % Figure 314 | handles_gui.figure = figure( ... 315 | 'Tag', 'figure', ... 316 | 'Units', 'characters', ... 317 | 'Position', ncorr_util_figpos(pos_parent,[35 133]), ... 318 | 'Name', 'Set Seeds', ... 319 | 'MenuBar', 'none', ... 320 | 'NumberTitle', 'off', ... 321 | 'Color', get(0,'DefaultUicontrolBackgroundColor'), ... 322 | 'handlevisibility','off', ... 323 | 'DockControls','off', ... 324 | 'Resize','off', ... 325 | 'WindowStyle','modal', ... 326 | 'Visible','off', ... 327 | 'IntegerHandle','off', ... 328 | 'Interruptible','off'); 329 | 330 | % Panels 331 | handles_gui.group_seedoptions = uipanel( ... 332 | 'Parent', handles_gui.figure, ... 333 | 'Tag', 'group_seedoptions', ... 334 | 'Units', 'characters', ... 335 | 'Position', [2 28.5 35 6.0], ... 336 | 'Title', 'Seed Options', ... 337 | 'Interruptible','off'); 338 | 339 | handles_gui.group_menu = uipanel( ... 340 | 'Parent', handles_gui.figure, ... 341 | 'Tag', 'group_menu', ... 342 | 'Units', 'characters', ... 343 | 'Position', [2 21.2 35 6.4], ... 344 | 'Title', 'Menu', ... 345 | 'Interruptible','off'); 346 | 347 | handles_gui.group_setseeds = uipanel( ... 348 | 'Parent', handles_gui.figure, ... 349 | 'Tag', 'group_setseeds', ... 350 | 'Units', 'characters', ... 351 | 'Position', [39.0 0.75 92 33.75], ... 352 | 'Title', 'Select Region', ... 353 | 'Interruptible','off'); 354 | 355 | % Axes 356 | handles_gui.axes_setseeds = axes( ... 357 | 'Parent', handles_gui.group_setseeds, ... 358 | 'Tag', 'axes_setseeds', ... 359 | 'Units', 'characters', ... 360 | 'Position', [2.4 3 86.2 28.8], ... 361 | 'Visible', 'off', ... 362 | 'Interruptible','off'); 363 | 364 | % Static Texts 365 | handles_gui.text_seedcount = uicontrol( ... 366 | 'Parent', handles_gui.group_seedoptions, ... 367 | 'Tag', 'text_seedcount', ... 368 | 'Style', 'text', ... 369 | 'Units', 'characters', ... 370 | 'Position', [2.2 2.8 29.7 1.5], ... 371 | 'String', 'Seeds Set: 0 of ', ... 372 | 'HorizontalAlignment', 'left', ... 373 | 'Interruptible','off'); 374 | 375 | handles_gui.text_name = uicontrol( ... 376 | 'Parent', handles_gui.group_setseeds, ... 377 | 'Tag', 'text_name', ... 378 | 'Style', 'text', ... 379 | 'Units', 'characters', ... 380 | 'Position', [3 0.7 56.9 1.3], ... 381 | 'String', 'Name: ', ... 382 | 'HorizontalAlignment', 'left', ... 383 | 'Interruptible','off'); 384 | 385 | % Pushbuttons 386 | handles_gui.button_setseeds = uicontrol( ... 387 | 'Parent', handles_gui.group_seedoptions, ... 388 | 'Tag', 'button_setseeds', ... 389 | 'Style', 'pushbutton', ... 390 | 'Units', 'characters', ... 391 | 'Position', [2.1 1 29.7 1.7], ... 392 | 'String', 'Set Seeds', ... 393 | 'Callback', ncorr_util_wrapcallbacktrycatch(@callback_button_setseeds,handles_gui.figure), ... 394 | 'Interruptible','off'); 395 | 396 | handles_gui.button_finish = uicontrol( ... 397 | 'Parent', handles_gui.group_menu, ... 398 | 'Tag', 'button_finish', ... 399 | 'Style', 'pushbutton', ... 400 | 'Units', 'characters', ... 401 | 'Position', [2.1 3 29.7 1.7], ... 402 | 'String', 'Finish', ... 403 | 'Enable', 'off', ... 404 | 'Callback', ncorr_util_wrapcallbacktrycatch(@callback_button_finish,handles_gui.figure), ... 405 | 'Interruptible','off'); 406 | 407 | handles_gui.button_cancel = uicontrol( ... 408 | 'Parent', handles_gui.group_menu, ... 409 | 'Tag', 'button_cancel', ... 410 | 'Style', 'pushbutton', ... 411 | 'Units', 'characters', ... 412 | 'Position', [2.1 1 29.7 1.7], ... 413 | 'String', 'Cancel', ... 414 | 'Callback', ncorr_util_wrapcallbacktrycatch(@callback_button_cancel,handles_gui.figure), ... 415 | 'Interruptible','off'); 416 | end 417 | 418 | % Pause until figure is closed ---------------------------------------% 419 | waitfor(handles_gui.figure); 420 | end 421 | --------------------------------------------------------------------------------