diff --git a/Demo.m b/Demo.m index 13b9f588d7778d3c73b5c5272800ae12439b5fce..63640af522c4d4dc9b458836ae034494d0454c5c 100644 --- a/Demo.m +++ b/Demo.m @@ -1,10 +1,20 @@ close all clear -addpath(genpath('../Functions')) +addpath(genpath('./Functions')) %% Defaults -Data_path = '..\..\..\..\..\..\LINX FP08.001\Activity 7 - bending - visualizations and tiff stacks\mat_file\'; +% if you know the path otherwise choose the path using the +answer = questdlg('Write data path or browse to the data folder?', 'Data Path','Write','Browse','Browse'); +switch answer + case 'Write' + Data_path = '..\..\..\HCP Anywhere\LINX FP08.001\Activity 7 - bending - visualizations and tiff stacks\mat_file\'; + case 'Browse' + Data_path = uigetdir; + Data_path = fullfile(Data_path,'\'); + fprintf('Data Path: %s \n', Data_path) +end + samples = {'out_out_CD','out_out_MD','in_in_CD','in_in_MD'}; degree = {'0','45','90','180'}; layer_name = {'outer','inner'}; @@ -12,12 +22,17 @@ layer_name = {'outer','inner'}; %% extract poitns for the pipeline clc -flag_points = 0; % 1 if no point has been extracted and first time use of the code +flag_points = 1; % 1 if no point has been extracted and first time use of the code +flag_save = 0; % 0: to not save the results; 1: to save the results + +answer = inputdlg('Contrast Enhansed value:','Contrast',[1 50],{'.7 1.5'}); % this is default based on the data scanned at DTU +ce = str2num(answer{1}); + if flag_points for i = 1 sample = samples{i}; - for j = 1:4 + for j = 1 dg = degree{j}; %% load data @@ -35,7 +50,7 @@ clearvars -except Data_path samples degree layer_name clc answer = questdlg('Do you want to enhance the contrast? ','Contrast:','No','Yes','No'); -for i = 1 +for i = 1:4 sample = samples{i}; for j = 1:4 %% load data @@ -67,40 +82,48 @@ for i = 1 range = 20:-1:-20; end end + flag_save = 1; + flag_fig = 0; % if you want to check the results LayerDetection end %% measure disp('Measure ...') + flag_save = 1; PrepMeasure %% registration disp('Registration ...') + flag_save = 1; PrepRegistration end end %% visualize the measure -for i = 1%:4 +for i = 1:4 + flag_save = 1; Measure end close all %% apply the registration -for i = 1%:4 +for i = 1:4 + flag_save = 1; Registration end close all %% apply the alignment and transformation -for i = 1%:4 +for i = 1:4 + flag_save = 1; + flag_fig = 1; Transformation end close all %% convert the meshes -for i = 1%:4 +for i = 1:4 Export2inp end close all diff --git a/Export2inp.m b/Export2inp.m index 440cdcd6f6b2e72a602a8bfb06a0f9f29ff949d1..a24a2763a3e9ff77481e2179faab2ed7b1c86936 100644 --- a/Export2inp.m +++ b/Export2inp.m @@ -3,8 +3,6 @@ samples = {'out_out_CD','out_out_MD','in_in_CD','in_in_MD'}; degree = {'0','45','90','180'}; -flag_save = 1; - load_folder_layers = 'Results\layer\transfer\align_0dg_rest\'; save_folder = 'Results\export\'; @@ -29,7 +27,7 @@ end reshape(permute(aligned_inner_dg0(:,:,3,:),[2,1,3,4]),[sz0(1)*sz0(2)*sz0(4),1])]; no = [(1:size(no,1))',no]; - disp('Write mesh ...') + disp('Write mesh inner 0 ...') parfeval(@write_mesh,0,no,ele,[save_folder,'inner_',sample,'_0']); no = [reshape(permute(aligned_outer_dg0(:,:,1,:),[2,1,3,4]),[sz0(1)*sz0(2)*sz0(4),1]), ... @@ -37,7 +35,7 @@ end reshape(permute(aligned_outer_dg0(:,:,3,:),[2,1,3,4]),[sz0(1)*sz0(2)*sz0(4),1])]; no = [(1:size(no,1))',no]; - disp('Write mesh ...') + disp('Write mesh outer 0 ...') parfeval(@write_mesh,0,no,ele,[save_folder,'outer_',sample,'_0']); % 45 @@ -46,7 +44,7 @@ end reshape(permute(aligned_inner_dg45(:,:,3,:),[2,1,3,4]),[sz0(1)*sz0(2)*sz0(4),1])]; no = [(1:size(no,1))',no]; - disp('Write mesh ...') + disp('Write mesh inner 45 ...') parfeval(@write_mesh,0,no,ele,[save_folder,'inner_',sample,'_45']); no = [reshape(permute(aligned_outer_dg45(:,:,1,:),[2,1,3,4]),[sz0(1)*sz0(2)*sz0(4),1]), ... @@ -54,7 +52,7 @@ end reshape(permute(aligned_outer_dg45(:,:,3,:),[2,1,3,4]),[sz0(1)*sz0(2)*sz0(4),1])]; no = [(1:size(no,1))',no]; - disp('Write mesh ...') + disp('Write mesh outer 45 ...') parfeval(@write_mesh,0,no,ele,[save_folder,'outer_',sample,'_45']); % 90 @@ -63,7 +61,7 @@ end reshape(permute(aligned_inner_dg90(:,:,3,:),[2,1,3,4]),[sz0(1)*sz0(2)*sz0(4),1])]; no = [(1:size(no,1))',no]; - disp('Write mesh ...') + disp('Write mesh inner 90 ...') parfeval(@write_mesh,0,no,ele,[save_folder,'inner_',sample,'_90']); no = [reshape(permute(aligned_outer_dg90(:,:,1,:),[2,1,3,4]),[sz0(1)*sz0(2)*sz0(4),1]), ... @@ -71,7 +69,7 @@ end reshape(permute(aligned_outer_dg90(:,:,3,:),[2,1,3,4]),[sz0(1)*sz0(2)*sz0(4),1])]; no = [(1:size(no,1))',no]; - disp('Write mesh ...') + disp('Write mesh outer 90 ...') parfeval(@write_mesh,0,no,ele,[save_folder,'outer_',sample,'_90']); % 180 @@ -80,7 +78,7 @@ end reshape(permute(aligned_inner_dg180(:,:,3,:),[2,1,3,4]),[sz0(1)*sz0(2)*sz0(4),1])]; no = [(1:size(no,1))',no]; - disp('Write mesh ...') + disp('Write mesh inner 180 ...') parfeval(@write_mesh,0,no,ele,[save_folder,'inner_',sample,'_180']); no = [reshape(permute(aligned_outer_dg180(:,:,1,:),[2,1,3,4]),[sz0(1)*sz0(2)*sz0(4),1]), ... @@ -88,7 +86,7 @@ end reshape(permute(aligned_outer_dg180(:,:,3,:),[2,1,3,4]),[sz0(1)*sz0(2)*sz0(4),1])]; no = [(1:size(no,1))',no]; - disp('Write mesh ...') + disp('Write mesh outer 180 s...') parfeval(@write_mesh,0,no,ele,[save_folder,'outer_',sample,'_180']); %end \ No newline at end of file diff --git a/Functions/functions_GraphCut/GraphCutMex.cpp b/Functions/functions_GraphCut/GraphCutMex.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0cd28e75fd2a0f9a132a276576c3706d2e36c25c --- /dev/null +++ b/Functions/functions_GraphCut/GraphCutMex.cpp @@ -0,0 +1,100 @@ +#include <math.h> +#include "mex.h" +#include "graph.h" +#include <vector> + +void +mexFunction(int nout, mxArray *out[], + int nin, const mxArray *in[]) +{ + if (nin != 3) + mexErrMsgTxt("Three arguments are required (nNodes,TerminalWeights,EdgeWeights)") ; + if (nout > 2) + mexErrMsgTxt("Too many output arguments."); + + + int nNodes = (int) *mxGetPr(in[0]); + + + const int* twDim = mxGetDimensions(in[1]) ; + // mexPrintf("Dim is: %d\n", twDim[0]); + // mexPrintf("Dim is: %d\n", twDim[1]); + int twRows = twDim[0] ; + int twCols = twDim[1] ; + double* twPt = mxGetPr(in[1]) ; + if(twCols!=3) + mexErrMsgTxt("The Terminal Weight matix should have 3 columns, (Node,sink,source)."); + + + const int* ewDim = mxGetDimensions(in[2]) ; + int ewRows = ewDim[0] ; + int ewCols = ewDim[1] ; + double* ewPt = mxGetPr(in[2]) ; + if(ewCols!=4) + mexErrMsgTxt("The Terminal Weight matix should have 4 columns, (From,To,Capacity,Rev_Capacity)."); + + + typedef Graph<double,double,double> GraphType; + GraphType G(static_cast<int>(nNodes), static_cast<int>(ewRows+twRows)); + G.add_node(nNodes); + + for(int cTw=0;cTw<twRows;cTw++) + { + //Test for nodes in range + int node=(int)twPt[cTw]-1; + if(node<0 || node>=nNodes) + mexErrMsgTxt("index out of bounds in TerminalWeight Matrix."); + G.add_tweights(node,twPt[cTw+twRows],twPt[cTw+2*twRows]); + //mexPrintf("The loop for TerminalWeight is: %d\n", cTw); + } + + // mexPrintf("ewRows is: %d\n", ewRows); + // mexEvalString("drawnow"); + + for(int cEw=0;cEw<ewRows;cEw++) + { + //Test for nodes in range + int From=(int)ewPt[cEw]-1; + int To=(int)ewPt[cEw+ewRows]-1; + if(From<0 || From>=nNodes) + mexErrMsgTxt("From index out of bounds in Edge Weight Matrix."); + if(To<0 || To>=nNodes) + mexErrMsgTxt("To index out of bounds in Edge Weight Matrix."); + + G.add_edge(From,To,ewPt[cEw+2*ewRows],ewPt[cEw+3*ewRows]); + //mexPrintf("The loop for Edge Weight is: %d\n", cEw); + } + + double flow=G.maxflow(); + + std::vector<int> SourceCut; + + // mexPrintf("nNodes is: %d\n", nNodes); + // mexEvalString("drawnow"); + + for(int cNode=0;cNode<nNodes;cNode++) + { + if(G.what_segment(cNode)== GraphType::SOURCE) + SourceCut.push_back(cNode+1); + //mexPrintf("The loop for graph is: %d\n", cNode); + } + + + out[0] = mxCreateDoubleMatrix(SourceCut.size(), 1, mxREAL) ; + double* pOut=mxGetPr(out[0]); + std::vector<int>::const_iterator Itt(SourceCut.begin()); + for(;Itt!=SourceCut.end();++Itt) + { + *pOut=*Itt; + ++pOut; + //mexPrintf("The loop for pOut: %d\n", pOut); + } + + + if(nout==2) + { + out[1] = mxCreateDoubleMatrix(1, 1, mxREAL) ; + *mxGetPr(out[1])=flow; + } + +} \ No newline at end of file diff --git a/Functions/functions_GraphCut/GraphCutMex.mexa64 b/Functions/functions_GraphCut/GraphCutMex.mexa64 new file mode 100644 index 0000000000000000000000000000000000000000..a9df40a4455e24c1d68dffa4196f96e4cd9f291e Binary files /dev/null and b/Functions/functions_GraphCut/GraphCutMex.mexa64 differ diff --git a/Functions/functions_GraphCut/GraphCutMex.mexmaci64 b/Functions/functions_GraphCut/GraphCutMex.mexmaci64 new file mode 100644 index 0000000000000000000000000000000000000000..75cdb2c698cc1256beeb3c1826286d56f9dceda8 Binary files /dev/null and b/Functions/functions_GraphCut/GraphCutMex.mexmaci64 differ diff --git a/Functions/functions_GraphCut/GraphCutMex.mexw64 b/Functions/functions_GraphCut/GraphCutMex.mexw64 new file mode 100644 index 0000000000000000000000000000000000000000..91d726b3416ff2f451c7efe36a90c2c845a2e55f Binary files /dev/null and b/Functions/functions_GraphCut/GraphCutMex.mexw64 differ diff --git a/Functions/functions_GraphCut/GraphCutMex.mexw64.pdb b/Functions/functions_GraphCut/GraphCutMex.mexw64.pdb new file mode 100644 index 0000000000000000000000000000000000000000..6fd6cb3465dbb977dc57a697b6e9a9237d3bb484 Binary files /dev/null and b/Functions/functions_GraphCut/GraphCutMex.mexw64.pdb differ diff --git a/Functions/functions_GraphCut/block.h b/Functions/functions_GraphCut/block.h new file mode 100644 index 0000000000000000000000000000000000000000..93675f2f6ae8b8eb4dea5692e4f31302626eacca --- /dev/null +++ b/Functions/functions_GraphCut/block.h @@ -0,0 +1,268 @@ +/* block.h */ +/* + Template classes Block and DBlock + Implement adding and deleting items of the same type in blocks. + + If there there are many items then using Block or DBlock + is more efficient than using 'new' and 'delete' both in terms + of memory and time since + (1) On some systems there is some minimum amount of memory + that 'new' can allocate (e.g., 64), so if items are + small that a lot of memory is wasted. + (2) 'new' and 'delete' are designed for items of varying size. + If all items has the same size, then an algorithm for + adding and deleting can be made more efficient. + (3) All Block and DBlock functions are inline, so there are + no extra function calls. + + Differences between Block and DBlock: + (1) DBlock allows both adding and deleting items, + whereas Block allows only adding items. + (2) Block has an additional operation of scanning + items added so far (in the order in which they were added). + (3) Block allows to allocate several consecutive + items at a time, whereas DBlock can add only a single item. + + Note that no constructors or destructors are called for items. + + Example usage for items of type 'MyType': + + /////////////////////////////////////////////////// + #include "block.h" + #define BLOCK_SIZE 1024 + typedef struct { int a, b; } MyType; + MyType *ptr, *array[10000]; + + ... + + Block<MyType> *block = new Block<MyType>(BLOCK_SIZE); + + // adding items + for (int i=0; i<sizeof(array); i++) + { + ptr = block -> New(); + ptr -> a = ptr -> b = rand(); + } + + // reading items + for (ptr=block->ScanFirst(); ptr; ptr=block->ScanNext()) + { + printf("%d %d\n", ptr->a, ptr->b); + } + + delete block; + + ... + + DBlock<MyType> *dblock = new DBlock<MyType>(BLOCK_SIZE); + + // adding items + for (int i=0; i<sizeof(array); i++) + { + array[i] = dblock -> New(); + } + + // deleting items + for (int i=0; i<sizeof(array); i+=2) + { + dblock -> Delete(array[i]); + } + + // adding items + for (int i=0; i<sizeof(array); i++) + { + array[i] = dblock -> New(); + } + + delete dblock; + + /////////////////////////////////////////////////// + + Note that DBlock deletes items by marking them as + empty (i.e., by adding them to the list of free items), + so that this memory could be used for subsequently + added items. Thus, at each moment the memory allocated + is determined by the maximum number of items allocated + simultaneously at earlier moments. All memory is + deallocated only when the destructor is called. +*/ + +#ifndef __BLOCK_H__ +#define __BLOCK_H__ + +#include <stdlib.h> + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + +template <class Type> class Block +{ +public: + /* Constructor. Arguments are the block size and + (optionally) the pointer to the function which + will be called if allocation failed; the message + passed to this function is "Not enough memory!" */ + Block(int size, void (*err_function)(char *) = NULL) { first = last = NULL; block_size = size; error_function = err_function; } + + /* Destructor. Deallocates all items added so far */ + ~Block() { while (first) { block *next = first -> next; delete first; first = next; } } + + /* Allocates 'num' consecutive items; returns pointer + to the first item. 'num' cannot be greater than the + block size since items must fit in one block */ + Type *New(int num = 1) + { + Type *t; + + if (!last || last->current + num > last->last) + { + if (last && last->next) last = last -> next; + else + { + block *next = (block *) new char [sizeof(block) + (block_size-1)*sizeof(Type)]; + if (!next) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } + if (last) last -> next = next; + else first = next; + last = next; + last -> current = & ( last -> data[0] ); + last -> last = last -> current + block_size; + last -> next = NULL; + } + } + + t = last -> current; + last -> current += num; + return t; + } + + /* Returns the first item (or NULL, if no items were added) */ + Type *ScanFirst() + { + for (scan_current_block=first; scan_current_block; scan_current_block = scan_current_block->next) + { + scan_current_data = & ( scan_current_block -> data[0] ); + if (scan_current_data < scan_current_block -> current) return scan_current_data ++; + } + return NULL; + } + + /* Returns the next item (or NULL, if all items have been read) + Can be called only if previous ScanFirst() or ScanNext() + call returned not NULL. */ + Type *ScanNext() + { + while (scan_current_data >= scan_current_block -> current) + { + scan_current_block = scan_current_block -> next; + if (!scan_current_block) return NULL; + scan_current_data = & ( scan_current_block -> data[0] ); + } + return scan_current_data ++; + } + + /* Marks all elements as empty */ + void Reset() + { + block *b; + if (!first) return; + for (b=first; ; b=b->next) + { + b -> current = & ( b -> data[0] ); + if (b == last) break; + } + last = first; + } + +/***********************************************************************/ + +private: + + typedef struct block_st + { + Type *current, *last; + struct block_st *next; + Type data[1]; + } block; + + int block_size; + block *first; + block *last; + + block *scan_current_block; + Type *scan_current_data; + + void (*error_function)(char *); +}; + +/***********************************************************************/ +/***********************************************************************/ +/***********************************************************************/ + +template <class Type> class DBlock +{ +public: + /* Constructor. Arguments are the block size and + (optionally) the pointer to the function which + will be called if allocation failed; the message + passed to this function is "Not enough memory!" */ + DBlock(int size, void (*err_function)(char *) = NULL) { first = NULL; first_free = NULL; block_size = size; error_function = err_function; } + + /* Destructor. Deallocates all items added so far */ + ~DBlock() { while (first) { block *next = first -> next; delete first; first = next; } } + + /* Allocates one item */ + Type *New() + { + block_item *item; + + if (!first_free) + { + block *next = first; + first = (block *) new char [sizeof(block) + (block_size-1)*sizeof(block_item)]; + if (!first) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } + first_free = & (first -> data[0] ); + for (item=first_free; item<first_free+block_size-1; item++) + item -> next_free = item + 1; + item -> next_free = NULL; + first -> next = next; + } + + item = first_free; + first_free = item -> next_free; + return (Type *) item; + } + + /* Deletes an item allocated previously */ + void Delete(Type *t) + { + ((block_item *) t) -> next_free = first_free; + first_free = (block_item *) t; + } + +/***********************************************************************/ + +private: + + typedef union block_item_st + { + Type t; + block_item_st *next_free; + } block_item; + + typedef struct block_st + { + struct block_st *next; + block_item data[1]; + } block; + + int block_size; + block *first; + block_item *first_free; + + void (*error_function)(char *); +}; + + +#endif + diff --git a/Functions/functions_GraphCut/compile_mex_functions.m b/Functions/functions_GraphCut/compile_mex_functions.m new file mode 100644 index 0000000000000000000000000000000000000000..798ba7880444c4e1d99fa9167c337f0cbbd2a122 --- /dev/null +++ b/Functions/functions_GraphCut/compile_mex_functions.m @@ -0,0 +1,7 @@ +% compiling mex functions +% using libut makes it possible to use (ctrl + C) to stop C++ from inside +% MATLAB + +mex -g -O LIBPATH="C:/Program Files/MATLAB/R2019b/extern/lib/win64/microsoft" -llibut -compatibleArrayDims GraphCutMex.cpp MaxFlow.cpp graph.cpp + +mex -g -O -llibut -compatibleArrayDims GraphCutMex.cpp MaxFlow.cpp graph.cpp diff --git a/Functions/functions_GraphCut/graph.cpp b/Functions/functions_GraphCut/graph.cpp new file mode 100644 index 0000000000000000000000000000000000000000..19b0be67b958de6ca151c2ef3224040c6fc7d8d2 --- /dev/null +++ b/Functions/functions_GraphCut/graph.cpp @@ -0,0 +1,114 @@ +/* graph.cpp */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "graph.h" +#include "instances.inc" + + +template <typename captype, typename tcaptype, typename flowtype> + Graph<captype, tcaptype, flowtype>::Graph(int node_num_max, int edge_num_max, void (*err_function)(char *)) + : node_num(0), + nodeptr_block(NULL), + error_function(err_function) +{ + if (node_num_max < 16) node_num_max = 16; + if (edge_num_max < 16) edge_num_max = 16; + + nodes = (node*) malloc(node_num_max*sizeof(node)); + arcs = (arc*) malloc(2*edge_num_max*sizeof(arc)); + if (!nodes || !arcs) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } + + node_last = nodes; + node_max = nodes + node_num_max; + arc_last = arcs; + arc_max = arcs + 2*edge_num_max; + + maxflow_iteration = 0; + flow = 0; +} + +template <typename captype, typename tcaptype, typename flowtype> + Graph<captype,tcaptype,flowtype>::~Graph() +{ + if (nodeptr_block) + { + delete nodeptr_block; + nodeptr_block = NULL; + } + free(nodes); + free(arcs); +} + +template <typename captype, typename tcaptype, typename flowtype> + void Graph<captype,tcaptype,flowtype>::reset() +{ + node_last = nodes; + arc_last = arcs; + node_num = 0; + + if (nodeptr_block) + { + delete nodeptr_block; + nodeptr_block = NULL; + } + + maxflow_iteration = 0; + flow = 0; +} + +template <typename captype, typename tcaptype, typename flowtype> + void Graph<captype,tcaptype,flowtype>::reallocate_nodes(int num) +{ + int node_num_max = (int)(node_max - nodes); + node* nodes_old = nodes; + + node_num_max += node_num_max / 2; + if (node_num_max < node_num + num) node_num_max = node_num + num; + nodes = (node*) realloc(nodes_old, node_num_max*sizeof(node)); + if (!nodes) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } + + node_last = nodes + node_num; + node_max = nodes + node_num_max; + + if (nodes != nodes_old) + { + arc* a; + for (a=arcs; a<arc_last; a++) + { + a->head = (node*) ((char*)a->head + (((char*) nodes) - ((char*) nodes_old))); + } + } +} + +template <typename captype, typename tcaptype, typename flowtype> + void Graph<captype,tcaptype,flowtype>::reallocate_arcs() +{ + int arc_num_max = (int)(arc_max - arcs); + int arc_num = (int)(arc_last - arcs); + arc* arcs_old = arcs; + + arc_num_max += arc_num_max / 2; if (arc_num_max & 1) arc_num_max ++; + arcs = (arc*) realloc(arcs_old, arc_num_max*sizeof(arc)); + if (!arcs) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } + + arc_last = arcs + arc_num; + arc_max = arcs + arc_num_max; + + if (arcs != arcs_old) + { + node* i; + arc* a; + for (i=nodes; i<node_last; i++) + { + if (i->first) i->first = (arc*) ((char*)i->first + (((char*) arcs) - ((char*) arcs_old))); + } + for (a=arcs; a<arc_last; a++) + { + if (a->next) a->next = (arc*) ((char*)a->next + (((char*) arcs) - ((char*) arcs_old))); + a->sister = (arc*) ((char*)a->sister + (((char*) arcs) - ((char*) arcs_old))); + } + } +} diff --git a/Functions/functions_GraphCut/graph.h b/Functions/functions_GraphCut/graph.h new file mode 100644 index 0000000000000000000000000000000000000000..87efe7ef57a36269bed7725233e994650d8949dd --- /dev/null +++ b/Functions/functions_GraphCut/graph.h @@ -0,0 +1,506 @@ +/* graph.h */ +/* + This software library implements the maxflow algorithm + described in + + "An Experimental Comparison of Min-Cut/Max-Flow Algorithms for Energy Minimization in Vision." + Yuri Boykov and Vladimir Kolmogorov. + In IEEE Transactions on Pattern Analysis and Machine Intelligence (PAMI), + September 2004 + + This algorithm was developed by Yuri Boykov and Vladimir Kolmogorov + at Siemens Corporate Research. To make it available for public use, + it was later reimplemented by Vladimir Kolmogorov based on open publications. + + If you use this software for research purposes, you should cite + the aforementioned paper in any resulting publication. + + ---------------------------------------------------------------------- + + REUSING TREES: + + Starting with version 3.0, there is a also an option of reusing search + trees from one maxflow computation to the next, as described in + + "Efficiently Solving Dynamic Markov Random Fields Using Graph Cuts." + Pushmeet Kohli and Philip H.S. Torr + International Conference on Computer Vision (ICCV), 2005 + + If you use this option, you should cite + the aforementioned paper in any resulting publication. +*/ + + + +/* + For description, license, example usage see README.TXT. +*/ + +#ifndef __GRAPH_H__ +#define __GRAPH_H__ + +#include <string.h> +#include "block.h" + +#include <assert.h> +// NOTE: in UNIX you need to use -DNDEBUG preprocessor option to supress assert's!!! + + + +// captype: type of edge capacities (excluding t-links) +// tcaptype: type of t-links (edges between nodes and terminals) +// flowtype: type of total flow +// +// Current instantiations are in instances.inc +template <typename captype, typename tcaptype, typename flowtype> class Graph +{ +public: + typedef enum + { + SOURCE = 0, + SINK = 1 + } termtype; // terminals + typedef int node_id; + + ///////////////////////////////////////////////////////////////////////// + // BASIC INTERFACE FUNCTIONS // + // (should be enough for most applications) // + ///////////////////////////////////////////////////////////////////////// + + // Constructor. + // The first argument gives an estimate of the maximum number of nodes that can be added + // to the graph, and the second argument is an estimate of the maximum number of edges. + // The last (optional) argument is the pointer to the function which will be called + // if an error occurs; an error message is passed to this function. + // If this argument is omitted, exit(1) will be called. + // + // IMPORTANT: It is possible to add more nodes to the graph than node_num_max + // (and node_num_max can be zero). However, if the count is exceeded, then + // the internal memory is reallocated (increased by 50%) which is expensive. + // Also, temporarily the amount of allocated memory would be more than twice than needed. + // Similarly for edges. + // If you wish to avoid this overhead, you can download version 2.2, where nodes and edges are stored in blocks. + Graph(int node_num_max, int edge_num_max, void (*err_function)(char *) = NULL); + + // Destructor + ~Graph(); + + // Adds node(s) to the graph. By default, one node is added (num=1); then first call returns 0, second call returns 1, and so on. + // If num>1, then several nodes are added, and node_id of the first one is returned. + // IMPORTANT: see note about the constructor + node_id add_node(int num = 1); + + // Adds a bidirectional edge between 'i' and 'j' with the weights 'cap' and 'rev_cap'. + // IMPORTANT: see note about the constructor + void add_edge(node_id i, node_id j, captype cap, captype rev_cap); + + // Adds new edges 'SOURCE->i' and 'i->SINK' with corresponding weights. + // Can be called multiple times for each node. + // Weights can be negative. + // NOTE: the number of such edges is not counted in edge_num_max. + // No internal memory is allocated by this call. + void add_tweights(node_id i, tcaptype cap_source, tcaptype cap_sink); + + + // Computes the maxflow. Can be called several times. + // FOR DESCRIPTION OF reuse_trees, SEE mark_node(). + // FOR DESCRIPTION OF changed_list, SEE remove_from_changed_list(). + flowtype maxflow(bool reuse_trees = false, Block<node_id>* changed_list = NULL); + + // After the maxflow is computed, this function returns to which + // segment the node 'i' belongs (Graph<captype,tcaptype,flowtype>::SOURCE or Graph<captype,tcaptype,flowtype>::SINK). + // + // Occasionally there may be several minimum cuts. If a node can be assigned + // to both the source and the sink, then default_segm is returned. + termtype what_segment(node_id i, termtype default_segm = SOURCE); + + + + ////////////////////////////////////////////// + // ADVANCED INTERFACE FUNCTIONS // + // (provide access to the graph) // + ////////////////////////////////////////////// + +private: + struct node; + struct arc; + +public: + + //////////////////////////// + // 1. Reallocating graph. // + //////////////////////////// + + // Removes all nodes and edges. + // After that functions add_node() and add_edge() must be called again. + // + // Advantage compared to deleting Graph and allocating it again: + // no calls to delete/new (which could be quite slow). + // + // If the graph structure stays the same, then an alternative + // is to go through all nodes/edges and set new residual capacities + // (see functions below). + void reset(); + + //////////////////////////////////////////////////////////////////////////////// + // 2. Functions for getting pointers to arcs and for reading graph structure. // + // NOTE: adding new arcs may invalidate these pointers (if reallocation // + // happens). So it's best not to add arcs while reading graph structure. // + //////////////////////////////////////////////////////////////////////////////// + + // The following two functions return arcs in the same order that they + // were added to the graph. NOTE: for each call add_edge(i,j,cap,cap_rev) + // the first arc returned will be i->j, and the second j->i. + // If there are no more arcs, then the function can still be called, but + // the returned arc_id is undetermined. + typedef arc* arc_id; + arc_id get_first_arc(); + arc_id get_next_arc(arc_id a); + + // other functions for reading graph structure + int get_node_num() { return node_num; } + int get_arc_num() { return (int)(arc_last - arcs); } + void get_arc_ends(arc_id a, node_id& i, node_id& j); // returns i,j to that a = i->j + + /////////////////////////////////////////////////// + // 3. Functions for reading residual capacities. // + /////////////////////////////////////////////////// + + // returns residual capacity of SOURCE->i minus residual capacity of i->SINK + tcaptype get_trcap(node_id i); + // returns residual capacity of arc a + captype get_rcap(arc* a); + + ///////////////////////////////////////////////////////////////// + // 4. Functions for setting residual capacities. // + // NOTE: If these functions are used, the value of the flow // + // returned by maxflow() will not be valid! // + ///////////////////////////////////////////////////////////////// + + void set_trcap(node_id i, tcaptype trcap); + void set_rcap(arc* a, captype rcap); + + //////////////////////////////////////////////////////////////////// + // 5. Functions related to reusing trees & list of changed nodes. // + //////////////////////////////////////////////////////////////////// + + // If flag reuse_trees is true while calling maxflow(), then search trees + // are reused from previous maxflow computation. + // In this case before calling maxflow() the user must + // specify which parts of the graph have changed by calling mark_node(): + // add_tweights(i),set_trcap(i) => call mark_node(i) + // add_edge(i,j),set_rcap(a) => call mark_node(i); mark_node(j) + // + // This option makes sense only if a small part of the graph is changed. + // The initialization procedure goes only through marked nodes then. + // + // mark_node(i) can either be called before or after graph modification. + // Can be called more than once per node, but calls after the first one + // do not have any effect. + // + // NOTE: + // - This option cannot be used in the first call to maxflow(). + // - It is not necessary to call mark_node() if the change is ``not essential'', + // i.e. sign(trcap) is preserved for a node and zero/nonzero status is preserved for an arc. + // - To check that you marked all necessary nodes, you can call maxflow(false) after calling maxflow(true). + // If everything is correct, the two calls must return the same value of flow. (Useful for debugging). + void mark_node(node_id i); + + // If changed_list is not NULL while calling maxflow(), then the algorithm + // keeps a list of nodes which could potentially have changed their segmentation label. + // Nodes which are not in the list are guaranteed to keep their old segmentation label (SOURCE or SINK). + // Example usage: + // + // typedef Graph<int,int,int> G; + // G* g = new Graph(nodeNum, edgeNum); + // Block<G::node_id>* changed_list = new Block<G::node_id>(128); + // + // ... // add nodes and edges + // + // g->maxflow(); // first call should be without arguments + // for (int iter=0; iter<10; iter++) + // { + // ... // change graph, call mark_node() accordingly + // + // g->maxflow(true, changed_list); + // G::node_id* ptr; + // for (ptr=changed_list->ScanFirst(); ptr; ptr=changed_list->ScanNext()) + // { + // G::node_id i = *ptr; assert(i>=0 && i<nodeNum); + // g->remove_from_changed_list(i); + // // do something with node i... + // if (g->what_segment(i) == G::SOURCE) { ... } + // } + // changed_list->Reset(); + // } + // delete changed_list; + // + // NOTE: + // - If changed_list option is used, then reuse_trees must be used as well. + // - In the example above, the user may omit calls g->remove_from_changed_list(i) and changed_list->Reset() in a given iteration. + // Then during the next call to maxflow(true, &changed_list) new nodes will be added to changed_list. + // - If the next call to maxflow() does not use option reuse_trees, then calling remove_from_changed_list() + // is not necessary. ("changed_list->Reset()" or "delete changed_list" should still be called, though). + void remove_from_changed_list(node_id i) + { + assert(i>=0 && i<node_num && nodes[i].is_in_changed_list); + nodes[i].is_in_changed_list = 0; + } + + + + + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +private: + // internal variables and functions + + struct node + { + arc *first; // first outcoming arc + + arc *parent; // node's parent + node *next; // pointer to the next active node + // (or to itself if it is the last node in the list) + int TS; // timestamp showing when DIST was computed + int DIST; // distance to the terminal + int is_sink : 1; // flag showing whether the node is in the source or in the sink tree (if parent!=NULL) + int is_marked : 1; // set by mark_node() + int is_in_changed_list : 1; // set by maxflow if + + tcaptype tr_cap; // if tr_cap > 0 then tr_cap is residual capacity of the arc SOURCE->node + // otherwise -tr_cap is residual capacity of the arc node->SINK + + }; + + struct arc + { + node *head; // node the arc points to + arc *next; // next arc with the same originating node + arc *sister; // reverse arc + + captype r_cap; // residual capacity + }; + + struct nodeptr + { + node *ptr; + nodeptr *next; + }; + static const int NODEPTR_BLOCK_SIZE = 128; + + node *nodes, *node_last, *node_max; // node_last = nodes+node_num, node_max = nodes+node_num_max; + arc *arcs, *arc_last, *arc_max; // arc_last = arcs+2*edge_num, arc_max = arcs+2*edge_num_max; + + int node_num; + + DBlock<nodeptr> *nodeptr_block; + + void (*error_function)(char *); // this function is called if a error occurs, + // with a corresponding error message + // (or exit(1) is called if it's NULL) + + flowtype flow; // total flow + + // reusing trees & list of changed pixels + int maxflow_iteration; // counter + Block<node_id> *changed_list; + + ///////////////////////////////////////////////////////////////////////// + + node *queue_first[2], *queue_last[2]; // list of active nodes + nodeptr *orphan_first, *orphan_last; // list of pointers to orphans + int TIME; // monotonically increasing global counter + + ///////////////////////////////////////////////////////////////////////// + + void reallocate_nodes(int num); // num is the number of new nodes + void reallocate_arcs(); + + // functions for processing active list + void set_active(node *i); + node *next_active(); + + // functions for processing orphans list + void set_orphan_front(node* i); // add to the beginning of the list + void set_orphan_rear(node* i); // add to the end of the list + + void add_to_changed_list(node* i); + + void maxflow_init(); // called if reuse_trees == false + void maxflow_reuse_trees_init(); // called if reuse_trees == true + void augment(arc *middle_arc); + void process_source_orphan(node *i); + void process_sink_orphan(node *i); + + void test_consistency(node* current_node=NULL); // debug function +}; + + + + + + + + + + + +/////////////////////////////////////// +// Implementation - inline functions // +/////////////////////////////////////// + + + +template <typename captype, typename tcaptype, typename flowtype> + inline typename Graph<captype,tcaptype,flowtype>::node_id Graph<captype,tcaptype,flowtype>::add_node(int num) +{ + assert(num > 0); + + if (node_last + num > node_max) reallocate_nodes(num); + + if (num == 1) + { + node_last -> first = NULL; + node_last -> tr_cap = 0; + node_last -> is_marked = 0; + node_last -> is_in_changed_list = 0; + + node_last ++; + return node_num ++; + } + else + { + memset(node_last, 0, num*sizeof(node)); + + node_id i = node_num; + node_num += num; + node_last += num; + return i; + } +} + +template <typename captype, typename tcaptype, typename flowtype> + inline void Graph<captype,tcaptype,flowtype>::add_tweights(node_id i, tcaptype cap_source, tcaptype cap_sink) +{ + assert(i >= 0 && i < node_num); + + tcaptype delta = nodes[i].tr_cap; + if (delta > 0) cap_source += delta; + else cap_sink -= delta; + flow += (cap_source < cap_sink) ? cap_source : cap_sink; + nodes[i].tr_cap = cap_source - cap_sink; +} + +template <typename captype, typename tcaptype, typename flowtype> + inline void Graph<captype,tcaptype,flowtype>::add_edge(node_id _i, node_id _j, captype cap, captype rev_cap) +{ + assert(_i >= 0 && _i < node_num); + assert(_j >= 0 && _j < node_num); + assert(_i != _j); + assert(cap >= 0); + assert(rev_cap >= 0); + + if (arc_last == arc_max) reallocate_arcs(); + + arc *a = arc_last ++; + arc *a_rev = arc_last ++; + + node* i = nodes + _i; + node* j = nodes + _j; + + a -> sister = a_rev; + a_rev -> sister = a; + a -> next = i -> first; + i -> first = a; + a_rev -> next = j -> first; + j -> first = a_rev; + a -> head = j; + a_rev -> head = i; + a -> r_cap = cap; + a_rev -> r_cap = rev_cap; +} + +template <typename captype, typename tcaptype, typename flowtype> + inline typename Graph<captype,tcaptype,flowtype>::arc* Graph<captype,tcaptype,flowtype>::get_first_arc() +{ + return arcs; +} + +template <typename captype, typename tcaptype, typename flowtype> + inline typename Graph<captype,tcaptype,flowtype>::arc* Graph<captype,tcaptype,flowtype>::get_next_arc(arc* a) +{ + return a + 1; +} + +template <typename captype, typename tcaptype, typename flowtype> + inline void Graph<captype,tcaptype,flowtype>::get_arc_ends(arc* a, node_id& i, node_id& j) +{ + assert(a >= arcs && a < arc_last); + i = (node_id) (a->sister->head - nodes); + j = (node_id) (a->head - nodes); +} + +template <typename captype, typename tcaptype, typename flowtype> + inline tcaptype Graph<captype,tcaptype,flowtype>::get_trcap(node_id i) +{ + assert(i>=0 && i<node_num); + return nodes[i].tr_cap; +} + +template <typename captype, typename tcaptype, typename flowtype> + inline captype Graph<captype,tcaptype,flowtype>::get_rcap(arc* a) +{ + assert(a >= arcs && a < arc_last); + return a->r_cap; +} + +template <typename captype, typename tcaptype, typename flowtype> + inline void Graph<captype,tcaptype,flowtype>::set_trcap(node_id i, tcaptype trcap) +{ + assert(i>=0 && i<node_num); + nodes[i].tr_cap = trcap; +} + +template <typename captype, typename tcaptype, typename flowtype> + inline void Graph<captype,tcaptype,flowtype>::set_rcap(arc* a, captype rcap) +{ + assert(a >= arcs && a < arc_last); + a->r_cap = rcap; +} + + +template <typename captype, typename tcaptype, typename flowtype> + inline typename Graph<captype,tcaptype,flowtype>::termtype Graph<captype,tcaptype,flowtype>::what_segment(node_id i, termtype default_segm) +{ + if (nodes[i].parent) + { + return (nodes[i].is_sink) ? SINK : SOURCE; + } + else + { + return default_segm; + } +} + +template <typename captype, typename tcaptype, typename flowtype> + inline void Graph<captype,tcaptype,flowtype>::mark_node(node_id _i) +{ + node* i = nodes + _i; + if (!i->next) + { + /* it's not in the list yet */ + if (queue_last[1]) queue_last[1] -> next = i; + else queue_first[1] = i; + queue_last[1] = i; + i -> next = i; + } + i->is_marked = 1; +} + + +#endif diff --git a/Functions/functions_GraphCut/instances.inc b/Functions/functions_GraphCut/instances.inc new file mode 100644 index 0000000000000000000000000000000000000000..9c9ee37c27d6e3f0434bd277f309c17b2caa8c8e --- /dev/null +++ b/Functions/functions_GraphCut/instances.inc @@ -0,0 +1,16 @@ +#include "graph.h" + +#ifdef _MSC_VER +#pragma warning(disable: 4661) +#endif + +// Instantiations: <captype, tcaptype, flowtype> +// IMPORTANT: +// flowtype should be 'larger' than tcaptype +// tcaptype should be 'larger' than captype + +template class Graph<int,int,int>; +template class Graph<short,int,int>; +template class Graph<float,float,float>; +template class Graph<double,double,double>; + diff --git a/Functions/functions_GraphCut/maxflow.cpp b/Functions/functions_GraphCut/maxflow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..38b5dcc53794ea37776755674593a138bb294f30 --- /dev/null +++ b/Functions/functions_GraphCut/maxflow.cpp @@ -0,0 +1,702 @@ +/* maxflow.cpp */ + + +#include <stdio.h> +#include "graph.h" +#include "instances.inc" +#include "mex.h" + + +/* + special constants for node->parent +*/ +#define TERMINAL ( (arc *) 1 ) /* to terminal */ +#define ORPHAN ( (arc *) 2 ) /* orphan */ + + +#define INFINITE_D ((int)(((unsigned)-1)/2)) /* infinite distance to the terminal */ +#ifdef __cplusplus /*function for stopping from MATLAB*/ +extern "C" bool utIsInterruptPending(); +#else +extern bool utIsInterruptPending(); +#endif + +/***********************************************************************/ + +/* + Functions for processing active list. + i->next points to the next node in the list + (or to i, if i is the last node in the list). + If i->next is NULL iff i is not in the list. + + There are two queues. Active nodes are added + to the end of the second queue and read from + the front of the first queue. If the first queue + is empty, it is replaced by the second queue + (and the second queue becomes empty). +*/ + + +template <typename captype, typename tcaptype, typename flowtype> + inline void Graph<captype,tcaptype,flowtype>::set_active(node *i) +{ + if (!i->next) + { + /* it's not in the list yet */ + if (queue_last[1]) queue_last[1] -> next = i; + else queue_first[1] = i; + queue_last[1] = i; + i -> next = i; + } +} + +/* + Returns the next active node. + If it is connected to the sink, it stays in the list, + otherwise it is removed from the list +*/ +template <typename captype, typename tcaptype, typename flowtype> + inline typename Graph<captype,tcaptype,flowtype>::node* Graph<captype,tcaptype,flowtype>::next_active() +{ + node *i; + + while ( 1 ) + { + if (!(i=queue_first[0])) + { + queue_first[0] = i = queue_first[1]; + queue_last[0] = queue_last[1]; + queue_first[1] = NULL; + queue_last[1] = NULL; + if (!i) return NULL; + } + + /* remove it from the active list */ + if (i->next == i) queue_first[0] = queue_last[0] = NULL; + else queue_first[0] = i -> next; + i -> next = NULL; + + /* a node in the list is active iff it has a parent */ + if (i->parent) return i; + } +} + +/***********************************************************************/ + +template <typename captype, typename tcaptype, typename flowtype> + inline void Graph<captype,tcaptype,flowtype>::set_orphan_front(node *i) +{ + nodeptr *np; + i -> parent = ORPHAN; + np = nodeptr_block -> New(); + np -> ptr = i; + np -> next = orphan_first; + orphan_first = np; +} + +template <typename captype, typename tcaptype, typename flowtype> + inline void Graph<captype,tcaptype,flowtype>::set_orphan_rear(node *i) +{ + nodeptr *np; + i -> parent = ORPHAN; + np = nodeptr_block -> New(); + np -> ptr = i; + if (orphan_last) orphan_last -> next = np; + else orphan_first = np; + orphan_last = np; + np -> next = NULL; +} + +/***********************************************************************/ + +template <typename captype, typename tcaptype, typename flowtype> + inline void Graph<captype,tcaptype,flowtype>::add_to_changed_list(node *i) +{ + if (changed_list && !i->is_in_changed_list) + { + node_id* ptr = changed_list->New(); + *ptr = (node_id)(i - nodes); + i->is_in_changed_list = true; + } +} + +/***********************************************************************/ + +template <typename captype, typename tcaptype, typename flowtype> + void Graph<captype,tcaptype,flowtype>::maxflow_init() +{ + node *i; + + queue_first[0] = queue_last[0] = NULL; + queue_first[1] = queue_last[1] = NULL; + orphan_first = NULL; + + TIME = 0; + + for (i=nodes; i<node_last; i++) + { + i -> next = NULL; + i -> is_marked = 0; + i -> is_in_changed_list = 0; + i -> TS = TIME; + if (i->tr_cap > 0) + { + /* i is connected to the source */ + i -> is_sink = 0; + i -> parent = TERMINAL; + set_active(i); + i -> DIST = 1; + } + else if (i->tr_cap < 0) + { + /* i is connected to the sink */ + i -> is_sink = 1; + i -> parent = TERMINAL; + set_active(i); + i -> DIST = 1; + } + else + { + i -> parent = NULL; + } + } +} + +template <typename captype, typename tcaptype, typename flowtype> + void Graph<captype,tcaptype,flowtype>::maxflow_reuse_trees_init() +{ + node* i; + node* j; + node* queue = queue_first[1]; + arc* a; + nodeptr* np; + + queue_first[0] = queue_last[0] = NULL; + queue_first[1] = queue_last[1] = NULL; + orphan_first = orphan_last = NULL; + + TIME ++; + + while ((i=queue)) + { + queue = i->next; + if (queue == i) queue = NULL; + i->next = NULL; + i->is_marked = 0; + set_active(i); + + if (i->tr_cap == 0) + { + if (i->parent) set_orphan_rear(i); + continue; + } + + if (i->tr_cap > 0) + { + if (!i->parent || i->is_sink) + { + i->is_sink = 0; + for (a=i->first; a; a=a->next) + { + j = a->head; + if (!j->is_marked) + { + if (j->parent == a->sister) set_orphan_rear(j); + if (j->parent && j->is_sink && a->r_cap > 0) set_active(j); + } + } + add_to_changed_list(i); + } + } + else + { + if (!i->parent || !i->is_sink) + { + i->is_sink = 1; + for (a=i->first; a; a=a->next) + { + j = a->head; + if (!j->is_marked) + { + if (j->parent == a->sister) set_orphan_rear(j); + if (j->parent && !j->is_sink && a->sister->r_cap > 0) set_active(j); + } + } + add_to_changed_list(i); + } + } + i->parent = TERMINAL; + i -> TS = TIME; + i -> DIST = 1; + } + + //test_consistency(); + + /* adoption */ + while ((np=orphan_first)) + { + orphan_first = np -> next; + i = np -> ptr; + nodeptr_block -> Delete(np); + if (!orphan_first) orphan_last = NULL; + if (i->is_sink) process_sink_orphan(i); + else process_source_orphan(i); + } + /* adoption end */ + + //test_consistency(); +} + +template <typename captype, typename tcaptype, typename flowtype> + void Graph<captype,tcaptype,flowtype>::augment(arc *middle_arc) +{ + node *i; + arc *a; + tcaptype bottleneck; + + + /* 1. Finding bottleneck capacity */ + /* 1a - the source tree */ + bottleneck = middle_arc -> r_cap; + for (i=middle_arc->sister->head; ; i=a->head) + { + a = i -> parent; + if (a == TERMINAL) break; + if (bottleneck > a->sister->r_cap) bottleneck = a -> sister -> r_cap; + } + if (bottleneck > i->tr_cap) bottleneck = i -> tr_cap; + /* 1b - the sink tree */ + for (i=middle_arc->head; ; i=a->head) + { + a = i -> parent; + if (a == TERMINAL) break; + if (bottleneck > a->r_cap) bottleneck = a -> r_cap; + } + if (bottleneck > - i->tr_cap) bottleneck = - i -> tr_cap; + + + /* 2. Augmenting */ + /* 2a - the source tree */ + middle_arc -> sister -> r_cap += bottleneck; + middle_arc -> r_cap -= bottleneck; + for (i=middle_arc->sister->head; ; i=a->head) + { + a = i -> parent; + if (a == TERMINAL) break; + a -> r_cap += bottleneck; + a -> sister -> r_cap -= bottleneck; + if (!a->sister->r_cap) + { + set_orphan_front(i); // add i to the beginning of the adoption list + } + } + i -> tr_cap -= bottleneck; + if (!i->tr_cap) + { + set_orphan_front(i); // add i to the beginning of the adoption list + } + /* 2b - the sink tree */ + for (i=middle_arc->head; ; i=a->head) + { + a = i -> parent; + if (a == TERMINAL) break; + a -> sister -> r_cap += bottleneck; + a -> r_cap -= bottleneck; + if (!a->r_cap) + { + set_orphan_front(i); // add i to the beginning of the adoption list + } + } + i -> tr_cap += bottleneck; + if (!i->tr_cap) + { + set_orphan_front(i); // add i to the beginning of the adoption list + } + + + flow += bottleneck; +} + +/***********************************************************************/ + +template <typename captype, typename tcaptype, typename flowtype> + void Graph<captype,tcaptype,flowtype>::process_source_orphan(node *i) +{ + node *j; + arc *a0, *a0_min = NULL, *a; + int d, d_min = INFINITE_D; + + /* trying to find a new parent */ + for (a0=i->first; a0; a0=a0->next) + if (a0->sister->r_cap) + { + j = a0 -> head; + if (!j->is_sink && (a=j->parent)) + { + /* checking the origin of j */ + d = 0; + while ( 1 ) + { + if (j->TS == TIME) + { + d += j -> DIST; + break; + } + a = j -> parent; + d ++; + if (a==TERMINAL) + { + j -> TS = TIME; + j -> DIST = 1; + break; + } + if (a==ORPHAN) { d = INFINITE_D; break; } + j = a -> head; + } + if (d<INFINITE_D) /* j originates from the source - done */ + { + if (d<d_min) + { + a0_min = a0; + d_min = d; + } + /* set marks along the path */ + for (j=a0->head; j->TS!=TIME; j=j->parent->head) + { + j -> TS = TIME; + j -> DIST = d --; + } + } + } + } + + if (i->parent = a0_min) + { + i -> TS = TIME; + i -> DIST = d_min + 1; + } + else + { + /* no parent is found */ + add_to_changed_list(i); + + /* process neighbors */ + for (a0=i->first; a0; a0=a0->next) + { + j = a0 -> head; + if (!j->is_sink && (a=j->parent)) + { + if (a0->sister->r_cap) set_active(j); + if (a!=TERMINAL && a!=ORPHAN && a->head==i) + { + set_orphan_rear(j); // add j to the end of the adoption list + } + } + } + } +} + +template <typename captype, typename tcaptype, typename flowtype> + void Graph<captype,tcaptype,flowtype>::process_sink_orphan(node *i) +{ + node *j; + arc *a0, *a0_min = NULL, *a; + int d, d_min = INFINITE_D; + + /* trying to find a new parent */ + for (a0=i->first; a0; a0=a0->next) + if (a0->r_cap) + { + j = a0 -> head; + if (j->is_sink && (a=j->parent)) + { + /* checking the origin of j */ + d = 0; + while ( 1 ) + { + if (j->TS == TIME) + { + d += j -> DIST; + break; + } + a = j -> parent; + d ++; + if (a==TERMINAL) + { + j -> TS = TIME; + j -> DIST = 1; + break; + } + if (a==ORPHAN) { d = INFINITE_D; break; } + j = a -> head; + } + if (d<INFINITE_D) /* j originates from the sink - done */ + { + if (d<d_min) + { + a0_min = a0; + d_min = d; + } + /* set marks along the path */ + for (j=a0->head; j->TS!=TIME; j=j->parent->head) + { + j -> TS = TIME; + j -> DIST = d --; + } + } + } + } + + if (i->parent = a0_min) + { + i -> TS = TIME; + i -> DIST = d_min + 1; + } + else + { + /* no parent is found */ + add_to_changed_list(i); + + /* process neighbors */ + for (a0=i->first; a0; a0=a0->next) + { + j = a0 -> head; + if (j->is_sink && (a=j->parent)) + { + if (a0->r_cap) set_active(j); + if (a!=TERMINAL && a!=ORPHAN && a->head==i) + { + set_orphan_rear(j); // add j to the end of the adoption list + } + } + } + } +} + +/***********************************************************************/ + +template <typename captype, typename tcaptype, typename flowtype> + flowtype Graph<captype,tcaptype,flowtype>::maxflow(bool reuse_trees, Block<node_id>* _changed_list) +{ + node *i, *j, *current_node = NULL; + arc *a; + nodeptr *np, *np_next; + + if (!nodeptr_block) + { + nodeptr_block = new DBlock<nodeptr>(NODEPTR_BLOCK_SIZE, error_function); + } + + changed_list = _changed_list; + if (maxflow_iteration == 0 && reuse_trees) { if (error_function) (*error_function)("reuse_trees cannot be used in the first call to maxflow()!"); exit(1); } + if (changed_list && !reuse_trees) { if (error_function) (*error_function)("changed_list cannot be used without reuse_trees!"); exit(1); } + + if (reuse_trees) maxflow_reuse_trees_init(); + else maxflow_init(); + + // main loop + while ( 1 ) + { + if (utIsInterruptPending()) { /* check for a Ctrl-C event */ + mexPrintf("Ctrl-C Detected. END\n\n"); + mexEvalString("drawnow"); + return -1; + } + // mexEvalString("drawnow"); + // test_consistency(current_node); + + if ((i=current_node)) + { + i -> next = NULL; /* remove active flag */ + if (!i->parent) i = NULL; + } + if (!i) + { + if (!(i = next_active())) break; + } + + /* growth */ + if (!i->is_sink) + { + /* grow source tree */ + for (a=i->first; a; a=a->next) + if (a->r_cap) + { + j = a -> head; + if (!j->parent) + { + j -> is_sink = 0; + j -> parent = a -> sister; + j -> TS = i -> TS; + j -> DIST = i -> DIST + 1; + set_active(j); + add_to_changed_list(j); + } + else if (j->is_sink) break; + else if (j->TS <= i->TS && + j->DIST > i->DIST) + { + /* heuristic - trying to make the distance from j to the source shorter */ + j -> parent = a -> sister; + j -> TS = i -> TS; + j -> DIST = i -> DIST + 1; + } + } + } + else + { + /* grow sink tree */ + for (a=i->first; a; a=a->next) + if (a->sister->r_cap) + { + j = a -> head; + if (!j->parent) + { + j -> is_sink = 1; + j -> parent = a -> sister; + j -> TS = i -> TS; + j -> DIST = i -> DIST + 1; + set_active(j); + add_to_changed_list(j); + } + else if (!j->is_sink) { a = a -> sister; break; } + else if (j->TS <= i->TS && + j->DIST > i->DIST) + { + /* heuristic - trying to make the distance from j to the sink shorter */ + j -> parent = a -> sister; + j -> TS = i -> TS; + j -> DIST = i -> DIST + 1; + } + } + } + + TIME ++; + + if (a) + { + i -> next = i; /* set active flag */ + current_node = i; + + /* augmentation */ + augment(a); + /* augmentation end */ + + /* adoption */ + while ((np=orphan_first)) + { + np_next = np -> next; + np -> next = NULL; + + while ((np=orphan_first)) + { + orphan_first = np -> next; + i = np -> ptr; + nodeptr_block -> Delete(np); + if (!orphan_first) orphan_last = NULL; + if (i->is_sink) process_sink_orphan(i); + else process_source_orphan(i); + } + + orphan_first = np_next; + } + /* adoption end */ + } + else current_node = NULL; + } + // test_consistency(); + + if (!reuse_trees || (maxflow_iteration % 64) == 0) + { + delete nodeptr_block; + nodeptr_block = NULL; + } + + maxflow_iteration ++; + return flow; +} + +/***********************************************************************/ + + +template <typename captype, typename tcaptype, typename flowtype> + void Graph<captype,tcaptype,flowtype>::test_consistency(node* current_node) +{ + node *i; + arc *a; + int r; + int num1 = 0, num2 = 0; + + // test whether all nodes i with i->next!=NULL are indeed in the queue + for (i=nodes; i<node_last; i++) + { + if (i->next || i==current_node) num1 ++; + } + for (r=0; r<3; r++) + { + i = (r == 2) ? current_node : queue_first[r]; + if (i) + for ( ; ; i=i->next) + { + num2 ++; + if (i->next == i) + { + if (r<2) assert(i == queue_last[r]); + else assert(i == current_node); + break; + } + } + } + assert(num1 == num2); + + for (i=nodes; i<node_last; i++) + { + // test whether all edges in seach trees are non-saturated + if (i->parent == NULL) {} + else if (i->parent == ORPHAN) {} + else if (i->parent == TERMINAL) + { + if (!i->is_sink) assert(i->tr_cap > 0); + else assert(i->tr_cap < 0); + } + else + { + if (!i->is_sink) assert (i->parent->sister->r_cap > 0); + else assert (i->parent->r_cap > 0); + } + // test whether passive nodes in search trees have neighbors in + // a different tree through non-saturated edges + if (i->parent && !i->next) + { + if (!i->is_sink) + { + assert(i->tr_cap >= 0); + for (a=i->first; a; a=a->next) + { + if (a->r_cap > 0) assert(a->head->parent && !a->head->is_sink); + } + } + else + { + assert(i->tr_cap <= 0); + for (a=i->first; a; a=a->next) + { + if (a->sister->r_cap > 0) assert(a->head->parent && a->head->is_sink); + } + } + } + // test marking invariants + if (i->parent && i->parent!=ORPHAN && i->parent!=TERMINAL) + { + assert(i->TS <= i->parent->head->TS); + if (i->TS == i->parent->head->TS) assert(i->DIST > i->parent->head->DIST); + } + } +} + + + void Inst() + { + Graph<int,int,int> G(1,1); + G.maxflow(); + } \ No newline at end of file diff --git a/Functions/functions_auxiliary/click_points.m b/Functions/functions_auxiliary/click_points.m new file mode 100644 index 0000000000000000000000000000000000000000..8e10f2013c1fd623dadc6c8d1ab820c640b345af --- /dev/null +++ b/Functions/functions_auxiliary/click_points.m @@ -0,0 +1,13 @@ +function [xp,yp] = click_points + +hold on +[x,y,button] = ginputWhite(1); +xp = []; +yp = []; +while button==1 + xp = [xp,x]; + yp = [yp,y]; + plot(x,y,'co') + [x,y,button] = ginputWhite(1); +end +end \ No newline at end of file diff --git a/Functions/functions_auxiliary/compute_normals.m b/Functions/functions_auxiliary/compute_normals.m new file mode 100644 index 0000000000000000000000000000000000000000..741500e0946b4e2d24730e2ae25af669e1885e95 --- /dev/null +++ b/Functions/functions_auxiliary/compute_normals.m @@ -0,0 +1,23 @@ +function N = compute_normals(S,open) +% normals for a closed (or open!) curve + +if nargin<2 + open = false; +end + +X = S([end,1:end,1],:); % extended S + +dX = X(1:end-1,:)-X(2:end,:); % dX +Ne = [dX(:,2),-dX(:,1)]; % edge normals orthogonal to dX +de = sum(Ne.^2,2).^0.5; % for normalization +nonzero = de~=0; +Ne(nonzero,:) = Ne(nonzero,:)./de(nonzero,[1 1]); % edge normals +N = 0.5*(Ne(1:end-1,:)+Ne(2:end,:)); % vertices normals +d = sum(N.^2,2).^0.5; % for normalization +nonzero = d~=0; +N(nonzero,:) = N(nonzero,:)./d(nonzero,[1 1]); + +if open + N(1,:) = Ne(2,:); + N(end,:) = Ne(end-1,:); +end \ No newline at end of file diff --git a/Functions/functions_auxiliary/distribute_points.m b/Functions/functions_auxiliary/distribute_points.m new file mode 100644 index 0000000000000000000000000000000000000000..df0df4631c4ed1600279e50321b3715c1932f198 --- /dev/null +++ b/Functions/functions_auxiliary/distribute_points.m @@ -0,0 +1,42 @@ +function p_new = distribute_points(p,type,value,open) +% DISTRIBUTE_POINTS Distributes snakes points equidistantly +% (NOTE: this version can also handle open snake) +% +% DISTRIBUTE_POINTS(P) keeps the number of points +% DISTRIBUTE_POINTS(P,'number',N) returns N points +% DISTRIBUTE_POINTS(P,'ael',d) returns average edge length d +% Author: vand@dtu.dk + +if nargin<4 + open = false; +end + +if ~open + p = p([1:end,1],:); % closing the curve +end +N = size(p,1); % number of points (+ 1 for closed curve) +dist = sqrt(sum(diff(p).^2,2)); % edge segment lengths +t = [0;cumsum(dist)]; % current positions +% if we want the fixed edge length then the new N could be computed +% from the total length of the curve which is t(end) + +if nargin<2 + N_new = N; +else + switch lower(type) + case 'number' + N_new = value+~open; + case 'ael' + N_new = round(t(end)/value+~open); + otherwise + error('Unknown distribution type.') + end +end + +tq = linspace(0,t(end),N_new)'; % equidistant positions +%p_new(:,1) = interp1(t,p(:,1),tq); % distributed x +%p_new(:,2) = interp1(t,p(:,2),tq); % distributed y +p_new = interp1(t,p,tq); +if ~open + p_new = p_new(1:end-1,:); % opening the curve again +end diff --git a/Functions/functions_auxiliary/ginputWhite.m b/Functions/functions_auxiliary/ginputWhite.m new file mode 100644 index 0000000000000000000000000000000000000000..c9c1b220a25f861a2aac1791e128a814eb429343 --- /dev/null +++ b/Functions/functions_auxiliary/ginputWhite.m @@ -0,0 +1,319 @@ +function [out1,out2,out3] = ginputWhite(arg1) +%GINPUT Graphical input from mouse. +% [X,Y] = GINPUT(N) gets N points from the current axes and returns +% the X- and Y-coordinates in length N vectors X and Y. The cursor +% can be positioned using a mouse. Data points are entered by pressing +% a mouse button or any key on the keyboard except carriage return, +% which terminates the input before N points are entered. If the current +% axes is a geographic axes, the coordinates returned are latitude and +% longitude instead of X and Y. +% +% [X,Y] = GINPUT gathers an unlimited number of points until the +% return key is pressed. +% +% [X,Y,BUTTON] = GINPUT(N) returns a third result, BUTTON, that +% contains a vector of integers specifying which mouse button was +% used (1,2,3 from left) or ASCII numbers if a key on the keyboard +% was used. +% +% Examples: +% [x,y] = ginput; +% +% [x,y] = ginput(5); +% +% [x, y, button] = ginput(1); +% +% See also GTEXT, WAITFORBUTTONPRESS. + +% Copyright 1984-2018 The MathWorks, Inc. + +out1 = []; out2 = []; out3 = []; y = []; + +if ~matlab.ui.internal.isFigureShowEnabled + error(message('MATLAB:hg:NoDisplayNoFigureSupport', 'ginput')) +end + + % Check Inputs + if nargin == 0 + how_many = -1; + b = []; + else + how_many = arg1; + b = []; + if ~isPositiveScalarIntegerNumber(how_many) + error(message('MATLAB:ginput:NeedPositiveInt')) + end + if how_many == 0 + % If input argument is equal to zero points, + % give a warning and return empty for the outputs. + warning (message('MATLAB:ginput:InputArgumentZero')); + end + end + + % Get figure + fig = gcf; + drawnow; + figure(gcf); + + % Make sure the figure has an axes + gca(fig); + + % Setup the figure to disable interactive modes and activate pointers. + initialState = setupFcn(fig); + + % onCleanup object to restore everything to original state in event of + % completion, closing of figure errors or ctrl+c. + c = onCleanup(@() restoreFcn(initialState)); + + drawnow + char = 0; + + while how_many ~= 0 + waserr = 0; + try + keydown = wfbp; + catch %#ok<CTCH> + waserr = 1; + end + if(waserr == 1) + if(ishghandle(fig)) + cleanup(c); + error(message('MATLAB:ginput:Interrupted')); + else + cleanup(c); + error(message('MATLAB:ginput:FigureDeletionPause')); + end + end + % g467403 - ginput failed to discern clicks/keypresses on the figure it was + % registered to operate on and any other open figures whose handle + % visibility were set to off + figchildren = allchild(0); + if ~isempty(figchildren) + ptr_fig = figchildren(1); + else + error(message('MATLAB:ginput:FigureUnavailable')); + end + % old code -> ptr_fig = get(0,'CurrentFigure'); Fails when the + % clicked figure has handlevisibility set to callback + if(ptr_fig == fig) + if keydown + char = get(fig, 'CurrentCharacter'); + button = abs(get(fig, 'CurrentCharacter')); + else + button = get(fig, 'SelectionType'); + if strcmp(button,'open') + button = 1; + elseif strcmp(button,'normal') + button = 1; + elseif strcmp(button,'extend') + button = 2; + elseif strcmp(button,'alt') + button = 3; + else + error(message('MATLAB:ginput:InvalidSelection')) + end + end + + if(char == 13) % & how_many ~= 0) + % if the return key was pressed, char will == 13, + % and that's our signal to break out of here whether + % or not we have collected all the requested data + % points. + % If this was an early breakout, don't include + % the <Return> key info in the return arrays. + % We will no longer count it if it's the last input. + break; + end + + axes_handle = gca; + if ~(isa(axes_handle,'matlab.graphics.axis.Axes') ... + || isa(axes_handle,'matlab.graphics.axis.GeographicAxes')) + % If gca is not an axes, warn but keep listening for clicks. + % (There may still be other subplots with valid axes) + warning(message('MATLAB:Chart:UnsupportedConvenienceFunction', 'ginput', axes_handle.Type)); + continue + end + + drawnow; + pt = get(axes_handle, 'CurrentPoint'); + how_many = how_many - 1; + + + + out1 = [out1;pt(1,1)]; %#ok<AGROW> + y = [y;pt(1,2)]; %#ok<AGROW> + b = [b;button]; %#ok<AGROW> + end + end + + % Cleanup and Restore + cleanup(c); + + if nargout > 1 + out2 = y; + if nargout > 2 + out3 = b; + end + else + out1 = [out1 y]; + end + +end + +function valid = isPositiveScalarIntegerNumber(how_many) +valid = ~isa(how_many, 'matlab.graphics.Graphics') && ... % not a graphics handle + ~ischar(how_many) && ... % is numeric + isscalar(how_many) && ... % is scalar + (fix(how_many) == how_many) && ... % is integer in value + how_many >= 0; % is positive +end + +function key = wfbp +%WFBP Replacement for WAITFORBUTTONPRESS that has no side effects. + +fig = gcf; +current_char = []; %#ok<NASGU> + +% Now wait for that buttonpress, and check for error conditions +waserr = 0; +try + h=findall(fig,'Type','uimenu','Accelerator','C'); % Disabling ^C for edit menu so the only ^C is for + set(h,'Accelerator',''); % interrupting the function. + keydown = waitforbuttonpress; + current_char = double(get(fig,'CurrentCharacter')); % Capturing the character. + if~isempty(current_char) && (keydown == 1) % If the character was generated by the + if(current_char == 3) % current keypress AND is ^C, set 'waserr'to 1 + waserr = 1; % so that it errors out. + end + end + + set(h,'Accelerator','C'); % Set back the accelerator for edit menu. +catch %#ok<CTCH> + waserr = 1; +end +drawnow; +if(waserr == 1) + set(h,'Accelerator','C'); % Set back the accelerator if it errored out. + error(message('MATLAB:ginput:Interrupted')); +end + +if nargout>0, key = keydown; end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +end + +function initialState = setupFcn(fig) + +% Store Figure Handle. +initialState.figureHandle = fig; + +% Suspend figure functions +initialState.uisuspendState = uisuspend(fig); + +% Disable Plottools Buttons +initialState.toolbar = findobj(allchild(fig),'flat','Type','uitoolbar'); +if ~isempty(initialState.toolbar) + initialState.ptButtons = [uigettool(initialState.toolbar,'Plottools.PlottoolsOff'), ... + uigettool(initialState.toolbar,'Plottools.PlottoolsOn')]; + initialState.ptState = get (initialState.ptButtons,'Enable'); + set (initialState.ptButtons,'Enable','off'); +end + +% Disable AxesToolbar +initialState.axes = findobj(allchild(fig),'-isa','matlab.graphics.axis.AbstractAxes'); +tb = get(initialState.axes, 'Toolbar'); +if ~isempty(tb) && ~iscell(tb) + initialState.toolbarVisible{1} = tb.Visible; + tb.Visible = 'off'; +else + for i=1:numel(tb) + if ~isempty(tb{i}) + initialState.toolbarVisible{i} = tb{i}.Visible; + tb{i}.Visible = 'off'; + end + end +end + +%Setup empty pointer +cdata = NaN(16,16); +hotspot = [8,8]; +set(gcf,'Pointer','custom','PointerShapeCData',cdata,'PointerShapeHotSpot',hotspot) + +% Create uicontrols to simulate fullcrosshair pointer. +initialState.CrossHair = createCrossHair(fig); + +% Adding this to enable automatic updating of currentpoint on the figure +% This function is also used to update the display of the fullcrosshair +% pointer and make them track the currentpoint. +set(fig,'WindowButtonMotionFcn',@(o,e) dummy()); % Add dummy so that the CurrentPoint is constantly updated +initialState.MouseListener = addlistener(fig,'WindowMouseMotion', @(o,e) updateCrossHair(o,initialState.CrossHair)); + +% Get the initial Figure Units +initialState.fig_units = get(fig,'Units'); +end + +function restoreFcn(initialState) +if ishghandle(initialState.figureHandle) + delete(initialState.CrossHair); + + % Figure Units + set(initialState.figureHandle,'Units',initialState.fig_units); + + set(initialState.figureHandle,'WindowButtonMotionFcn',''); + delete(initialState.MouseListener); + + % Plottools Icons + if ~isempty(initialState.toolbar) && ~isempty(initialState.ptButtons) + set (initialState.ptButtons(1),'Enable',initialState.ptState{1}); + set (initialState.ptButtons(2),'Enable',initialState.ptState{2}); + end + + % Restore axestoolbar + for i=1:numel(initialState.axes) + if ~isempty(initialState.axes(i).Toolbar) + initialState.axes(i).Toolbar.Visible_I = initialState.toolbarVisible{i}; + end + end + + % UISUSPEND + uirestore(initialState.uisuspendState); +end +end + +function updateCrossHair(fig, crossHair) +% update cross hair for figure. +gap = 3; % 3 pixel view port between the crosshairs +cp = hgconvertunits(fig, [fig.CurrentPoint 0 0], fig.Units, 'pixels', fig); +cp = cp(1:2); +figPos = hgconvertunits(fig, fig.Position, fig.Units, 'pixels', fig.Parent); +figWidth = figPos(3); +figHeight = figPos(4); + +% Early return if point is outside the figure +if cp(1) < gap || cp(2) < gap || cp(1)>figWidth-gap || cp(2)>figHeight-gap + return +end + +set(crossHair, 'Visible', 'on'); +thickness = 1; % 1 Pixel thin lines. +set(crossHair(1), 'Position', [0 cp(2) cp(1)-gap thickness]); +set(crossHair(2), 'Position', [cp(1)+gap cp(2) figWidth-cp(1)-gap thickness]); +set(crossHair(3), 'Position', [cp(1) 0 thickness cp(2)-gap]); +set(crossHair(4), 'Position', [cp(1) cp(2)+gap thickness figHeight-cp(2)-gap]); +end + +function crossHair = createCrossHair(fig) +% Create thin uicontrols with black backgrounds to simulate fullcrosshair pointer. +% 1: horizontal left, 2: horizontal right, 3: vertical bottom, 4: vertical top +for k = 1:4 + crossHair(k) = uicontrol(fig, 'Style', 'text', 'Visible', 'off', 'Units', 'pixels', 'BackgroundColor', [1 1 1], 'HandleVisibility', 'off', 'HitTest', 'off'); %#ok<AGROW> +end +end + +function cleanup(c) +if isvalid(c) + delete(c); +end +end + +function dummy(~,~) +end diff --git a/Functions/functions_auxiliary/registerImages.m b/Functions/functions_auxiliary/registerImages.m new file mode 100644 index 0000000000000000000000000000000000000000..8d225e4361c4a390ac81efd9510b6e7b9f5c59d0 --- /dev/null +++ b/Functions/functions_auxiliary/registerImages.m @@ -0,0 +1,54 @@ +function [MOVINGREG] = registerImages(MOVING,FIXED,MatchThreshold) +%registerImages Register grayscale images using auto-generated code from Registration Estimator app. +% [MOVINGREG] = registerImages(MOVING,FIXED) Register grayscale images +% MOVING and FIXED using auto-generated code from the Registration +% Estimator app. The values for all registration parameters were set +% interactively in the app and result in the registered image stored in the +% structure array MOVINGREG. + +% Auto-generated by registrationEstimator app on 29-Apr-2020 +%----------------------------------------------------------- + + +% Feature-based techniques require license to Computer Vision Toolbox +checkLicense() + +% Default spatial referencing objects +fixedRefObj = imref2d(size(FIXED)); +movingRefObj = imref2d(size(MOVING)); + +% Detect SURF features +fixedPoints = detectSURFFeatures(FIXED,'MetricThreshold',750.000000,'NumOctaves',3,'NumScaleLevels',5); +movingPoints = detectSURFFeatures(MOVING,'MetricThreshold',750.000000,'NumOctaves',3,'NumScaleLevels',5); + +% Extract features +[fixedFeatures,fixedValidPoints] = extractFeatures(FIXED,fixedPoints,'Upright',false); +[movingFeatures,movingValidPoints] = extractFeatures(MOVING,movingPoints,'Upright',false); + +% Match features +indexPairs = matchFeatures(fixedFeatures,movingFeatures,'MatchThreshold',MatchThreshold,'MaxRatio',0.01*MatchThreshold); +fixedMatchedPoints = fixedValidPoints(indexPairs(:,1)); +movingMatchedPoints = movingValidPoints(indexPairs(:,2)); +MOVINGREG.FixedMatchedFeatures = fixedMatchedPoints; +MOVINGREG.MovingMatchedFeatures = movingMatchedPoints; + +% Apply transformation - Results may not be identical between runs because of the randomized nature of the algorithm +tform = estimateGeometricTransform(movingMatchedPoints,fixedMatchedPoints,'affine'); +MOVINGREG.Transformation = tform; +MOVINGREG.RegisteredImage = imwarp(MOVING, movingRefObj, tform, 'OutputView', fixedRefObj, 'SmoothEdges', true); + +% Store spatial referencing object +MOVINGREG.SpatialRefObj = fixedRefObj; + +end + +function checkLicense() + +% Check for license to Computer Vision Toolbox +CVSTStatus = license('test','Video_and_Image_Blockset'); +if ~CVSTStatus + error(message('images:imageRegistration:CVSTRequired')); +end + +end + diff --git a/Functions/functions_auxiliary/regularization_matrix_open.m b/Functions/functions_auxiliary/regularization_matrix_open.m new file mode 100644 index 0000000000000000000000000000000000000000..85816be0ec223068c0f5c15f29d277a517414fc2 --- /dev/null +++ b/Functions/functions_auxiliary/regularization_matrix_open.m @@ -0,0 +1,14 @@ +function B = regularization_matrix_open(N,alpha,beta) +% B is an NxN matrix for imposing elasticity and rigidity to snakes +% alpha is weigth for second derivative (elasticity) +% beta is weigth for (-)fourth derivative (rigidity) + +r = zeros(1,N); +r(1:3) = alpha*[-2 1 0]/4 + beta*[-6 4 -1]/16; +r(end-1:end) = alpha*[0 1]/4 + beta*[-1 4]/16; +A = toeplitz(r); +A([1,2,end-1,end],:) = 0; +A(2,1:3) = alpha*[1 -2 1]/4; +A(end-1,end-2:end) = alpha*[1 -2 1]/4; + +B = inv(eye(N)-A); \ No newline at end of file diff --git a/Functions/functions_auxiliary/slice_points_interpolation.m b/Functions/functions_auxiliary/slice_points_interpolation.m new file mode 100644 index 0000000000000000000000000000000000000000..e841338ab4ef382b471331f8b6b0e69951c80a7f --- /dev/null +++ b/Functions/functions_auxiliary/slice_points_interpolation.m @@ -0,0 +1,48 @@ +function pk = slice_points_interpolation(p1,p2,n,m,flag_fig) +% SLICE_POINTS_INTERPOLATION takes two sets of points and the range of +% interpolition n & m and return pk which is the interpolants points +% [n:(n-m+1):m] +% +% the sampling is done by computing +% v = p1-p2 +% pk = p2 + d/norm(v)*v where d is the increment of the distances +% +% written by bepi@dtu.dk first version 2019.12.02 + +%default no figure +if nargout < 3 + flag_fig = 0; +end + +d_p = sqrt(sum((p1-p2).^2,2)); + +pk = nan(n-m+1,3,length(d_p)); +for j = 1:length(d_p) + sim = linspace(0,d_p(j),n-m+1); + for i = 1:length(sim) + if sim(i) == 0 + pk(i,1:2,j) = p2(j,:); + else + pk(i,1:2,j) = p2(j,:) + (sim(i)/d_p(j)).*(p1(j,:)-p2(j,:)); + end + pk(i,3,j) = m + i-1;%n,m = (220:800)'; + end +end + +if flag_fig + figure, + plot3([p2(:,1),p2(:,1)]',... + [p1(:,2),p1(:,2)]',... + [ones(size(p1,1),1)*n,ones(size(p1,1),1)*m]') + xlabel('x') ; ylabel('y') ; zlabel('z') + hold on + + for s = 1:size(pk,3) + tmp = squeeze(pk(:,:,s)); + plot3(tmp(:,1),tmp(:,2),tmp(:,3),'-xm') + end +end + +end + + diff --git a/Functions/functions_cost/smoothsurface_cost.m b/Functions/functions_cost/smoothsurface_cost.m new file mode 100644 index 0000000000000000000000000000000000000000..eb93a6736624c64e9af9d86b8d0d53564461e4ff --- /dev/null +++ b/Functions/functions_cost/smoothsurface_cost.m @@ -0,0 +1,28 @@ +function cost = smoothsurface_cost(V,mu,std,s1,s2) +% abda@dtu.dk + +if nargin == 1 + mu = 23000; + std = 4000; + s1 = 5; + s2 = 1.5; +elseif nargin == 3 + s1 = 5; + s2 = 1.5; +end + +V_exp = 1/(std*sqrt(2*pi))*exp(-(V-mu).^2/(2*std^2)); +V_exp = V_exp/(max(V_exp(:))); +g = get_gauss(s1); +[~,dg] = get_gauss(s2); +cost = imfilter(imfilter(V_exp, dg','replicate'),g,'replicate'); + +cost = (cost-min(cost(:)))/(max(cost(:))-min(cost(:))); + +function [g,dg] = get_gauss(s) + +x = -ceil(3*s):ceil(3*s); +g = exp(-x.^2/(2*s*s)); +g = g/sum(g); +dg = -x/(s^2).*g; +%ddg = -g/(s^2) - x/(s^2).*dg; \ No newline at end of file diff --git a/Functions/functions_curvature/surfature.m b/Functions/functions_curvature/surfature.m new file mode 100644 index 0000000000000000000000000000000000000000..93ef859710d991a642b48153fe43208f0b8fce3b --- /dev/null +++ b/Functions/functions_curvature/surfature.m @@ -0,0 +1,70 @@ +function [K,H,Pmax,Pmin] = surfature(X,Y,Z) +% SURFATURE - COMPUTE GAUSSIAN AND MEAN CURVATURES OF A SURFACE +% [K,H] = SURFATURE(X,Y,Z), WHERE X,Y,Z ARE 2D ARRAYS OF POINTS ON THE +% SURFACE. K AND H ARE THE GAUSSIAN AND MEAN CURVATURES, RESPECTIVELY. +% SURFATURE RETURNS 2 ADDITIONAL ARGUEMENTS, +% [K,H,Pmax,Pmin] = SURFATURE(...), WHERE Pmax AND Pmin ARE THE MINIMUM +% AND MAXIMUM CURVATURES AT EACH POINT, RESPECTIVELY. +% +% modified by bepi@dtu.dk 2020.03.17 +% +% Example +% [X,Y,Z] = peaks; +% [K,H,P1,P2] = surfature(X,Y,Z); +% surf(X,Y,Z,H,'facecolor','interp'); +% set(gca,'clim',[-1,1]) + +% First Derivatives +[Xu,Xv] = gradient(X); +[Yu,Yv] = gradient(Y); +[Zu,Zv] = gradient(Z); + +% Second Derivatives +[Xuu,~] = gradient(Xu); +[Yuu,~] = gradient(Yu); +[Zuu,~] = gradient(Zu); + +[Xuv,Xvv] = gradient(Xv); +[Yuv,Yvv] = gradient(Yv); +[Zuv,Zvv] = gradient(Zv); + +% Reshape 2D Arrays into Vectors +Xu = Xu(:); Yu = Yu(:); Zu = Zu(:); +Xv = Xv(:); Yv = Yv(:); Zv = Zv(:); +Xuu = Xuu(:); Yuu = Yuu(:); Zuu = Zuu(:); +Xuv = Xuv(:); Yuv = Yuv(:); Zuv = Zuv(:); +Xvv = Xvv(:); Yvv = Yvv(:); Zvv = Zvv(:); + +Xu = [Xu Yu Zu]; +Xv = [Xv Yv Zv]; +Xuu = [Xuu Yuu Zuu]; +Xuv = [Xuv Yuv Zuv]; +Xvv = [Xvv Yvv Zvv]; + +% First fundamental Coeffecients of the surface (E,F,G) +E = dot(Xu,Xu,2); +F = dot(Xu,Xv,2); +G = dot(Xv,Xv,2); + +m = cross(Xu,Xv,2); +p = sqrt(dot(m,m,2)); +n = m./[p p p]; + +% Second fundamental Coeffecients of the surface (L,M,N) +L = dot(Xuu,n,2); +M = dot(Xuv,n,2); +N = dot(Xvv,n,2); + +[s,t] = size(Z); + +% Gaussian Curvature +K = (L.*N - M.^2)./(E.*G - F.^2); +K = reshape(K,s,t); + +% Mean Curvature +H = (E.*N + G.*L - 2.*F.*M)./(2*(E.*G - F.^2)); +H = reshape(H,s,t); + +% Principal Curvatures +Pmax = H + sqrt(H.^2 - K); +Pmin = H - sqrt(H.^2 - K); diff --git a/Functions/functions_gridcut/grid_cut.m b/Functions/functions_gridcut/grid_cut.m new file mode 100644 index 0000000000000000000000000000000000000000..9635300bddbcf1a3721fe34dd0d295d7c71b664d --- /dev/null +++ b/Functions/functions_gridcut/grid_cut.m @@ -0,0 +1,252 @@ +function s = grid_cut(s_cost, r_cost, delta_xy, wrap_xy, delta_lu) +%GRID_CUT detects terrain-like surfaces in a volumetric data. +% +% Detected surfaces are terrain-like, i.e. height z is a function of the +% position (x,y). Only on-surface costs, only in-region costs or both type +% of costs may be used. When using in-region costs for multiple surfaces, +% surfaces are non-intersecting and ordered, so that the first surface has +% the smallest z values, and the last surface has the largest z values. +% +% S = GRID_CUT(S_COST,R_COST,DELTA_XY,WRAP_XY,DELTA_LU) +% +% S_COST is a size (X,Y,Z,K) surface cost function. Cost (x,y,z,k) is an +% inverse to likelihood of a voxel (x,y,z) containing surface k. +% K is the number of surfaces and Z is up. When using only in-region +% costs, S_COST should either contain only zeros or be empty. +% R_COST is a size (X,Y,Z,K+1) region cost function. Cost (x,y,z,k) is an +% inverse to likelihood of a voxel (x,y,z) being in a region between +% the surface k and k+1, where the first and the last region are +% boundedend with the volume boundary. When using only on-surface +% costs, R_COST should either contain only zeros or be empty. +% DELTA_XY is a size (K,2) array of stiffness parameters for x and y. If a +% size (1,2) array is given, the same stiffness parameteres are used +% for all surfaces. If size (K,1) array is given, the same stiffness +% is used for x and y. +% WRAP_XY is a length 2 array of boolean wrap options for x and y. If not +% given defaults to false. +% DELTA_LU is a size (K-1,2) array of lower and upper surface overlaps, +% [dl du]. The constraint is: dl <= surf_k - surf_k+1 <= du. When +% using in-region costs, the surfaces are non-intersecting and +% ordered which requires dl > 0 and du >= dl. If a size (1,2) array +% is given, the same overlap parameteres are used for all surfaces. +% If not given, defaults to "no constraint" (dl=-Z, du=Z) when using +% only on-surface costs and to "no overlap" (dl=1, du=Z) otherwise. +% S is a size (X,Y,K) matrix of z coordinates for K segmented surfaces. +% +% Based on: +% "Optimal surface segmentation in volumetric images-a graph-theoretic +% approach." Li, Wu, Chen and Sonka. PAMI 2006. +% "Incorporation of Regional Information in Optimal 3-D Graph Search with +% Application for Intraretinal Layer Segmentation of Optical Coherence +% Tomography Images". Haeker, Wu, Abramoff, Kardon and Sonka. IPMI 2007. +% +% Author: Vedrana Andersen Dahl, vand@dtu.dk, 2013 +% +% NOTE: needs Kolmogorovs implementation of the algorithm from +% "An Experimental Comparison of Min-Cut/Max-Flow Algorithms for +% Energy Minimization in Vision." +% Yuri Boykov and Vladimir Kolmogorov. PAMI 2004 + +% assigning an unique index to each vertex in the first 3D graph (first surface) + +if nargin<5 + delta_lu = []; +end +if nargin<4 + wrap_xy = []; +end +[s_cost, r_cost, delta_xy, wrap_xy, delta_lu] = assign_defaults... + (s_cost, r_cost, delta_xy, wrap_xy, delta_lu); +[X,Y,Z,K] = size(s_cost); +dimension = [X Y Z]; +indices = reshape(1:prod(dimension),dimension); +base = indices(:,:,1); +next_surface = prod(dimension); % adding to indices is a shift to next surface +layers = (0:(K-1))*next_surface; + +wrap = [wrap_xy 0]; % we do not wrap in z direction + +% EDGES WHICH ARE THE SAME FOR ALL SURFACES: +% intracolumn arcs pointing down, Equation (2) +Ea1 = displacement_to_edges(indices, [0 0 -1], wrap); % first surface +Ea = repmat(Ea1,[K 1]) + kron(layers(:),ones(size(Ea1))); % all surfaces +Ea = [Ea,ones(size(Ea,1),1)*[inf,0]]; % assigning [inf 0] weight +% base edges, part of intercolumn arcs, Equation (3) +erxb1 = displacement_to_edges(base, [1 0 0], wrap); +eryb1 = displacement_to_edges(base, [0 1 0], wrap); +Erb1 = [erxb1; eryb1]; % first surface +Erb = repmat(Erb1,[K 1]) + kron(layers(:),ones(size(Erb1))); % all surfaces +% assigning [inf inf] weight, base edges are in both directions +Erb = [Erb,ones(size(Erb,1),1)*[inf,inf]]; + +% EDGES WHICH DEPEND ON SURFACE STIFFNESS: +% slanted edges, part of intercolumn arcs, Equation (3) +Erpm = []; % preallocation length could be computed from X,Y,Z,delta and wrap +for k = 1:K + erxp = displacement_to_edges(indices, [1 0 -delta_xy(k,1)], wrap); + erxm = displacement_to_edges(indices, [-1 0 -delta_xy(k,1)], wrap); + eryp = displacement_to_edges(indices, [0 1 -delta_xy(k,2)], wrap); + erym = displacement_to_edges(indices, [0 -1 -delta_xy(k,2)], wrap); + erpm = [erxp; erxm; eryp; erym] + layers(k); + Erpm = [Erpm;erpm]; +end +% assigning [inf 0] weight, slanted edges are in one direction +Erpm = [Erpm,ones(size(Erpm,1),1)*[inf,0]]; +Er = [Erpm; Erb]; % all inter edges, for all surfaces +E = [Ea;Er]; % all intracolumn and intercolumn edges, for all surfaces + +% EDGES WHICH DEPEND ON DISTANCE BETWEEN SURFACES: +% intersurface arc, Equation (4) +if K>1 % only if we have more than 1 surface + Es = []; % preallocation length could be computed + for k = 1:K-1 + esl = displacement_to_edges(indices, [0 0 delta_lu(k,1)], wrap); + esl = esl + ones(size(esl,1),1)*layers([k,k+1]); + esu = displacement_to_edges(indices, [0 0 -delta_lu(k,2)], wrap); + esu = esu + ones(size(esu,1),1)*layers([k+1,k]); + es = [esl;esu]; + Es = [Es;es]; + end + Es = [Es,ones(size(Es,1),1)*[inf,0]]; % assigning [inf 0] weight + + % intersurface base edges + esb = [layers(1:end-1)',layers(2:end)']+1; %first vertex in all surfaces + Esb = [esb,ones(K-1,1)*[inf,inf]]; % assigning [inf inf] + E = [E;Es;Esb]; % all intracolumn, intercolumn and intersurface edges, for all surfaces +end + +% EDGES WHICH DEPEND ON SURFACE LIKELIHOOD: +% up to here we do not use s_cost -- consider efficient algorithm if solving +% multiple problems of the same size +% vertex s_cost, Equation (1), done simulatniously for all surfaces +w_on = -1*ones([dimension,K]); % to prevent empty solution, see second half of section 4.1 +w_on(:,:,2:end,:) = double(s_cost(:,:,2:end,:))-double(s_cost(:,:,1:end-1,:)); +% In case of layered surfaces vertices which can't be realized should be removed, +% i.e. topmost vertices of lower surface and lowest vertices of higher surface. +% Instead of removing vertices, I assign inf weight to topmost vertices. +% And this only when delta_lu are both positive (both negative can be +% avoided by proper ordering of surfaces). +if K>1 % only if we have more than 1 surface + for k=1:K-1 % assigning inf on-surface weights instead of removing vertices + if delta_lu(k,1)>0 && delta_lu(k,1)<= delta_lu(k,2) % bit clumsy + w_on(:,:,end-delta_lu(k,1)+1:end,k) = inf; + end + end +end + +% In-region cost, converting to double to avoid problems when using images +w_in = double(r_cost(:,:,:,1:end-1))-double(r_cost(:,:,:,2:end)); +% There are issues concerning in-region cost e.g. a better way of preventing +% empty solutions or topmost solutions. +w_in(:,:,1,:) = -inf; % preventing empty solution + +w = w_on + w_in; + +Vp_ix = find(w(:)>=0); % positive vertices, to be connected to sink +Vm_ix = find(w(:)<0); % negative vertices, to be connected to source +Es = [Vm_ix, -w(Vm_ix), zeros(length(Vm_ix),1)]; % source edges +Et = [Vp_ix, zeros(length(Vp_ix),1), w(Vp_ix)]; % sink edges +Est = [Es;Et]; % all terminal edges + +% FINDING GRAPH CUT USING MAGIC +Scut = GraphCutMex(prod(dimension)*K,Est,E); + +% retreiving surfaces as the upper envelope of Scut +S = zeros(X,Y,Z,K); +S(Scut) = 1; +s = zeros(X,Y,K); +for ki = 1:K + for x = 1:X + for y = 1:Y + s(x,y,ki) = find(S(x,y,:,ki),1,'last'); + end + end +end +end + +% NOTE: when some of the intersurface edges point upwards, i.e. when +% delta_l is positive, the lowest delta_l rows of a graph for higher +% surface can not appear in any feasible solution and can be removed. +% This has NOT been implemented here. + +function edges = displacement_to_edges(indices, disp, wrap) +% indices -- 3D volume of indices +% disp -- length 3 vector of displacements, given as [x y,z] +% wrapping -- length 3 vector of boolean options, given as [wrapx wrapy wrapz] +% edges -- two columns of indices, each line is an edge. + +[x_from,x_into] = displace_1D_indices(size(indices,1),disp(1),wrap(1)); +[y_from,y_into] = displace_1D_indices(size(indices,2),disp(2),wrap(2)); +[z_from,z_into] = displace_1D_indices(size(indices,3),disp(3),wrap(3)); + +indices_from = indices(x_from,y_from,z_from); +indices_into = indices(x_into,y_into,z_into); + +edges = [indices_from(:),indices_into(:)]; +end + +function [from,into] = displace_1D_indices(dim,disp,wrap) +% dim -- length of the indices vector +% disp -- length of the displacement +% wrap -- boolean, indicating trimming or wrapping + +indices = 1:dim; +if wrap + from = indices([1-min(disp,0):end,1:-min(disp,0)]); + into = indices([1+max(disp,0):end,1:max(disp,0)]); +else + from = indices(1-min(disp,0):end-max(disp,0)); + into = indices(1+max(disp,0):end+min(disp,0)); +end +end + +function [s_cost, r_cost, delta_xy, wrap_xy, delta_lu] = assign_defaults... + (s_cost, r_cost, delta_xy, wrap_xy, delta_lu) +% either cost_s or cost_r need to be given (not empty and not all zeros) +if isempty(s_cost) + s_cost = zeros(size(r_cost)-[0,0,0,1]); +end +if isempty(r_cost) + r_cost = zeros([size(s_cost,1),size(s_cost,2),size(s_cost,3),... + size(s_cost,4)]+[0,0,0,1]); % to allow s_cost to be 3D + regional = false; +elseif all(r_cost(:)==0) + regional = false; +else + regional = true; +end + +[X,Y,Z,K] = size(s_cost); +[Xr,Yr,Zr,Krplus1] = size(r_cost); +if ~X==Xr || ~Y==Yr || ~Z==Zr || ~(K+1)==Krplus1 + error('Error using grid_cut. Dimensions of s_cost and r_cost must agree.') +end + +% smoothness constraint may be given once for all surfaces +if size(delta_xy,1)==1 + delta_xy = ones(K,1)*delta_xy; +end +% smoothness constraint may be given once for both directions +if size(delta_xy,2)==1 + delta_xy = delta_xy*ones(1,2); +end + +if isempty(wrap_xy) || (numel(wrap_xy)==1 && wrap_xy==0) + wrap_xy = [0 0]; +end + +% overlap constraint defaults to no overlap in region case +% and no constraint in surface case +if isempty(delta_lu) + if regional % no overlap + delta_lu = [1 Z]; + else % no constraint + delta_lu = [-Z Z]; + end +end + +% overlap constraint may be given once for all surface pairs +if all(size(delta_lu)==[1,2]) + delta_lu = ones(K-1,1)*delta_lu; +end +end \ No newline at end of file diff --git a/Functions/functions_mesh/do_mesh_example.m b/Functions/functions_mesh/do_mesh_example.m new file mode 100644 index 0000000000000000000000000000000000000000..6166d1ab9f0dd78280818b522fbfda17c15a0719 --- /dev/null +++ b/Functions/functions_mesh/do_mesh_example.m @@ -0,0 +1,87 @@ +clear all + +Lx = 2; +Ly = 1.3; +Lz = 0.5; + +nx = 2;%10 +ny = 3;%7 +nz = 4;%5 + +cnt = 1; +for i=1:nx + for j=1:ny + for k=1:nz + nodes(cnt,:) = [cnt i*Lx/nx j*Ly/ny k*Lz/nz]; + cnt = cnt + 1; + end + end +end + +number_of_nodes = cnt-1; + +clf,plot3(nodes(:,2),nodes(:,3),nodes(:,4),'.') + +elements = []; +cnt = 1; +for i=1:nx-1 + for j=1:ny-1 + for k=1:nz-1 + x0 = i*Lx/nx; + y0 = j*Ly/ny; + z0 = k*Lz/nz; + x1 = (i+1)*Lx/nx; + y1 = (j+1)*Ly/ny; + z1 = (k+1)*Lz/nz; + + dist_to_nodes = ((nodes(:,2)-x0).^2+(nodes(:,3)-y0).^2+(nodes(:,4)-z0).^2); + [minval,minidx] = min(dist_to_nodes); + corner(1) = minidx; + dist_to_nodes = ((nodes(:,2)-x1).^2+(nodes(:,3)-y0).^2+(nodes(:,4)-z0).^2); + [minval,minidx] = min(dist_to_nodes); + corner(2) = minidx; + dist_to_nodes = ((nodes(:,2)-x1).^2+(nodes(:,3)-y1).^2+(nodes(:,4)-z0).^2); + [minval,minidx] = min(dist_to_nodes); + corner(3) = minidx; + dist_to_nodes = ((nodes(:,2)-x0).^2+(nodes(:,3)-y1).^2+(nodes(:,4)-z0).^2); + [minval,minidx] = min(dist_to_nodes); + corner(4) = minidx; + + dist_to_nodes = ((nodes(:,2)-x0).^2+(nodes(:,3)-y0).^2+(nodes(:,4)-z1).^2); + [minval,minidx] = min(dist_to_nodes); + corner(5) = minidx; + + dist_to_nodes = ((nodes(:,2)-x1).^2+(nodes(:,3)-y0).^2+(nodes(:,4)-z1).^2); + [minval,minidx] = min(dist_to_nodes); + corner(6) = minidx; + + dist_to_nodes = ((nodes(:,2)-x1).^2+(nodes(:,3)-y1).^2+(nodes(:,4)-z1).^2); + [minval,minidx] = min(dist_to_nodes); + corner(7) = minidx; + dist_to_nodes = ((nodes(:,2)-x0).^2+(nodes(:,3)-y1).^2+(nodes(:,4)-z1).^2); + [minval,minidx] = min(dist_to_nodes); + corner(8) = minidx; + + elements(cnt,:)=[cnt corner([1 2 3 4 5 6 7 8 ])]; + cnt=cnt+1; + + end + end +end + +figure,patch('Faces',elements(:,2:end),'Vertices',nodes(:,2:4)) + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% fid = fopen('output_mesh_example.inp','w'); +% fprintf(fid,'*Part, name=my_mesh \n'); +% fprintf(fid,'*Node \n'); +% for i=1:size(nodes,1) +% fprintf(fid,'%d, %f, %f, %f \n',nodes(i,:)); +% end +% fprintf(fid,'*Element, type=C3D8R \n'); +% for i = 1:size(elements,1) +% fprintf(fid,'%d, %d, %d, %d, %d, %d, %d, %d, %d \n',elements(i,:)); +% end +% +% fprintf(fid,'*End Part \n'); \ No newline at end of file diff --git a/Functions/functions_mesh/elements_mesh.m b/Functions/functions_mesh/elements_mesh.m new file mode 100644 index 0000000000000000000000000000000000000000..f62f26a20d0c53f42c160fd2bf29251b8c16900e --- /dev/null +++ b/Functions/functions_mesh/elements_mesh.m @@ -0,0 +1,19 @@ +function ele = elements_mesh(mesh) + +sz0 = size(mesh); +ny = sz0(1); +nz = sz0(2); +cnt = 1; +ele = zeros((nz-1)*(ny-1),9); +%h = waitbar(0,'1','Name',inputname(mesh)); +for j = 1:ny-1 + for k = 1:nz-1 + i = (j-1)*nz+k; + ele(cnt,:)=[cnt i ny*nz+i ny*nz+nz+i nz+i i+1 ny*nz+i+1 ny*nz+nz+i+1 nz+i+1]; + cnt=cnt+1; + end + %waitbar(j/(ny-1),h,sprintf('j: %d',j)) +end +%close(h) + +end \ No newline at end of file diff --git a/Functions/functions_mesh/write_mesh.m b/Functions/functions_mesh/write_mesh.m new file mode 100644 index 0000000000000000000000000000000000000000..9495f455914a59a7cdee8f15784c05d714fa7c90 --- /dev/null +++ b/Functions/functions_mesh/write_mesh.m @@ -0,0 +1,18 @@ +function write_mesh(nodes,elements,filename) + +fid = fopen([filename,'.inp'],'w'); +[~,name,~] = fileparts(filename); +fprintf(fid,'*Part, name=%s \n',name); +fprintf(fid,'*Node \n'); +for i=1:size(nodes,1) + fprintf(fid,'%d, %f, %f, %f \n',nodes(i,:)); +end + +fprintf(fid,'*Element, type=C3D8R \n'); +for i = 1:size(elements,1) + fprintf(fid,'%d, %d, %d, %d, %d, %d, %d, %d, %d \n',elements(i,:)); +end + +fprintf(fid,'*End Part \n'); +fclose(fid); +end \ No newline at end of file diff --git a/Functions/functions_visualization/show_matvol.m b/Functions/functions_visualization/show_matvol.m new file mode 100644 index 0000000000000000000000000000000000000000..24992ce1a504149b4dd875305d1dae26aa0a9de4 --- /dev/null +++ b/Functions/functions_visualization/show_matvol.m @@ -0,0 +1,48 @@ +function show_matvol(V,clim) +% SHOW_MATVOL Shows volumetric data +% show_matvol(V,clim) +% Inputs: volume, grayscale color limits (optional) +% Copyright 2018 Vedrana Andersen Dahl, vand@dtu.dk + +figure('Units','Normalized','Position',[0.1 0.3 0.5 0.6],... + 'KeyPressFcn',@key_press); +dim = size(V); +if nargin<2 || isempty(clim) + clim = [min(double(V(:))),max(double(V(:)))]; +end +z = round(0.5*(dim(3)+1)); +%update_drawing +i = imagesc(V(:,:,z),clim); +title(['slice ',num2str(z),'/',num2str(dim(3))]), axis image ij +colormap gray +drawnow + +%%%%%%%%%% CALLBACK FUNCTIONS %%%%%%%%%% + function key_press(~,object) + % keyboard commands + switch object.Key + case 'uparrow' + z = min(z+1,dim(3)); + case 'downarrow' + z = max(z-1,1); + case 'rightarrow' + z = min(z+10,dim(3)); + case 'leftarrow' + z = max(z-10,1); + case 'pagedown' + z = min(z+50,dim(3)); + case 'pageup' + z = max(z-50,1); + end + update_drawing + end + +%%%%%%%%%% HELPING FUNCTIONS %%%%%%%%%% + function update_drawing + set(i,'CData',V(:,:,z)) + title(['slice ',num2str(z),'/',num2str(dim(3))]) + drawnow + end + +end + diff --git a/Functions/functions_visualization/suptitle_modified.m b/Functions/functions_visualization/suptitle_modified.m new file mode 100644 index 0000000000000000000000000000000000000000..3fafe385b8903535b811ec06fa7cd647510c3af7 --- /dev/null +++ b/Functions/functions_visualization/suptitle_modified.m @@ -0,0 +1,109 @@ +function hout=suptitle_modified(str,interpreter) +%SUPTITLE puts a title above all subplots. +% +% SUPTITLE('text') adds text to the top of the figure +% above all subplots (a "super title"). Use this function +% after all subplot commands. +% +% SUPTITLE is a helper function for yeastdemo. + +% Copyright 2003-2014 The MathWorks, Inc. + + +% Warning: If the figure or axis units are non-default, this +% function will temporarily change the units. + +% Parameters used to position the supertitle. + +% Amount of the figure window devoted to subplots +plotregion = .92; + +% Y position of title in normalized coordinates +titleypos = .95; + +% Fontsize for supertitle +fs = get(gcf,'defaultaxesfontsize')+4; + +% Fudge factor to adjust y spacing between subplots +fudge=1; + +haold = gca; +figunits = get(gcf,'units'); + +% Get the (approximate) difference between full height (plot + title +% + xlabel) and bounding rectangle. + +if ~strcmp(figunits,'pixels') + set(gcf,'units','pixels'); + pos = get(gcf,'position'); + set(gcf,'units',figunits); +else + pos = get(gcf,'position'); +end +ff = (fs-4)*1.27*5/pos(4)*fudge; + +% The 5 here reflects about 3 characters of height below +% an axis and 2 above. 1.27 is pixels per point. + +% Determine the bounding rectangle for all the plots + +h = findobj(gcf,'Type','axes'); + +oldUnits = get(h, {'Units'}); +if ~all(strcmp(oldUnits, 'normalized')) + % This code is based on normalized units, so we need to temporarily + % change the axes to normalized units. + set(h, 'Units', 'normalized'); + cleanup = onCleanup(@()resetUnits(h, oldUnits)); +end + +max_y=0; +min_y=1; +oldtitle = []; +numAxes = length(h); +thePositions = zeros(numAxes,4); +for i=1:numAxes + pos=get(h(i),'pos'); + thePositions(i,:) = pos; + if ~strcmp(get(h(i),'Tag'),'suptitle') + if pos(2) < min_y + min_y=pos(2)-ff/5*3; + end + if pos(4)+pos(2) > max_y + max_y=pos(4)+pos(2)+ff/5*2; + end + else + oldtitle = h(i); + end +end + +if max_y > plotregion + scale = (plotregion-min_y)/(max_y-min_y); + for i=1:numAxes + pos = thePositions(i,:); + pos(2) = (pos(2)-min_y)*scale+min_y; + pos(4) = pos(4)*scale-(1-scale)*ff/5*3; + set(h(i),'position',pos); + end +end + +np = get(gcf,'nextplot'); +set(gcf,'nextplot','add'); +if ~isempty(oldtitle) + delete(oldtitle); +end +axes('pos',[0 1 1 1],'visible','off','Tag','suptitle'); +ht=text(.5,titleypos-1,str,'Interpreter',interpreter);set(ht,'horizontalalignment','center','fontsize',fs); +set(gcf,'nextplot',np); +axes(haold); +if nargout + hout=ht; +end +end + +function resetUnits(h, oldUnits) + % Reset units on axes object. Note that one of these objects could have + % been an old supertitle that has since been deleted. + valid = isgraphics(h); + set(h(valid), {'Units'}, oldUnits(valid)); +end \ No newline at end of file diff --git a/LayerDetection.m b/LayerDetection.m index 6db4c491bf13de32d5ec8f0acf9eb26b352e8b9d..81252ca57cce20e8d22b08c552a60c8358323c36 100644 --- a/LayerDetection.m +++ b/LayerDetection.m @@ -1,7 +1,4 @@ %% detecting layers -flag_save = 1; -flag_fig = 0; % if you want to check the results - % create result folder save_folder = 'Results\layer\'; if ~exist(save_folder,'dir') diff --git a/PointSelection.m b/PointSelection.m index 0d60119e889a36cf68d0a753aef487c5b97146e5..7eb0e6fc980b731f18b7e1a4e7846dbc4ea80864 100644 --- a/PointSelection.m +++ b/PointSelection.m @@ -1,189 +1,185 @@ %% move from pipeline to here to select the points -flag_run = 1; save_folder = 'Results\point\'; if ~exist(save_folder,'dir') mkdir(save_folder) end -if flag_run - %% contrast enhancement - ce = [.7 1.5]; - - points = []; - points.contrast_enhansed_range = ce;%contrast enhansed range - %% initail points layer - - %f = msgbox({'Suggestions:';'0 & 45 dg: first & last slice';'90 & 180 dg: first, mid & last or first, 1/3, 2/3 & last'},'Choose Slices','warn'); - fprintf('Suggestions:\n0 & 45 dg: first & last slice\n90 & 180 dg: first, middle & last slice or\n first, 1/3, 2/3 & last slice\n') - show_matvol(Vol) - % move to desire slice and run this line to get the points for that slice - fprintf('Controls\n--------\n up-arrow : Next slice in volume \n down-arrow : Previous slice in volume \n right-arrow : +10 slices \n left-arrow : -10 slices \n PgUp : -50 slices \n PgDown : +50 slices \n') - fprintf('Choose the slices by scrolling in the volume and press Enter \n DO NOT CLOSE THE FIGURE!') - input(''); - - answer = inputdlg('Enter space-separated slice numbers:','Slices',[1 50],{'100 2550'}); - points.slice_no = str2num(answer{1}); - - num_layers = length(points.slice_no); - - %% select points, measure the length and distribute points - for l = 1:2 - flag_layer = layer_name{l}; - fprintf('%s layer:\n',flag_layer) - switch length(points.slice_no) - case 2 - fprintf('Go to slice %d and select points on the %s layer\n',points.slice_no(1),flag_layer) - input(''); - [x1,y1] = click_points; - fprintf('Go to slice %d and select %d points on the %s layer\n',points.slice_no(2),length(x1),flag_layer) - input(''); - [x2,y2] = click_points; - - pts1 = round([x1',y1']); - pts2 = round([x2',y2']); - points.two_slice = cat(3,pts1,pts2); - - if l == 1 %compute the length of the layers. this gives us number of px per layers and we keep it same for inner and outer layer - dx = []; - dy = []; - len0 = []; - for s = 1:2 - dx = points.two_slice(1:end-1,1,s)-points.two_slice(2:end,1,s); - dy = points.two_slice(1:end-1,2,s)-points.two_slice(2:end,2,s); - len0(1,s) = sum(sqrt(dx.^2+dy.^2)); - end - m_l = round(mean(len0)); - end - - p_new1 = distribute_points(pts1,'number',m_l,1); - p_new2 = distribute_points(pts2,'number',m_l,1); - - points.all_slice = slice_points_interpolation(p_new2,p_new1,... - points.slice_no(2),points.slice_no(1),0); +%% initail points layer +points = []; +points.contrast_enhansed_range = ce;%contrast enhansed range + +fprintf('Suggestions:\n0 & 45 dg: first & last slice\n90 & 180 dg: first, middle & last slice or\n first, 1/3, 2/3 & last slice\n') +show_matvol(Vol) + +% move to desire slice and run this line to get the points for that slice +fprintf('Controls\n--------\n up-arrow : Next slice in volume \n down-arrow : Previous slice in volume \n right-arrow : +10 slices \n left-arrow : -10 slices \n PgUp : -50 slices \n PgDown : +50 slices \n') +fprintf('Choose the slices by scrolling in the volume and press Enter \n DO NOT CLOSE THE FIGURE!') +input(''); + +answer = inputdlg('Enter space-separated slice numbers:','Slices',[1 50],{'100 2550'}); +points.slice_no = str2num(answer{1}); + +num_layers = length(points.slice_no); + +%% select points, measure the length and distribute points +for l = 1:2 + flag_layer = layer_name{l}; + fprintf('%s layer:\n',flag_layer) + switch length(points.slice_no) + case 2 + fprintf('Go to slice %d and select points on the %s layer\n',points.slice_no(1),flag_layer) + input(''); + [x1,y1] = click_points; + fprintf('Go to slice %d and select %d points on the %s layer\n',points.slice_no(2),length(x1),flag_layer) + input(''); + [x2,y2] = click_points; + + pts1 = round([x1',y1']); + pts2 = round([x2',y2']); + points.two_slice = cat(3,pts1,pts2); + + if l == 1 %compute the length of the layers. this gives us number of px per layers and we keep it same for inner and outer layer dx = []; dy = []; - len1 = []; - for s = 1:size(points.all_slice,1) - dx = points.all_slice(s,1,1:end-1)-points.all_slice(s,1,2:end); - dy = points.all_slice(s,2,1:end-1)-points.all_slice(s,2,2:end); - len1(1,s) = sum(sqrt(dx.^2+dy.^2)); - end - fprintf('Approximated length ~ %d or %d \n',m_l,round(mean(len1))); - - case 3 - - fprintf('Go to slice %d and select points on the %s layer\n',points.slice_no(1),flag_layer) - input(''); - [x1,y1] = click_points; - fprintf('Go to slice %d and select %d points on the %s layer\n',points.slice_no(2),length(x1),flag_layer) - input(''); - [x2,y2] = click_points; - fprintf('Go to slice %d and select %d points on the %s layer\n',points.slice_no(3),length(x1),flag_layer) - input(''); - [x3,y3] = click_points; - - pts1 = round([x1',y1']); - pts2 = round([x2',y2']); - pts3 = round([x3',y3']); - - points.three_slice = cat(3,pts1,pts2,pts3); - - if l == 1 %compute the length of the layers. this gives us number of px per layers and we keep it same for inner and outer layer - dx = []; - dy = []; - len0 = []; - for s = 1:3 - dx = points.three_slice(1:end-1,1,s)-points.three_slice(2:end,1,s); - dy = points.three_slice(1:end-1,2,s)-points.three_slice(2:end,2,s); - len0(1,s) = sum(sqrt(dx.^2+dy.^2)); - end - m_l = round(mean(len0)); + len0 = []; + for s = 1:2 + dx = points.two_slice(1:end-1,1,s)-points.two_slice(2:end,1,s); + dy = points.two_slice(1:end-1,2,s)-points.two_slice(2:end,2,s); + len0(1,s) = sum(sqrt(dx.^2+dy.^2)); end - - p_new1 = distribute_points(pts1,'number',m_l,1); - p_new2 = distribute_points(pts2,'number',m_l,1); - p_new3 = distribute_points(pts3,'number',m_l,1); - - slice_23 = slice_points_interpolation(p_new3,p_new2,... - points.slice_no(3),points.slice_no(2),0); - slice_12 = slice_points_interpolation(p_new2,p_new1,... - points.slice_no(2)-1,points.slice_no(1),0); - points.all_slice = [slice_12;slice_23]; - + m_l = round(mean(len0)); + end + + p_new1 = distribute_points(pts1,'number',m_l,1); + p_new2 = distribute_points(pts2,'number',m_l,1); + + points.all_slice = slice_points_interpolation(p_new2,p_new1,... + points.slice_no(2),points.slice_no(1),0); + dx = []; + dy = []; + len1 = []; + for s = 1:size(points.all_slice,1) + dx = points.all_slice(s,1,1:end-1)-points.all_slice(s,1,2:end); + dy = points.all_slice(s,2,1:end-1)-points.all_slice(s,2,2:end); + len1(1,s) = sum(sqrt(dx.^2+dy.^2)); + end + fprintf('Approximated length ~ %d or %d \n',m_l,round(mean(len1))); + + case 3 + + fprintf('Go to slice %d and select points on the %s layer\n',points.slice_no(1),flag_layer) + input(''); + [x1,y1] = click_points; + fprintf('Go to slice %d and select %d points on the %s layer\n',points.slice_no(2),length(x1),flag_layer) + input(''); + [x2,y2] = click_points; + fprintf('Go to slice %d and select %d points on the %s layer\n',points.slice_no(3),length(x1),flag_layer) + input(''); + [x3,y3] = click_points; + + pts1 = round([x1',y1']); + pts2 = round([x2',y2']); + pts3 = round([x3',y3']); + + points.three_slice = cat(3,pts1,pts2,pts3); + + if l == 1 %compute the length of the layers. this gives us number of px per layers and we keep it same for inner and outer layer dx = []; dy = []; - len1 = []; - for s = 1:size(points.all_slice,1) - dx = points.all_slice(s,1,1:end-1)-points.all_slice(s,1,2:end); - dy = points.all_slice(s,2,1:end-1)-points.all_slice(s,2,2:end); - len1(1,s) = sum(sqrt(dx.^2+dy.^2)); + len0 = []; + for s = 1:3 + dx = points.three_slice(1:end-1,1,s)-points.three_slice(2:end,1,s); + dy = points.three_slice(1:end-1,2,s)-points.three_slice(2:end,2,s); + len0(1,s) = sum(sqrt(dx.^2+dy.^2)); end - fprintf('Approximated length ~ %d or %d \n',m_l,round(mean(len1))); - - case 4 - - fprintf('Go to slice %d and select points on the %s layer\n',points.slice_no(1),flag_layer) - input(''); - [x1,y1] = click_points; - fprintf('Go to slice %d and select %d points on the %s layer\n',points.slice_no(2),length(x1),flag_layer) - input(''); - [x2,y2] = click_points; - fprintf('Go to slice %d and select %d points on the %s layer\n',points.slice_no(3),length(x1),flag_layer) - input(''); - [x3,y3] = click_points; - fprintf('Go to slice %d and select %d points on the %s layer\n',points.slice_no(4),length(x1),flag_layer) - input(''); - [x4,y4] = click_points; - - pts1 = round([x1',y1']); - pts2 = round([x2',y2']); - pts3 = round([x3',y3']); - pts4 = round([x4',y4']); - - points.four_slice = cat(3,pts1,pts2,pts3,pts4); - - if l == 1 %compute the length of the layers. this gives us number of px per layers and we keep it same for inner and outer layer - dx = []; - dy = []; - len0 = []; - for s = 1:4 - dx = points.four_slice(1:end-1,1,s)-points.four_slice(2:end,1,s); - dy = points.four_slice(1:end-1,2,s)-points.four_slice(2:end,2,s); - len0(1,s) = sum(sqrt(dx.^2+dy.^2)); - end - m_l = round(mean(len0)); - end - - p_new1 = distribute_points(pts1,'number',m_l,1); - p_new2 = distribute_points(pts2,'number',m_l,1); - p_new3 = distribute_points(pts3,'number',m_l,1); - p_new4 = distribute_points(pts4,'number',m_l,1); - - slice_34 = slice_points_interpolation(p_new4,p_new3,... - points.slice_no(4),points.slice_no(3),0); - slice_23 = slice_points_interpolation(p_new3,p_new2,... - points.slice_no(3),points.slice_no(2),0); - slice_12 = slice_points_interpolation(p_new2,p_new1,... - points.slice_no(2)-1,points.slice_no(1),0); - points.all_slice = [slice_12;slice_23;slice_34]; - + m_l = round(mean(len0)); + end + + p_new1 = distribute_points(pts1,'number',m_l,1); + p_new2 = distribute_points(pts2,'number',m_l,1); + p_new3 = distribute_points(pts3,'number',m_l,1); + + slice_23 = slice_points_interpolation(p_new3,p_new2,... + points.slice_no(3),points.slice_no(2),0); + slice_12 = slice_points_interpolation(p_new2,p_new1,... + points.slice_no(2)-1,points.slice_no(1),0); + points.all_slice = [slice_12;slice_23]; + + dx = []; + dy = []; + len1 = []; + for s = 1:size(points.all_slice,1) + dx = points.all_slice(s,1,1:end-1)-points.all_slice(s,1,2:end); + dy = points.all_slice(s,2,1:end-1)-points.all_slice(s,2,2:end); + len1(1,s) = sum(sqrt(dx.^2+dy.^2)); + end + fprintf('Approximated length ~ %d or %d \n',m_l,round(mean(len1))); + + case 4 + + fprintf('Go to slice %d and select points on the %s layer\n',points.slice_no(1),flag_layer) + input(''); + [x1,y1] = click_points; + fprintf('Go to slice %d and select %d points on the %s layer\n',points.slice_no(2),length(x1),flag_layer) + input(''); + [x2,y2] = click_points; + fprintf('Go to slice %d and select %d points on the %s layer\n',points.slice_no(3),length(x1),flag_layer) + input(''); + [x3,y3] = click_points; + fprintf('Go to slice %d and select %d points on the %s layer\n',points.slice_no(4),length(x1),flag_layer) + input(''); + [x4,y4] = click_points; + + pts1 = round([x1',y1']); + pts2 = round([x2',y2']); + pts3 = round([x3',y3']); + pts4 = round([x4',y4']); + + points.four_slice = cat(3,pts1,pts2,pts3,pts4); + + if l == 1 %compute the length of the layers. this gives us number of px per layers and we keep it same for inner and outer layer dx = []; dy = []; - len1 = []; - for s = 1:size(points.all_slice,1) - dx = points.all_slice(s,1,1:end-1)-points.all_slice(s,1,2:end); - dy = points.all_slice(s,2,1:end-1)-points.all_slice(s,2,2:end); - len1(1,s) = sum(sqrt(dx.^2+dy.^2)); + len0 = []; + for s = 1:4 + dx = points.four_slice(1:end-1,1,s)-points.four_slice(2:end,1,s); + dy = points.four_slice(1:end-1,2,s)-points.four_slice(2:end,2,s); + len0(1,s) = sum(sqrt(dx.^2+dy.^2)); end - fprintf('Approximated length ~ %d or %d \n',m_l,round(mean(len1))); - end + m_l = round(mean(len0)); + end + + p_new1 = distribute_points(pts1,'number',m_l,1); + p_new2 = distribute_points(pts2,'number',m_l,1); + p_new3 = distribute_points(pts3,'number',m_l,1); + p_new4 = distribute_points(pts4,'number',m_l,1); + + slice_34 = slice_points_interpolation(p_new4,p_new3,... + points.slice_no(4),points.slice_no(3),0); + slice_23 = slice_points_interpolation(p_new3,p_new2,... + points.slice_no(3),points.slice_no(2),0); + slice_12 = slice_points_interpolation(p_new2,p_new1,... + points.slice_no(2)-1,points.slice_no(1),0); + points.all_slice = [slice_12;slice_23;slice_34]; + + dx = []; + dy = []; + len1 = []; + for s = 1:size(points.all_slice,1) + dx = points.all_slice(s,1,1:end-1)-points.all_slice(s,1,2:end); + dy = points.all_slice(s,2,1:end-1)-points.all_slice(s,2,2:end); + len1(1,s) = sum(sqrt(dx.^2+dy.^2)); + end + fprintf('Approximated length ~ %d or %d \n',m_l,round(mean(len1))); + end - %% save points + %% save points + if flag_save save([save_folder,flag_layer,'_points_',sample,'_',dg],'points') end - close all end +close all %% test the points by visualization % for s = 1:points.slice_no(2)-points.slice_no(1) diff --git a/PrepMeasure.m b/PrepMeasure.m index 1370eef2b7f28d6ebfb441d97621e4083ecfd4ff..dbd7bad1e48ad2c54f8ae030713f2378fc93f6c3 100644 --- a/PrepMeasure.m +++ b/PrepMeasure.m @@ -1,7 +1,5 @@ %% measuring the distance betwee two sruface and length of each surface -addpath(genpath('Functions')) - -flag_save = 1; +addpath(genpath('.\Functions')) save_folder = 'Results\measure\'; if ~exist(save_folder,'dir') diff --git a/PrepRegistration.m b/PrepRegistration.m index 211d329215aea0355f9430e7f95050a058461d17..0ae1eeb3f3572c52eaeafd6c466bfc27b7f8dd8a 100644 --- a/PrepRegistration.m +++ b/PrepRegistration.m @@ -1,7 +1,5 @@ %% summing over 10 pixels along normal direction of the smoothed surface -addpath(genpath('Functions')) - -flag_save = 1; +addpath(genpath('.\Functions')) save_folder = 'Results\registration\'; if ~exist(save_folder,'dir') diff --git a/Registration.m b/Registration.m index 403980ce921c0215822481c06de776cbb98e0cd5..7460ebd479aac485f2c1a5a6fb3e47762cd0c603 100644 --- a/Registration.m +++ b/Registration.m @@ -36,7 +36,9 @@ end Reg_dg45_outer = registerImages(dg45_outer,dg180_outer,30); Reg_dg90_outer = registerImages(dg90_outer,dg180_outer,30); - save([save_folder,'reg_tfrom_outer_',sample],'*_outer') + if flag_save + save([save_folder,'reg_tfrom_outer_',sample],'*_outer') + end %end @@ -86,8 +88,10 @@ end subplot(223) imshowpair(inner_90dg,inner.dg180_inner),title('90 to 180') suptitle_modified(['inner using inner T ',sample],'none') - saveas(gcf,[save_folder,'inner_using_inner_T_',sample,'.fig']) - saveas(gcf,[save_folder,'inner_using_inner_T_',sample,'.png']) + if flag_save + saveas(gcf,[save_folder,'inner_using_inner_T_',sample,'.fig']) + saveas(gcf,[save_folder,'inner_using_inner_T_',sample,'.png']) + end % check all outer agains 180 using inner transform T MOVING = outer.dg0_outer;%imrotate(layers(:,:,1),180); @@ -119,8 +123,10 @@ end subplot(223) imshowpair(outer_90dg,outer.dg180_outer),title('90 to 180') suptitle_modified(['outer using inner T ',sample],'none') - saveas(gcf,[save_folder,'outer_using_inner_T_',sample,'.fig']) - saveas(gcf,[save_folder,'outer_using_inner_T_',sample,'.png']) + if flag_save + saveas(gcf,[save_folder,'outer_using_inner_T_',sample,'.fig']) + saveas(gcf,[save_folder,'outer_using_inner_T_',sample,'.png']) + end %end close all @@ -238,34 +244,36 @@ end subplot(236) imshowpair(outer_90dg(:,:,s),outer.dg180_outer),title('90 to 180') suptitle_modified(sample,'none') - - saveas(gcf,sprintf('%sReg_s%d_%s.png',save_folder,s,sample)) + if flag_save + saveas(gcf,sprintf('%sReg_s%d_%s.png',save_folder,s,sample)) + end + end + if flag_save + disp('Save results ...') + layers = inner_0dg; + Ref = inner_0dg_Ref; + save([save_folder,'Reg_inner_layers_',sample,'_0'],'layers','Ref') + layers = inner_45dg; + Ref = inner_45dg_Ref; + save([save_folder,'Reg_inner_layers_',sample,'_45'],'layers','Ref') + layers = inner_90dg; + Ref = inner_90dg_Ref; + save([save_folder,'Reg_inner_layers_',sample,'_90'],'layers','Ref') + layers = inner_180dg; + save([save_folder,'Reg_inner_layers_',sample,'_180'],'layers') + + layers = outer_0dg; + Ref = outer_0dg_Ref; + save([save_folder,'Reg_outer_layers_',sample,'_0'],'layers','Ref') + layers = outer_45dg; + Ref = outer_45dg_Ref; + save([save_folder,'Reg_outer_layers_',sample,'_45'],'layers','Ref') + layers = outer_90dg; + Ref = outer_90dg_Ref; + save([save_folder,'Reg_outer_layers_',sample,'_90'],'layers','Ref') + layers = outer_180dg; + save([save_folder,'Reg_outer_layers_',sample,'_180'],'layers') end - disp('Save results ...') - layers = inner_0dg; - Ref = inner_0dg_Ref; - save([save_folder,'Reg_inner_layers_',sample,'_0'],'layers','Ref') - layers = inner_45dg; - Ref = inner_45dg_Ref; - save([save_folder,'Reg_inner_layers_',sample,'_45'],'layers','Ref') - layers = inner_90dg; - Ref = inner_90dg_Ref; - save([save_folder,'Reg_inner_layers_',sample,'_90'],'layers','Ref') - layers = inner_180dg; - save([save_folder,'Reg_inner_layers_',sample,'_180'],'layers') - - layers = outer_0dg; - Ref = outer_0dg_Ref; - save([save_folder,'Reg_outer_layers_',sample,'_0'],'layers','Ref') - layers = outer_45dg; - Ref = outer_45dg_Ref; - save([save_folder,'Reg_outer_layers_',sample,'_45'],'layers','Ref') - layers = outer_90dg; - Ref = outer_90dg_Ref; - save([save_folder,'Reg_outer_layers_',sample,'_90'],'layers','Ref') - layers = outer_180dg; - save([save_folder,'Reg_outer_layers_',sample,'_180'],'layers') - %end close all @@ -363,25 +371,26 @@ end clear tmp Reg end - disp('Save results ...') - points = inner_0dg; - save([save_folder,'Reg_inner_points_',sample,'_0'],'points') - points = inner_45dg; - save([save_folder,'Reg_inner_points_',sample,'_45'],'points') - points = inner_90dg; - save([save_folder,'Reg_inner_points_',sample,'_90'],'points') - points = inner_180dg; - save([save_folder,'Reg_inner_points_',sample,'_180'],'points') - - points = outer_0dg; - save([save_folder,'Reg_outer_points_',sample,'_0'],'points') - points = outer_45dg; - save([save_folder,'Reg_outer_points_',sample,'_45'],'points') - points = outer_90dg; - save([save_folder,'Reg_outer_points_',sample,'_90'],'points') - points = outer_180dg; - save([save_folder,'Reg_outer_points_',sample,'_180'],'points') - + if flag_save + disp('Save results ...') + points = inner_0dg; + save([save_folder,'Reg_inner_points_',sample,'_0'],'points') + points = inner_45dg; + save([save_folder,'Reg_inner_points_',sample,'_45'],'points') + points = inner_90dg; + save([save_folder,'Reg_inner_points_',sample,'_90'],'points') + points = inner_180dg; + save([save_folder,'Reg_inner_points_',sample,'_180'],'points') + + points = outer_0dg; + save([save_folder,'Reg_outer_points_',sample,'_0'],'points') + points = outer_45dg; + save([save_folder,'Reg_outer_points_',sample,'_45'],'points') + points = outer_90dg; + save([save_folder,'Reg_outer_points_',sample,'_90'],'points') + points = outer_180dg; + save([save_folder,'Reg_outer_points_',sample,'_180'],'points') + end %end close all diff --git a/Transformation.m b/Transformation.m index 351e9a23474ff6e4c97d60924c74220ec7931364..0779d1c5f018665a510435f2ad04311c1895f2e0 100644 --- a/Transformation.m +++ b/Transformation.m @@ -1,4 +1,4 @@ -addpath(genpath('..\Functions')) +addpath(genpath('.\Functions')) samples = {'out_out_CD','out_out_MD','in_in_CD','in_in_MD'}; layer_name = {'outer','inner'}; @@ -17,9 +17,6 @@ if ~exist(save_folder,'dir') mkdir(save_folder) end -flag_save = 1; -flag_fig = 1; - % if you want to run the measure separately you can use the for loop %for i = 1:4 sample = samples{i}; @@ -119,9 +116,6 @@ if ~exist(save_folder,'dir') mkdir(save_folder) end -flag_save = 1; -flag_fig = 1; - % if you want to run the measure separately you can use the for loop %for i = 1:4 sample = samples{i}; @@ -193,9 +187,6 @@ flag_fig = 1; %% move all the bend to the concave part for inner -flag_save = 1; -flag_fig = 1; - load_folder_layers = 'Results\layer\transfer\0dg_rest\'; save_folder = 'Results\layer\transfer\align_0dg_rest\';