├── 3rdparty └── gco-v3.0 │ ├── GCO_README.TXT │ ├── GCoptimization.cpp │ ├── GCoptimization.h │ ├── LinkedBlockList.cpp │ ├── LinkedBlockList.h │ ├── block.h │ ├── energy.h │ ├── example.cpp │ ├── graph.cpp │ ├── graph.h │ ├── matlab │ ├── GCO_BuildLib.m │ ├── GCO_ComputeEnergy.m │ ├── GCO_Create.m │ ├── GCO_Delete.m │ ├── GCO_ExpandOnAlpha.m │ ├── GCO_Expansion.m │ ├── GCO_GetLabeling.m │ ├── GCO_ListHandles.m │ ├── GCO_LoadLib.m │ ├── GCO_SetDataCost.m │ ├── GCO_SetLabelCost.m │ ├── GCO_SetLabelOrder.m │ ├── GCO_SetLabeling.m │ ├── GCO_SetNeighbors.m │ ├── GCO_SetSmoothCost.m │ ├── GCO_SetVerbosity.m │ ├── GCO_Swap.m │ ├── GCO_UnitTest.m │ ├── README.TXT │ └── gco_matlab.cpp │ └── maxflow.cpp ├── common ├── calNormal.m ├── calPosedMesh.m ├── calShapedMesh.m ├── combineParam.m ├── divideParam.m ├── extract_garments.m ├── garment_boundary.m ├── garment_faces.m ├── render_labels.m └── rodrigues.m ├── mesh_parser ├── cplusplus │ ├── CMakeLists.txt │ ├── cmake │ │ └── FindOpenMesh.cmake │ ├── main.cpp │ ├── mesh_parser.cpp │ └── mesh_parser.h ├── mesh_exporter.m └── mesh_parser.m ├── multi_cloth.m ├── multi_cloth_alignment ├── align_cloth.m ├── align_garment.m ├── align_skin.m ├── build_ring.m ├── energy_cloth.m ├── energy_garment.m ├── energy_skin.m ├── garment_fitting.m ├── garment_ring.m └── initial_smpl.m ├── publish └── publish_results.py ├── python ├── align_garment.py ├── common │ ├── __init__.py │ ├── calculation.py │ └── importer.py └── energy_garments.py ├── sdf_extractor ├── cplusplus │ ├── CMakeLists.txt │ ├── cmake │ │ └── FindOpenMesh.cmake │ └── sdf_extractor.cpp ├── sdf_extractor.m └── sdf_value.txt ├── segmentation.m ├── segmentation ├── calculate_unary.m ├── gco_segment.m ├── get_scan_prior.m ├── get_smpl_prior.m ├── gmm_fitting.m ├── gmm_sample.m ├── label_nearest.m ├── manually_modify.m ├── manually_modify_sub.m ├── matching_pair.m ├── meanfield.m ├── prior_pants.m ├── prior_shirt.m ├── prior_skin.m ├── render_prior.m └── render_texture.m ├── single_mesh.m ├── single_mesh_alignment ├── SingleMeshEnergy1.m ├── SingleMeshEnergy2.m ├── SingleMeshEnergy_GMdist.m ├── SingleMeshEnergy_OneLoss.m ├── estimate_fitting.m ├── estimate_trans.m ├── single_mesh_align.m ├── single_mesh_fitting.m ├── single_mesh_scale.m └── single_mesh_trans.m ├── smpl_model ├── smpl_base_f.obj ├── smpl_base_m.obj ├── smpl_f.mat └── smpl_m.mat ├── summary.md └── transfer └── cplusplus ├── CMakeLists.txt ├── OptionParser.cpp ├── OptionParser.h ├── cmake └── FindOpenMesh.cmake ├── main.cpp ├── transfer.cpp └── transfer.h /3rdparty/gco-v3.0/LinkedBlockList.cpp: -------------------------------------------------------------------------------- 1 | #include "LinkedBlockList.h" 2 | #include 3 | #include 4 | 5 | /*********************************************************************/ 6 | 7 | void LinkedBlockList::addFront(ListType item) { 8 | 9 | if ( m_head_block_size == GCLL_BLOCK_SIZE ) 10 | { 11 | LLBlock *tmp = (LLBlock *) new LLBlock; 12 | if ( !tmp ) {printf("\nOut of memory");exit(1);} 13 | tmp -> m_next = m_head; 14 | m_head = tmp; 15 | m_head_block_size = 0; 16 | } 17 | 18 | m_head ->m_item[m_head_block_size] = item; 19 | m_head_block_size++; 20 | } 21 | 22 | /*********************************************************************/ 23 | 24 | ListType LinkedBlockList::next() 25 | { 26 | ListType toReturn = m_cursor -> m_item[m_cursor_ind]; 27 | 28 | m_cursor_ind++; 29 | 30 | if ( m_cursor == m_head && m_cursor_ind >= m_head_block_size ) 31 | { 32 | m_cursor = m_cursor ->m_next; 33 | m_cursor_ind = 0; 34 | } 35 | else if ( m_cursor_ind == GCLL_BLOCK_SIZE ) 36 | { 37 | m_cursor = m_cursor ->m_next; 38 | m_cursor_ind = 0; 39 | } 40 | return(toReturn); 41 | } 42 | 43 | /*********************************************************************/ 44 | 45 | bool LinkedBlockList::hasNext() 46 | { 47 | if ( m_cursor != 0 ) return (true); 48 | else return(false); 49 | } 50 | 51 | 52 | /*********************************************************************/ 53 | 54 | LinkedBlockList::~LinkedBlockList() 55 | { 56 | LLBlock *tmp; 57 | 58 | while ( m_head != 0 ) 59 | { 60 | tmp = m_head; 61 | m_head = m_head->m_next; 62 | delete tmp; 63 | } 64 | }; 65 | 66 | /*********************************************************************/ 67 | 68 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/LinkedBlockList.h: -------------------------------------------------------------------------------- 1 | /* Singly Linked List of Blocks */ 2 | // This data structure should be used only for the GCoptimization class implementation 3 | // because it lucks some important general functions for general list, like remove_item() 4 | // The head block may be not full 5 | // For regular 2D grids, it's better to set GCLL_BLOCK_SIZE to 2 6 | // For other graphs, it should be set to the average expected number of neighbors 7 | // Data in linked list for the neighborhood system is allocated in blocks of size GCLL_BLOCK_SIZE 8 | 9 | #ifndef __LINKEDBLOCKLIST_H__ 10 | #define __LINKEDBLOCKLIST_H__ 11 | 12 | #define GCLL_BLOCK_SIZE 4 13 | // GCLL_BLOCKSIZE should "fit" into the type BlockType. That is 14 | // if GCLL_BLOCKSIZE is larger than 255 but smaller than largest short integer 15 | // then BlockType should be set to short 16 | typedef char BlockType; 17 | 18 | //The type of data stored in the linked list 19 | typedef void * ListType; 20 | 21 | class LinkedBlockList{ 22 | 23 | public: 24 | void addFront(ListType item); 25 | inline bool isEmpty(){if (m_head == 0) return(true); else return(false);}; 26 | inline LinkedBlockList(){m_head = 0; m_head_block_size = GCLL_BLOCK_SIZE;}; 27 | ~LinkedBlockList(); 28 | 29 | // Next three functins are for the linked list traversal 30 | inline void setCursorFront(){m_cursor = m_head; m_cursor_ind = 0;}; 31 | ListType next(); 32 | bool hasNext(); 33 | 34 | private: 35 | typedef struct LLBlockStruct{ 36 | ListType m_item[GCLL_BLOCK_SIZE]; 37 | struct LLBlockStruct *m_next; 38 | } LLBlock; 39 | 40 | LLBlock *m_head; 41 | // Remembers the number of elements in the head block, since it may not be full 42 | BlockType m_head_block_size; 43 | // For block traversal, points to current element in the current block 44 | BlockType m_cursor_ind; 45 | // For block traversal, points to current block in the linked list 46 | LLBlock *m_cursor; 47 | }; 48 | 49 | #endif 50 | 51 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/block.h: -------------------------------------------------------------------------------- 1 | /* block.h */ 2 | /* 3 | Template classes Block and DBlock 4 | Implement adding and deleting items of the same type in blocks. 5 | 6 | If there there are many items then using Block or DBlock 7 | is more efficient than using 'new' and 'delete' both in terms 8 | of memory and time since 9 | (1) On some systems there is some minimum amount of memory 10 | that 'new' can allocate (e.g., 64), so if items are 11 | small that a lot of memory is wasted. 12 | (2) 'new' and 'delete' are designed for items of varying size. 13 | If all items has the same size, then an algorithm for 14 | adding and deleting can be made more efficient. 15 | (3) All Block and DBlock functions are inline, so there are 16 | no extra function calls. 17 | 18 | Differences between Block and DBlock: 19 | (1) DBlock allows both adding and deleting items, 20 | whereas Block allows only adding items. 21 | (2) Block has an additional operation of scanning 22 | items added so far (in the order in which they were added). 23 | (3) Block allows to allocate several consecutive 24 | items at a time, whereas DBlock can add only a single item. 25 | 26 | Note that no constructors or destructors are called for items. 27 | 28 | Example usage for items of type 'MyType': 29 | 30 | /////////////////////////////////////////////////// 31 | #include "block.h" 32 | #define BLOCK_SIZE 1024 33 | typedef struct { int a, b; } MyType; 34 | MyType *ptr, *array[10000]; 35 | 36 | ... 37 | 38 | Block *block = new Block(BLOCK_SIZE); 39 | 40 | // adding items 41 | for (int i=0; i New(); 44 | ptr -> a = ptr -> b = rand(); 45 | } 46 | 47 | // reading items 48 | for (ptr=block->ScanFirst(); ptr; ptr=block->ScanNext()) 49 | { 50 | printf("%d %d\n", ptr->a, ptr->b); 51 | } 52 | 53 | delete block; 54 | 55 | ... 56 | 57 | DBlock *dblock = new DBlock(BLOCK_SIZE); 58 | 59 | // adding items 60 | for (int i=0; i New(); 63 | } 64 | 65 | // deleting items 66 | for (int i=0; i Delete(array[i]); 69 | } 70 | 71 | // adding items 72 | for (int i=0; i New(); 75 | } 76 | 77 | delete dblock; 78 | 79 | /////////////////////////////////////////////////// 80 | 81 | Note that DBlock deletes items by marking them as 82 | empty (i.e., by adding them to the list of free items), 83 | so that this memory could be used for subsequently 84 | added items. Thus, at each moment the memory allocated 85 | is determined by the maximum number of items allocated 86 | simultaneously at earlier moments. All memory is 87 | deallocated only when the destructor is called. 88 | */ 89 | 90 | #ifndef __BLOCK_H__ 91 | #define __BLOCK_H__ 92 | 93 | #include 94 | 95 | /***********************************************************************/ 96 | /***********************************************************************/ 97 | /***********************************************************************/ 98 | 99 | template class Block 100 | { 101 | public: 102 | /* Constructor. Arguments are the block size and 103 | (optionally) the pointer to the function which 104 | will be called if allocation failed; the message 105 | passed to this function is "Not enough memory!" */ 106 | Block(int size, void (*err_function)(const char *) = NULL) { first = last = NULL; block_size = size; error_function = err_function; } 107 | 108 | /* Destructor. Deallocates all items added so far */ 109 | ~Block() { while (first) { block *next = first -> next; delete first; first = next; } } 110 | 111 | /* Allocates 'num' consecutive items; returns pointer 112 | to the first item. 'num' cannot be greater than the 113 | block size since items must fit in one block */ 114 | Type *New(int num = 1) 115 | { 116 | Type *t; 117 | 118 | if (!last || last->current + num > last->last) 119 | { 120 | if (last && last->next) last = last -> next; 121 | else 122 | { 123 | block *next = (block *) new char [sizeof(block) + (block_size-1)*sizeof(Type)]; 124 | if (!next) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } 125 | if (last) last -> next = next; 126 | else first = next; 127 | last = next; 128 | last -> current = & ( last -> data[0] ); 129 | last -> last = last -> current + block_size; 130 | last -> next = NULL; 131 | } 132 | } 133 | 134 | t = last -> current; 135 | last -> current += num; 136 | return t; 137 | } 138 | 139 | /* Returns the first item (or NULL, if no items were added) */ 140 | Type *ScanFirst() 141 | { 142 | for (scan_current_block=first; scan_current_block; scan_current_block = scan_current_block->next) 143 | { 144 | scan_current_data = & ( scan_current_block -> data[0] ); 145 | if (scan_current_data < scan_current_block -> current) return scan_current_data ++; 146 | } 147 | return NULL; 148 | } 149 | 150 | /* Returns the next item (or NULL, if all items have been read) 151 | Can be called only if previous ScanFirst() or ScanNext() 152 | call returned not NULL. */ 153 | Type *ScanNext() 154 | { 155 | while (scan_current_data >= scan_current_block -> current) 156 | { 157 | scan_current_block = scan_current_block -> next; 158 | if (!scan_current_block) return NULL; 159 | scan_current_data = & ( scan_current_block -> data[0] ); 160 | } 161 | return scan_current_data ++; 162 | } 163 | 164 | /* Marks all elements as empty */ 165 | void Reset() 166 | { 167 | block *b; 168 | if (!first) return; 169 | for (b=first; ; b=b->next) 170 | { 171 | b -> current = & ( b -> data[0] ); 172 | if (b == last) break; 173 | } 174 | last = first; 175 | } 176 | 177 | /***********************************************************************/ 178 | 179 | private: 180 | 181 | typedef struct block_st 182 | { 183 | Type *current, *last; 184 | struct block_st *next; 185 | Type data[1]; 186 | } block; 187 | 188 | int block_size; 189 | block *first; 190 | block *last; 191 | 192 | block *scan_current_block; 193 | Type *scan_current_data; 194 | 195 | void (*error_function)(const char *); 196 | }; 197 | 198 | /***********************************************************************/ 199 | /***********************************************************************/ 200 | /***********************************************************************/ 201 | 202 | template class DBlock 203 | { 204 | public: 205 | /* Constructor. Arguments are the block size and 206 | (optionally) the pointer to the function which 207 | will be called if allocation failed; the message 208 | passed to this function is "Not enough memory!" */ 209 | DBlock(int size, void (*err_function)(const char *) = NULL) { first = NULL; first_free = NULL; block_size = size; error_function = err_function; } 210 | 211 | /* Destructor. Deallocates all items added so far */ 212 | ~DBlock() { while (first) { block *next = first -> next; delete first; first = next; } } 213 | 214 | /* Allocates one item */ 215 | Type *New() 216 | { 217 | block_item *item; 218 | 219 | if (!first_free) 220 | { 221 | block *next = first; 222 | first = (block *) new char [sizeof(block) + (block_size-1)*sizeof(block_item)]; 223 | if (!first) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } 224 | first_free = & (first -> data[0] ); 225 | for (item=first_free; item next_free = item + 1; 227 | item -> next_free = NULL; 228 | first -> next = next; 229 | } 230 | 231 | item = first_free; 232 | first_free = item -> next_free; 233 | return (Type *) item; 234 | } 235 | 236 | /* Deletes an item allocated previously */ 237 | void Delete(Type *t) 238 | { 239 | ((block_item *) t) -> next_free = first_free; 240 | first_free = (block_item *) t; 241 | } 242 | 243 | /***********************************************************************/ 244 | 245 | private: 246 | 247 | typedef union block_item_st 248 | { 249 | Type t; 250 | block_item_st *next_free; 251 | } block_item; 252 | 253 | typedef struct block_st 254 | { 255 | struct block_st *next; 256 | block_item data[1]; 257 | } block; 258 | 259 | int block_size; 260 | block *first; 261 | block_item *first_free; 262 | 263 | void (*error_function)(const char *); 264 | }; 265 | 266 | 267 | #endif 268 | 269 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/graph.cpp: -------------------------------------------------------------------------------- 1 | /* graph.cpp */ 2 | 3 | 4 | #include 5 | #include 6 | #include 7 | #include "graph.h" 8 | 9 | 10 | template 11 | Graph::Graph(int node_num_max, int edge_num_max, void (*err_function)(const char *)) 12 | : node_num(0), 13 | nodeptr_block(NULL), 14 | error_function(err_function) 15 | { 16 | if (node_num_max < 16) node_num_max = 16; 17 | if (edge_num_max < 16) edge_num_max = 16; 18 | 19 | nodes = (node*) malloc(node_num_max*sizeof(node)); 20 | arcs = (arc*) malloc(2*edge_num_max*sizeof(arc)); 21 | if (!nodes || !arcs) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } 22 | 23 | node_last = nodes; 24 | node_max = nodes + node_num_max; 25 | arc_last = arcs; 26 | arc_max = arcs + 2*edge_num_max; 27 | 28 | maxflow_iteration = 0; 29 | flow = 0; 30 | } 31 | 32 | template 33 | Graph::~Graph() 34 | { 35 | if (nodeptr_block) 36 | { 37 | delete nodeptr_block; 38 | nodeptr_block = NULL; 39 | } 40 | free(nodes); 41 | free(arcs); 42 | } 43 | 44 | template 45 | void Graph::reset() 46 | { 47 | node_last = nodes; 48 | arc_last = arcs; 49 | node_num = 0; 50 | 51 | if (nodeptr_block) 52 | { 53 | delete nodeptr_block; 54 | nodeptr_block = NULL; 55 | } 56 | 57 | maxflow_iteration = 0; 58 | flow = 0; 59 | } 60 | 61 | template 62 | void Graph::reallocate_nodes(int num) 63 | { 64 | int node_num_max = (int)(node_max - nodes); 65 | node* nodes_old = nodes; 66 | 67 | node_num_max += node_num_max / 2; 68 | if (node_num_max < node_num + num) node_num_max = node_num + num; 69 | nodes = (node*) realloc(nodes_old, node_num_max*sizeof(node)); 70 | if (!nodes) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } 71 | 72 | node_last = nodes + node_num; 73 | node_max = nodes + node_num_max; 74 | 75 | if (nodes != nodes_old) 76 | { 77 | arc* a; 78 | for (a=arcs; ahead = (node*) ((char*)a->head + (((char*) nodes) - ((char*) nodes_old))); 81 | } 82 | } 83 | } 84 | 85 | template 86 | void Graph::reallocate_arcs() 87 | { 88 | int arc_num_max = (int)(arc_max - arcs); 89 | int arc_num = (int)(arc_last - arcs); 90 | arc* arcs_old = arcs; 91 | 92 | arc_num_max += arc_num_max / 2; if (arc_num_max & 1) arc_num_max ++; 93 | arcs = (arc*) realloc(arcs_old, arc_num_max*sizeof(arc)); 94 | if (!arcs) { if (error_function) (*error_function)("Not enough memory!"); exit(1); } 95 | 96 | arc_last = arcs + arc_num; 97 | arc_max = arcs + arc_num_max; 98 | 99 | if (arcs != arcs_old) 100 | { 101 | node* i; 102 | arc* a; 103 | for (i=nodes; ifirst) i->first = (arc*) ((char*)i->first + (((char*) arcs) - ((char*) arcs_old))); 106 | } 107 | for (a=arcs; anext) a->next = (arc*) ((char*)a->next + (((char*) arcs) - ((char*) arcs_old))); 110 | a->sister = (arc*) ((char*)a->sister + (((char*) arcs) - ((char*) arcs_old))); 111 | } 112 | } 113 | } 114 | 115 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_BuildLib.m: -------------------------------------------------------------------------------- 1 | function GCO_BuildLib(Options) 2 | % GCO_BuildLib Attempt to compile and link the GCO_MATLAB library. 3 | % GCO_BuildLib is used internally by all other GCO_MATLAB commands 4 | % to recompile the wrapper library if it is not yet built. 5 | % 6 | % YOU DO NOT NEED TO EXPLICITLY CALL THIS FUNCTION, unless you want to 7 | % customise the build settings via GCO_BuildLib(Options). 8 | % Default options: 9 | % Options.Debug=0 % optimised, detailed checking disabled 10 | % Options.EnergyType='long long' % int64 energy accumulator C type 11 | % Options.EnergyTermType='int' % int32 energy term C type; if not specified, same as EnergyType 12 | % 13 | % Example: 14 | % % Enable detailed assertions (e.g. than energy does not go up 15 | % % during expansion) and use double-precision float energy terms. 16 | % GCO_BuildLib(struct('Debug',1,'EnergyType','double')); 17 | % 18 | 19 | if (nargin < 1) 20 | Options = struct(); 21 | end 22 | if (~isfield(Options,'Debug')), Options.Debug = 0; end 23 | if (~isfield(Options,'EnergyType')), Options.EnergyType = ''; end 24 | if (~isfield(Options,'EnergyTermType')), Options.EnergyTermType = ''; end 25 | if (~isfield(Options,'EnergyType32')), Options.EnergyType32 = 0; end 26 | if (~isfield(Options,'Force')), Options.Force = 1; end 27 | 28 | MEXFLAGS = ''; 29 | if (strcmp(computer(),'GLNXA64') || strcmp(computer(),'PCWIN64') || strcmp(computer(),'MACI64')) 30 | MEXFLAGS = [MEXFLAGS ' -largeArrayDims -DA64BITS']; 31 | end 32 | if (Options.Debug) 33 | MEXFLAGS = [MEXFLAGS ' -g']; 34 | end 35 | if (Options.EnergyType32) 36 | MEXFLAGS = [MEXFLAGS ' -DGCO_ENERGYTYPE32']; 37 | end 38 | if (Options.EnergyType) 39 | MEXFLAGS = [MEXFLAGS ' -DGCO_ENERGYTYPE=' Options.EnergyType]; 40 | end 41 | if (Options.EnergyTermType) 42 | MEXFLAGS = [MEXFLAGS ' -DGCO_ENERGYTERMTYPE=' Options.EnergyTermType]; 43 | end 44 | if (strcmp(computer(),'PCWIN')) % link with libut for user interruptibility 45 | MEXFLAGS = [MEXFLAGS ' -D_WIN32 "' matlabroot() '\extern\lib\win32\microsoft\libut.lib"' ]; 46 | elseif (strcmp(computer(),'PCWIN64')) 47 | MEXFLAGS = [MEXFLAGS ' -D_WIN64 "' matlabroot() '\extern\lib\win64\microsoft\libut.lib"' ]; 48 | else 49 | MEXFLAGS = [MEXFLAGS ' -lut' ]; 50 | end 51 | 52 | LIB_NAME = 'gco_matlab'; 53 | GCOMATDIR = fileparts(mfilename('fullpath')); 54 | GCODIR = fileparts(GCOMATDIR); 55 | OUTDIR = [ GCOMATDIR filesep 'bin' ]; 56 | [status msg msgid] = mkdir(GCOMATDIR, 'bin'); % Create bin directory 57 | addpath(OUTDIR); % and add it to search path 58 | if (~Options.Force && exist('gco_matlab')==3) 59 | return; 60 | end 61 | clear gco_matlab; 62 | 63 | mexcmd = ['mex ' MEXFLAGS ' -outdir ''' OUTDIR ''' -output ' LIB_NAME ' ' ]; 64 | 65 | % Append all source file names to the MEX command string 66 | SRCCPP = { 67 | [GCOMATDIR filesep 'gco_matlab.cpp'], 68 | [GCODIR filesep 'GCoptimization.cpp'], 69 | [GCODIR filesep 'graph.cpp'], 70 | [GCODIR filesep 'maxflow.cpp'], 71 | [GCODIR filesep 'LinkedBlockList.cpp'] 72 | }; 73 | for f=1:length(SRCCPP) 74 | mexcmd = [mexcmd ' ''' SRCCPP{f} ''' ']; 75 | end 76 | 77 | eval(mexcmd); % compile and link in one step 78 | 79 | end 80 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_ComputeEnergy.m: -------------------------------------------------------------------------------- 1 | function [Energy D S L] = GCO_ComputeEnergy(Handle) 2 | % GCO_ComputeEnergy Run alpha-expansion algorithm. 3 | % E = GCO_ComputeEnergy(Handle) returns energy of current labeling. 4 | % [E D S L] = GCO_ComputeEnergy(Handle) also provides a breakdown of 5 | % the energy into Data, Smooth, and Label costs. 6 | 7 | GCO_LoadLib(); 8 | [Energy D S L] = gco_matlab('gco_computeenergy',Handle); 9 | end 10 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_Create.m: -------------------------------------------------------------------------------- 1 | function Handle = GCO_Create(NumSites,NumLabels) 2 | % GCO_Create Create a GCoptimization object. 3 | % Handle = GCO_Create(NumSites,NumLabels) creates a new GCoptimization 4 | % object and returns a 'handle' to uniquely identify it. 5 | % Call GCO_Delete(Handle) to delete the object and free its memory. 6 | % Call GCO_Delete(GCO_ListHandles) to delete all GCO objects. 7 | 8 | GCO_LoadLib(); 9 | if (nargin < 2), error('Expected 2 arguments'); end 10 | Handle = gco_matlab('gco_create_general',int32(NumSites),int32(NumLabels)); 11 | end 12 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_Delete.m: -------------------------------------------------------------------------------- 1 | function GCO_Delete(Handle) 2 | % GCO_Delete Delete a GCoptimization object. 3 | % GCO_Delete(Handle) deletes the object corresponding to Handle 4 | % and frees its memory. 5 | 6 | gco_matlab('gco_delete',int32(Handle)); 7 | end 8 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_ExpandOnAlpha.m: -------------------------------------------------------------------------------- 1 | function GCO_ExpandOnAlpha(Handle,Alpha) 2 | % GCO_ExpandOnAlpha Perform a single alpha-expansion step. 3 | % GCO_Expansion(Handle,Alpha) takes the current labeling and performs 4 | % a single expansion step on label Alpha. 5 | 6 | GCO_LoadLib(); 7 | gco_matlab('gco_alphaexpansion',Handle,int32(Alpha)); 8 | end 9 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_Expansion.m: -------------------------------------------------------------------------------- 1 | function Energy = GCO_Expansion(Handle,MaxIter) 2 | % GCO_Expansion Run alpha-expansion algorithm. 3 | % GCO_Expansion(Handle) minimizes the current energy via 4 | % alpha-expansion until convergence. 5 | % GCO_Expansion(Handle,MaxIter) runs at most MaxIter expansion 6 | % Returns the energy of the computed labeling. 7 | % The labeling itself can be retrieved via GCO_GetLabeling. 8 | % The order of expansion can be influenced by GCO_SetLabelOrder. 9 | % If GCO_SetNeighbors is not called (i.e. no smoothness terms), then 10 | % Expansion will internally use a greedy algorithm (no graph cuts). 11 | % 12 | % IMPORTANT: the first version uses "adaptive cycles" (changed labels) 13 | % until convergence whereas the second applies up to MaxIter 14 | % "standard cycles" (all labels). Each strategy is faster/slower for 15 | % different applications, so see what works fastest for yours. 16 | % 17 | 18 | GCO_LoadLib(); 19 | if (nargin < 1), error('Expansion requires handle to GCO instance'); end 20 | if (nargin < 2), MaxIter = -1; end 21 | Energy = gco_matlab('gco_expansion',Handle,int32(MaxIter)); 22 | end 23 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_GetLabeling.m: -------------------------------------------------------------------------------- 1 | function Labeling = GCO_GetLabeling(Handle,varargin) 2 | % GCO_GetLabeling Retrieve the current labeling 3 | % GCO_GetLabeling(Handle) returns a column vector of all labels. 4 | % GCO_GetLabeling(Handle,i) returns the label of site i. 5 | % GCO_GetLabeling(Handle,i,count) returns labels i..i+count-1 6 | 7 | GCO_LoadLib(); 8 | Start = int32(1); 9 | Count = gco_matlab('gco_getnumsites',Handle); 10 | if (length(varargin) > 2) 11 | error('Too many input arguments.'); 12 | end 13 | if (length(varargin) >= 1), Start = int32(varargin{1}); Count = int32(1); end 14 | if (length(varargin) == 2), Count = int32(varargin{2}); end 15 | Labeling = gco_matlab('gco_getlabeling',Handle,Start,Count); 16 | end 17 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_ListHandles.m: -------------------------------------------------------------------------------- 1 | function Handles = GCO_ListHandles() 2 | % GCO_ListHandles Retrieve handles to all current GCO instances 3 | % Useful for cleaning up GCO instances that are using memory, 4 | % particularly when a script was interrupted. 5 | % Example: 6 | % GCO_Delete(GCO_ListHandles); % delete all GCO instances 7 | 8 | GCO_LoadLib(); 9 | Handles = gco_matlab('gco_listhandles'); 10 | end 11 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_LoadLib.m: -------------------------------------------------------------------------------- 1 | function GCO_LoadLib() 2 | % GCO_LoadLib Attempt to load the GCO_MATLAB library. 3 | % GCO_LoadLib is used internally by all other GCO_MATLAB commands 4 | % to compile (if necessary), load, and bind the wrapper library. 5 | 6 | if (isempty(getenv('GCO_MATLAB'))) 7 | GCO_BuildLib(struct('Force',false)); 8 | if (exist('gco_matlab') ~= 3) 9 | error('Failed to load gco_matlab library'); 10 | end 11 | warning on GCO:int32; 12 | setenv('GCO_MATLAB','LOADED'); % environment variables 10x faster than 'exists' 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_SetDataCost.m: -------------------------------------------------------------------------------- 1 | function GCO_SetDataCost(Handle,DataCost,Label) 2 | % GCO_SetDataCost Set the data cost of individual sites. 3 | % GCO_SetDataCost(Handle,DataCost) accepts a NumLabels-by-NumSites 4 | % int32 matrix where DataCost(k,i) is the cost of assigning 5 | % label k to site i. Unfortunately, in MATLAB 2014a the mxCreateReference, 6 | % function was removed, so the MEX extension (C++ code) now has to make 7 | % an internal copy of the DataCost array :( 8 | % 9 | % GCO_SetDataCost(Handle,DataCost,Label) accepts a 2-by-N int32 matrix 10 | % of (site,cost) pairs, i.e. DataCost(2,i) is the cost of assigning 11 | % Label to site DataCost(1,i). The site ids must be sorted in increasing 12 | % order. All ommitted site ids are assumed to be infeasible for Label. 13 | % This 'sparse' version of SetDataCost allows Expansion to run much 14 | % faster when labels are only feasible for a small subset of sites. 15 | % It is possible to assign infeasible labelings via GCO_SetLabeling, 16 | % but GCO_ComputeEnergy will add huge constants to represent each 17 | % infeasible assignment. 18 | % 19 | % SetDataCost can be called repeatedly, even after Expansion. 20 | % 21 | 22 | GCO_LoadLib(); 23 | if (nargin < 2), error('Expected at least 2 arguments'); end 24 | if (~isnumeric(DataCost)), error('DataCost must be numeric'); end 25 | if (~isreal(DataCost)), error('DataCost cannot be complex'); end 26 | NumLabels = gco_matlab('gco_getnumlabels',Handle); 27 | NumSites = gco_matlab('gco_getnumsites', Handle); 28 | EnergyTermClass = gco_matlab('gco_get_energyterm_class'); 29 | DataCostClass = class(DataCost); 30 | if (nargin == 2) 31 | Label = 0; % no specific label 32 | if (size(DataCost) ~= [ NumLabels NumSites ]) 33 | error('DataCost size must be [ NumLabels NumSites ]'); 34 | end 35 | else 36 | if (Label < 1 || Label > NumLabels) 37 | error('Label must be in range 1..NumLabels'); 38 | end 39 | if (size(DataCost,1) ~= 2) 40 | error('Sparse DataCost must contain two rows'); 41 | end 42 | end 43 | if (~strcmp(DataCostClass,EnergyTermClass)) 44 | OldDataCost = DataCost; 45 | DataCost = cast(OldDataCost,EnergyTermClass); 46 | if (NumSites*NumLabels > 200 || any(any(cast(DataCost, DataCostClass) ~= OldDataCost))) 47 | warning('GCO:type',['DataCost converted to ' EnergyTermClass]); 48 | end 49 | end 50 | gco_matlab('gco_setdatacost',Handle,DataCost,int32(Label)); 51 | end 52 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_SetLabelCost.m: -------------------------------------------------------------------------------- 1 | function GCO_SetLabelCost(Handle,LabelCost,LabelSubset) 2 | % GCO_SetLabelCost Set costs associated with using labels. 3 | % GCO_SetLabelCost(Handle,LabelCost) with scalar LabelCost gives the 4 | % same cost to all labels. 5 | % GCO_SetLabelCost(Handle,LabelCost) with 1xNumLabels LabelCost 6 | % associates cost LabelCost(k) to label k. 7 | % GCO_SetLabelCost(Handle,LabelCost,LabelSubset) sets the cost for using 8 | % at least one label mentioned in LabelSubset (i.e. LabelSubset is a 9 | % vector containing label indices). The cost is paid once. 10 | % SetLabelCost can be called before or after Expansion. 11 | 12 | GCO_LoadLib(); 13 | if (nargin < 2) 14 | error('Expected at least 2 arguments'); 15 | end 16 | NumLabels = gco_matlab('gco_getnumlabels',Handle); 17 | if (length(LabelCost) ~= 1 && length(LabelCost) ~= NumLabels) 18 | error('LabelCost must be scalar or of length NumLabels'); 19 | end 20 | EnergyTermClass = gco_matlab('gco_get_energyterm_class'); 21 | LabelCostClass = class(LabelCost); 22 | if (~strcmp(LabelCostClass,EnergyTermClass)) 23 | OldLabelCost = LabelCost; 24 | LabelCost = cast(OldLabelCost,EnergyTermClass); 25 | if (length(LabelCost) > 50 || any(any(cast(LabelCost, LabelCostClass) ~= OldLabelCost))) 26 | warning('GCO:type',['LabelCost converted to ' EnergyTermClass]); 27 | end 28 | end 29 | if (nargin < 3) 30 | gco_matlab('gco_setlabelcost',Handle,LabelCost); 31 | else 32 | if (length(LabelCost) ~= 1), error('LabelCost must be scalar'); end 33 | if (any(LabelSubset < 1) || any(LabelSubset > NumLabels)) 34 | error('LabelSubset must contain indices from 1..NumLabels'); 35 | end 36 | if (~isa(LabelSubset,'int32')) 37 | if (any(any(floor(LabelSubset) ~= LabelSubset))) 38 | error('LabelSubset must contain integers from 1..NumLabels'); 39 | end 40 | LabelSubset = int32(LabelSubset); 41 | end 42 | gco_matlab('gco_setlabelcost',Handle,LabelCost,LabelSubset); 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_SetLabelOrder.m: -------------------------------------------------------------------------------- 1 | function GCO_SetLabelOrder(Handle,Order) 2 | % GCO_SetLabelOrder Set label order for Expansion/Swap moves. 3 | % GCO_SetLabelOrder(Handle,Order) tells Expansion/Swap to select labels 4 | % in a specific order when Order contains integers from 1..NumLabels. 5 | % By default, Expansion/Swap use a consistent, prescribed order of labels 6 | % in until convergence. 7 | % Example: 8 | % GCO_SetLabelOrder(Handle,5:10); % only operate on labels 5..10 9 | % GCO_SetLabelOrder(Handle,randperm(NumLabels)); % random label order 10 | % 11 | 12 | GCO_LoadLib(); 13 | gco_matlab('gco_setlabelorder',Handle,int32(Order)); 14 | end 15 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_SetLabeling.m: -------------------------------------------------------------------------------- 1 | function GCO_SetLabeling(Handle,Labeling) 2 | % GCO_SetLabeling Sets the current labeling 3 | % GCO_SetLabeling(Handle,Labeling) sets the entire labeling. 4 | 5 | GCO_LoadLib(); 6 | if (isnumeric(Labeling)) 7 | NumSites = gco_matlab('gco_getnumsites',Handle); 8 | NumLabels = gco_matlab('gco_getnumlabels',Handle); 9 | if (length(Labeling) ~= NumSites) 10 | error('Labeling must be of length NumSites'); 11 | end 12 | if (~isa(Labeling,'int32')) 13 | if (any(floor(Labeling) ~= Labeling)) 14 | error('Labeling was not integer valued'); 15 | end 16 | Labeling = int32(Labeling); 17 | end 18 | if (min(Labeling) < 1 || max(Labeling) > NumLabels) 19 | error('Label must be in range 1..NumLabels'); 20 | end 21 | gco_matlab('gco_setlabeling',Handle,Labeling); 22 | end 23 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_SetNeighbors.m: -------------------------------------------------------------------------------- 1 | function GCO_SetNeighbors(Handle,Weights) 2 | % GCO_SetNeighbors Set (weighted) pairwise connectivity of all sites. 3 | % GCO_SetNeighbors(Handle,Weights) determines which sites are neighbors 4 | % and thereby have a SmoothCost associated with them. Weights is a 5 | % sparse NumSites-by-NumSites matrix, where Weights(i,j) > 0 indicates 6 | % that sites i and j are neighbors. If Weights is a 0-1 matrix, smooth 7 | % costs are spatially invariant. See SetSmoothCost for more. 8 | % 9 | % SetNeighbors cannot be called after Expansion. 10 | % Note: only the upper-triangular area of Weights is consulted 11 | % because the connectivity is undirected. 12 | 13 | GCO_LoadLib(); 14 | NumSites = gco_matlab('gco_getnumsites',Handle); 15 | if (size(Weights) ~= [ NumSites NumSites ]) 16 | error('Neighbors must be of size [ NumSites NumSites ]'); 17 | end 18 | if (~issparse(Weights)) 19 | if (NumSites > 100) 20 | warning('Sparsifying the Neighbors matrix (performance warning)'); 21 | end 22 | if (~isa(Weights,'double')) 23 | error('Neighbors matrix must be of type double, but with integral values'); 24 | end 25 | Weights = sparse(Weights); 26 | end 27 | gco_matlab('gco_setneighbors',Handle,Weights); 28 | end 29 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_SetSmoothCost.m: -------------------------------------------------------------------------------- 1 | function GCO_SetSmoothCost(Handle,SmoothCost) 2 | % GCO_SetSmoothCost Set the smooth cost of neighboring sites. 3 | % GCO_SetSmoothCost(Handle,SmoothCost) with a NumLabels-by-NumLabels 4 | % integer matrix makes SmoothCost(k,l) the unweighted cost 5 | % of assigning labels k and l to any neighboring sites. 6 | % For particular neighboring sites i,j the final, weighted cost 7 | % is actually Weights(i,j)*SmoothCost(k,l). The spatially-varying 8 | % weights are determined by GCO_SetNeighbors. 9 | % 10 | % If SetSmoothCost is never called, Potts model is used by default. 11 | % i.e. SmoothCost(k,l) = { 0 if k==l, 1 otherwise } 12 | % 13 | % SetSmoothCost can be called repeatedly, even after Expansion. 14 | 15 | GCO_LoadLib(); 16 | if (nargin < 2) 17 | error('Expected 2 arguments'); 18 | end 19 | if (~isnumeric(SmoothCost)) 20 | error('SmoothCost must be numeric'); 21 | end 22 | NumLabels = gco_matlab('gco_getnumlabels',Handle); 23 | if (size(SmoothCost) ~= [ NumLabels NumLabels ]) 24 | error('SmoothCost size must be [ NumLabels NumLabels ]'); 25 | end 26 | EnergyTermClass = gco_matlab('gco_get_energyterm_class'); 27 | SmoothCostClass = class(SmoothCost); 28 | if (~strcmp(SmoothCostClass,EnergyTermClass)) 29 | OldSmoothCost = SmoothCost; 30 | SmoothCost = cast(OldSmoothCost,EnergyTermClass); 31 | if (NumLabels > 50 || any(any(cast(SmoothCost, SmoothCostClass) ~= OldSmoothCost))) 32 | warning('GCO:type',['SmoothCost converted to ' EnergyTermClass]); 33 | end 34 | end 35 | if (any(SmoothCost ~= SmoothCost')) 36 | error('SmoothCost must be symmetric'); 37 | end 38 | gco_matlab('gco_setsmoothcost',Handle,SmoothCost); 39 | end 40 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_SetVerbosity.m: -------------------------------------------------------------------------------- 1 | function GCO_SetVerbosity(Handle,Level) 2 | % GCO_SetVerbosity Print status messages during Expansion/Swap. 3 | % Level 0 prints no output (full speed). 4 | % Level 1 prints cycle-level status messages. 5 | % Level 2 prints expansion/swap-level status messages. 6 | % The current energy is printed as 7 | % E=Total (E=DataCost+SmoothCost+LabelCost) 8 | % At level 2, the size of each binary graph cut problem is also 9 | % printed (# vars). 10 | % 11 | % Note that printing may have an effect on overall run time (tic/toc) 12 | % though the internal computation times are printed in milliseconds 13 | % and exclude the time to print. 14 | % 15 | % Example: 16 | % >> GCO_SetVerbosity(Handle,2); % Level 2 output 17 | % >> GCO_Expansion(Handle); 18 | % gco>> starting alpha-expansion w/ adaptive cycles 19 | % gco>> initial energy: E=18 (E=17+0+1) 20 | % gco>> after expansion(3): E=17 (E=14+1+2); 4 vars; (1 of 8); 0.003 ms 21 | % gco>> after expansion(7): E=15 (E=11+1+3); 2 vars; (2 of 8); 0.002 ms 22 | % ... 23 | % gco>> after cycle 1: E=12 (E=6+2+4); 8 expansions(s); 24 | 25 | GCO_LoadLib(); 26 | gco_matlab('gco_setverbosity',Handle,int32(Level)); 27 | end 28 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/GCO_Swap.m: -------------------------------------------------------------------------------- 1 | function Energy = GCO_Swap(Handle,MaxIter) 2 | % GCO_Swap Run alpha-beta-swap algorithm. 3 | % GCO_Swap(Handle) runs alpha-beta-swap until convergence. 4 | % GCO_Swap(Handle,MaxIter) runs at most MaxIter swap cycles. 5 | % Returns the energy of the computed labeling. 6 | % The labeling itself can be retrieved via GCO_GetLabeling. 7 | % The order of expansion can be influenced by GCO_SetLabelOrder. 8 | % 9 | % Note that neither label costs nor sparse data costs are currently 10 | % implement for alpha-beta-swap. 11 | 12 | GCO_LoadLib(); 13 | if (nargin < 1), error('Swap requires handle to GCO instance'); end 14 | if (nargin < 2), MaxIter = 1000000; end 15 | Energy = gco_matlab('gco_swap',Handle,int32(MaxIter)); 16 | end 17 | -------------------------------------------------------------------------------- /3rdparty/gco-v3.0/matlab/README.TXT: -------------------------------------------------------------------------------- 1 | /************************************************************************************** 2 | GCO_MATLAB - a Matlab wrapper for Olga Veksler's C++ graph-cut optimization library 3 | 4 | GCO_MATLAB Author(s): 5 | Andrew Delong 6 | Anton Osokin 7 | 8 | We're especially grateful to Lena Gorelick for helpful suggestions and for 9 | tracking down so many bugs! 10 | 11 | GCoptimization Author(s): 12 | Olga Veksler 13 | 14 | Description: 15 | This download provides a Matlab wrapper for the latest version of 'GCoptimization', 16 | Olga Veksler's multi-label optimization library written in C++. 17 | 18 | Note: A wrapper for an earlier version GCoptimization was authored by Shai Bagon and 19 | is available at http://www.wisdom.weizmann.ac.il/~bagon/matlab.html 20 | 21 | Revision History: 22 | Oct 14, 2014; - Added GCO_ENERGYTERM and GCO_ENERGYTERMTYPE macros to make 23 | float/double terms easier. Please note that float/double energy terms 24 | can cause expansion/swap steps to report a very small increase in energy 25 | due to accumulated rounding error inside the maxflow library. 26 | If Inf or NaN values appear in the energy terms, behaviour is undefined. 27 | May 18, 2014; - Support Matlab R2014a by removing use of mxCreateReference 28 | Jan 15, 2014; - Compiles with gcc 4.6+ even without -fpermissive 29 | Apr 12, 2011; - Fixed bug when sparse data costs had a dense bucket (thanks Joseph Tighe!) 30 | Nov 25, 2010; - Detect MACI64 correctly (thanks Francis Lauzon) 31 | Aug 31, 2010; - Compiles with gcc 4.4.1 on opensuse 11.2 (thanks Wei Liu) 32 | Aug 7, 2010; - Fixed bug when data costs are computed in a callback (thanks Evan Herbst!) 33 | - Fixed bug where setAllNeighbours didn't apply neighbourhood (Evan Herbst again) 34 | Jul 22, 2010; - Compiles with gcc 4.4.1 (thanks Julius Ziegler for the patch!) 35 | Jul 8, 2010; - Fixed crash in greedy code path when all labels get added (thanks Yangyan Li!) 36 | Apr 25, 2010; - Faster code path for sparse data costs; fixed related bug in higher-order labels 37 | Apr 21, 2010; - Added basic "verbose" mode (print cycle, energy, timings etc) 38 | - Expansion cycles now focus on labels for which the energy decreased (faster) 39 | Apr 19, 2010; - Added sparse datacost support 40 | - Allow GCO_SetLabelOrder to specify exact label order 41 | Apr 13, 2010; - Potts model is now the default if SetNeighbors is called without SetSmoothCost 42 | - Fixed bug in higher-order label costs 43 | - Expansion is now interruptable from MATLAB; temporary memory is freed 44 | - Added GCO_ListHandles and allow GCO_Delete to accept multiple handles 45 | - Better error message if a bad handle is passed to GCO_* 46 | Nov 17, 2009; - Fixed integer overflow in label-cost construction 47 | - Fixed bug where greedy algorithm would sometimes skip a label 48 | Nov 6, 2009; - Fixed bug in re-setting label costs after Expansion 49 | - Fixed bug in GCO_LoadLib 50 | Oct 27, 2009; - Removed support for arbitrary smoothcost (too slow, hard to maintain) 51 | - Added support for re-setting data, smooth, and label costs after Expansion 52 | - Added support for label subset costs 53 | - Changed build process to directly use MEX command 54 | - Added integer overflow checks into GCoptimization 55 | - GCoptimization now uses maxflow-3.0 library 56 | Sep 12, 2009; - Added support for arbitrary smoothcost from Matlab via a function_handle 57 | - Added GCO_UnitTest 58 | - Build script now handles spaces in paths properly 59 | Aug 23, 2009; - First version for internal testing 60 | 61 | ***************************************************************************************/ 62 | 63 | 0. System Requirements 64 | 65 | - Matlab 7.4.0 (R2007a) or above for 32-bit. 66 | Matlab 7.6.0 (R2008) or above for 64-bit. 67 | 68 | - Mex must be pre-configured to use a C++ compiler. (Run "mex -setup" if you have 69 | not already.) The C++ code requires at least Visual C++ 2005 (VC8). 70 | 71 | 72 | ---------------------------------------------------------------------------------------- 73 | 1. Installation 74 | 75 | - The package should contain the following files: 76 | 77 | GCO_MATLAB files: 78 | gco\matlab\GCO_*.m ; the Matlab commands that you can run 79 | gco\matlab\gco_matlab.cpp ; the library that links Matlab to GCoptimization 80 | 81 | GCoptimization files: 82 | gco\*.{h,cpp} ; the GCoptimization C++ library 83 | 84 | - Start Matlab, and make gco\matlab your working directory or add it to your path. 85 | 86 | - To test your installation of GCO_MATLAB, run the GCO_UnitTest command. 87 | You should hopefully see output like below. 88 | >> GCO_UnitTest 89 | BuildLib PASSED 90 | LoadLib PASSED 91 | Create/Delete PASSED 92 | ... 93 | >> 94 | 95 | - 96 | 97 | ---------------------------------------------------------------------------------------- 98 | 2. Getting Started -- A basic example, and important usage notes 99 | 100 | Once GCO_UnitTest passes, you should be able run the example sequence of commands below. 101 | 102 | >> h = GCO_Create(4,3); % Create new object with NumSites=4, NumLabels=3 103 | >> GCO_SetDataCost(h,[ 104 | 0 9 2 0; % Sites 1,4 prefer label 1 105 | 3 0 3 3; % Site 2 prefers label 2 (strongly) 106 | 5 9 0 5; % Site 3 prefers label 3 107 | ]); 108 | >> GCO_SetSmoothCost(h,[ 109 | 0 1 2; % 110 | 1 0 1; % Linear (Total Variation) pairwise cost 111 | 2 1 0; % 112 | ]); 113 | >> GCO_SetNeighbors(h,[ 114 | 0 1 0 0; % Sites 1 and 2 connected with weight 1 115 | 0 0 1 0; % Sites 2 and 3 connected with weight 1 116 | 0 0 0 2; % Sites 3 and 4 connected with weight 2 117 | 0 0 0 0; 118 | ]); 119 | >> GCO_Expansion(h); % Compute optimal labeling via alpha-expansion 120 | >> GCO_GetLabeling(h) 121 | ans = 122 | 1 % Optimal labeling is (1,2,1,1) 123 | 2 124 | 1 125 | 1 126 | >> [E D S] = GCO_ComputeEnergy(h) % Energy = Data Energy + Smooth Energy 127 | E = 128 | 4 129 | D = 130 | 2 131 | S = 132 | 2 133 | >> GCO_Delete(h); % Delete the GCoptimization object when finished 134 | 135 | 136 | *** Before using the MATLAB wrapper, please note the following: *** 137 | 138 | - Sites and labels are identified with 1-based indices (i.e. 1..N and *not* 0..N-1) 139 | 140 | - By default, all numeric costs should be int32, not single or double! 141 | To use single/double energy terms with the library, please type "help GCO_BuildLib" 142 | at the MATLAB command prompt. 143 | You'll receive a conversion warning if you pass in a large matrix of the wrong type. 144 | The only function that accepts double is GCO_SetNeighbors, because it needs a sparse matrix 145 | and sparse matrices only support double in MATLAB. 146 | ** The weights themselves must still be integer valued!! ** (1.0, 17.0, 42.0 etc) 147 | 148 | ---------------------------------------------------------------------------------------- 149 | 3. GCO_MATLAB functions 150 | 151 | Run 'help' in MATLAB to see the documentation of each function, e.g. 152 | >> help GCO_SetSmoothCost 153 | 154 | Most of the GCO_MATLAB functions are one-to-one with the C++ methods in the 155 | GCoptimization library. 156 | For more detailed documentation, please refer to the C++ library itself. 157 | Relevant files are: 158 | GCO_README.TXT 159 | example.cpp 160 | GCoptimization.h 161 | -------------------------------------------------------------------------------- /common/calNormal.m: -------------------------------------------------------------------------------- 1 | function [normals] = calNormal(faces, vertices) 2 | 3 | e1 = vertices(faces(:, 2), :) - vertices(faces(:, 1), :); 4 | e2 = vertices(faces(:, 3), :) - vertices(faces(:, 2), :); 5 | 6 | normal = cross(e1.', e2.').'; 7 | 8 | for i = 1:size(normal, 1) 9 | normal(i, :) = normal(i, :) / norm(normal(i, :)); 10 | end 11 | 12 | normal_pts = zeros(size(vertices)); 13 | normal_count = zeros(size(vertices, 1), 1); 14 | 15 | for i = 1:size(normal, 1) 16 | for j = 1:3 17 | id = faces(i, j); 18 | normal_pts(id, :) = normal_pts(id, :) + normal(i, :); 19 | normal_count(id) = normal_count(id) + 1; 20 | end 21 | end 22 | normal_pts = normal_pts ./ repmat(normal_count, 1, 3); 23 | normals = normal_pts; 24 | 25 | end 26 | -------------------------------------------------------------------------------- /common/calPosedMesh.m: -------------------------------------------------------------------------------- 1 | function [v_posed, j_posed] = calPosedMesh(model, pose, v_shaped, j_shaped, enablePoseBlendShape) 2 | 3 | if nargin < 5 4 | enablePoseBlendShape = 1; 5 | end 6 | 7 | joints_num = size(model.J, 1); 8 | if length(pose) ~= joints_num*3 9 | fprintf('pose size error!\n'); 10 | return ; 11 | end 12 | 13 | pose = reshape(pose, 3, []); 14 | if enablePoseBlendShape 15 | pose_rodrigues = zeros((joints_num-1)*9, 1); 16 | for i = 2:joints_num 17 | tmp_R = rodrigues(pose(:, i)) - eye(3); 18 | pose_rodrigues((i-2)*9+1:(i-1)*9) = reshape(tmp_R', 1, []); 19 | end 20 | pose_coeffs = model.posedirs; % n * 3 * 207 21 | pose_coeffs = reshape(pose_coeffs, [], (joints_num-1)*9); 22 | v_offset = pose_coeffs * pose_rodrigues; 23 | v_offset = reshape(v_offset, [], 3); 24 | v_posed = v_shaped + v_offset; 25 | else 26 | v_posed = v_shaped; 27 | end 28 | 29 | id_to_col = model.kintree_table(2, :); % length 24 30 | parent = id_to_col(model.kintree_table(1, 2:end) + 1) + 1; % length 23 31 | 32 | A = zeros(4, 4, joints_num); 33 | A(:, :, 1) = with_zeros(rodrigues(pose(:, 1)), j_shaped(1, :)'); 34 | 35 | for i = 2:joints_num 36 | parent_idx = parent(i-1); 37 | A(:, :, i) = A(:, :, parent_idx) * ... 38 | with_zeros(rodrigues(pose(:, i)), j_shaped(i, :)' - j_shaped(parent_idx, :)'); 39 | end 40 | 41 | A_global = A; 42 | for i = 1:joints_num 43 | A(:, :, i) = A(:, :, i) - pack(A(:, :, i) * [j_shaped(i, :), 0]'); 44 | end 45 | 46 | j_posed = zeros(joints_num, 3); 47 | for i = 1:joints_num 48 | j_posed(i, :) = A_global(1:3, 4, i)'; 49 | end 50 | 51 | A_colwise = reshape(A, [], joints_num); 52 | A_perVert = A_colwise * model.weights'; 53 | 54 | A_perVert = reshape(A_perVert, 4, 4, []); 55 | 56 | for i = 1:size(A_perVert,3) 57 | new_pos = A_perVert(:, :, i) * [v_posed(i, :), 1]'; 58 | v_posed(i, :) = new_pos(1:3); 59 | end 60 | 61 | end 62 | 63 | function [Rt] = pack(t) 64 | Rt = [zeros(4, 3), t]; 65 | end 66 | 67 | function [Rt] = with_zeros(R, t) 68 | Rt = [R, t]; 69 | Rt = [Rt; 0, 0, 0, 1]; 70 | end 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /common/calShapedMesh.m: -------------------------------------------------------------------------------- 1 | function [v_shaped, j_shaped] = calShapedMesh(model, betas) 2 | 3 | shape_coeffs = model.shapedirs.x; % n * 3 * 10 4 | 5 | betas_num = size(shape_coeffs, 3); 6 | if length(betas) ~= betas_num 7 | fprintf('betas size error!\n'); 8 | return ; 9 | end 10 | 11 | betas = reshape(betas, betas_num, []); 12 | shape_coeffs = reshape(shape_coeffs, [], betas_num); 13 | 14 | v_offset = shape_coeffs * betas; 15 | v_offset = reshape(v_offset, [], 3); 16 | v_shaped = model.v_template + v_offset; % return shaped mesh vertices 17 | 18 | j_shaped = model.J_regressor * v_shaped; 19 | 20 | end 21 | 22 | -------------------------------------------------------------------------------- /common/combineParam.m: -------------------------------------------------------------------------------- 1 | function param = combineParam(betas, pose, trans, scale) 2 | 3 | param = [betas, pose, trans, scale]; 4 | 5 | end 6 | -------------------------------------------------------------------------------- /common/divideParam.m: -------------------------------------------------------------------------------- 1 | function [betas, pose, trans, scale] = divideParam(param) 2 | 3 | betas = param(1:10); 4 | pose = param(11:11+71); 5 | trans = param(83:85); 6 | scale = param(86); 7 | 8 | end -------------------------------------------------------------------------------- /common/extract_garments.m: -------------------------------------------------------------------------------- 1 | function [garments, mesh_garments] = extract_garments(mesh, label) 2 | 3 | n_edges = size(mesh.edges, 1); 4 | n_faces = size(mesh.faces, 1); 5 | n_vertices = size(mesh.vertices, 1); 6 | 7 | n_boundary_skin = 1; 8 | boundary_skin = zeros(n_vertices, 1); 9 | 10 | n_boundary_shirt = 1; 11 | boundary_shirt = zeros(n_vertices, 1); 12 | 13 | n_boundary_pants = 1; 14 | boundary_pants = zeros(n_vertices, 1); 15 | 16 | n_boundary_faces = 1; 17 | boundary_faces = zeros(n_faces, 2); 18 | 19 | for i = 1 : n_edges 20 | edge = mesh.edges(i, :); 21 | if label(edge(1)) == label(edge(2)) 22 | continue; 23 | end 24 | 25 | boundary_faces(n_boundary_faces, :) = mesh.edge_faces(i, :); 26 | n_boundary_faces = n_boundary_faces + 1; 27 | 28 | if label(edge(1)) == 0 29 | boundary_skin(n_boundary_skin) = edge(1); 30 | n_boundary_skin = n_boundary_skin + 1; 31 | elseif label(edge(1)) == 1 32 | boundary_shirt(n_boundary_shirt) = edge(1); 33 | n_boundary_shirt = n_boundary_shirt + 1; 34 | elseif label(edge(1)) == 2 35 | boundary_pants(n_boundary_pants) = edge(1); 36 | n_boundary_pants = n_boundary_pants + 1; 37 | end 38 | 39 | if label(edge(2)) == 0 40 | boundary_skin(n_boundary_skin) = edge(2); 41 | n_boundary_skin = n_boundary_skin + 1; 42 | elseif label(edge(2)) == 1 43 | boundary_shirt(n_boundary_shirt) = edge(2); 44 | n_boundary_shirt = n_boundary_shirt + 1; 45 | elseif label(edge(2)) == 2 46 | boundary_pants(n_boundary_pants) = edge(2); 47 | n_boundary_pants = n_boundary_pants + 1; 48 | end 49 | end 50 | 51 | boundary_skin = boundary_skin(1:n_boundary_skin-1, :); 52 | boundary_skin = unique(boundary_skin); 53 | 54 | boundary_shirt = boundary_shirt(1:n_boundary_shirt-1, :); 55 | boundary_shirt = unique(boundary_shirt); 56 | 57 | boundary_pants = boundary_pants(1:n_boundary_pants-1, :); 58 | boundary_pants = unique(boundary_pants); 59 | 60 | boundary_faces = boundary_faces(1:n_boundary_faces-1, :); 61 | boundary_faces(isnan(boundary_faces)) = []; 62 | boundary_faces = unique(boundary_faces); 63 | 64 | ind_skin = find(label == 0); 65 | ind_shirt = find(label == 1); 66 | ind_pants = find(label == 2); 67 | 68 | % skin garment mesh faces 69 | faces_skin_ind = garment_faces(mesh, ind_skin, boundary_faces); 70 | % shirt garment mesh faces 71 | faces_shirt_ind = garment_faces(mesh, ind_shirt, boundary_faces); 72 | % pants garment mesh faces 73 | faces_pants_ind = garment_faces(mesh, ind_pants, boundary_faces); 74 | 75 | % assemble garments vertices index 76 | garments.skin.vertices_ind = ind_skin; 77 | garments.shirt.vertices_ind = ind_shirt; 78 | garments.pants.vertices_ind = ind_pants; 79 | 80 | % assemble garments faces index 81 | garments.skin.faces_ind = faces_skin_ind; 82 | garments.shirt.faces_ind = faces_shirt_ind; 83 | garments.pants.faces_ind = faces_pants_ind; 84 | 85 | % assemble boundary index 86 | garments.skin.boundary_ind = boundary_skin; 87 | garments.shirt.boundary_ind = boundary_shirt; 88 | garments.pants.boundary_ind = boundary_pants; 89 | 90 | % assemble local boundary index 91 | garments.skin.boundary_local_ind = garment_boundary(boundary_skin, ind_skin); 92 | garments.shirt.boundary_local_ind = garment_boundary(boundary_shirt, ind_shirt); 93 | garments.pants.boundary_local_ind = garment_boundary(boundary_pants, ind_pants); 94 | 95 | mesh_garments = mesh; 96 | mesh_garments.faces(boundary_faces, :) = []; 97 | mesh_garments.colors = render_labels(label); 98 | 99 | end 100 | 101 | -------------------------------------------------------------------------------- /common/garment_boundary.m: -------------------------------------------------------------------------------- 1 | function [boundary_local_ind] = garment_boundary(boundary_ind, vertices_ind) 2 | 3 | boundary_ind_tmp = vertices_ind; 4 | for i = 1 : length(boundary_ind_tmp) 5 | r = find(boundary_ind == boundary_ind_tmp(i)); 6 | if ~isnan(r) 7 | boundary_ind_tmp(i) = -1; 8 | end 9 | end 10 | 11 | boundary_local_ind = find(boundary_ind_tmp == -1); 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /common/garment_faces.m: -------------------------------------------------------------------------------- 1 | function [face_ind] = garment_faces(mesh, garment_ind, boundary_faces) 2 | 3 | face_ind = mesh.vert_faces(garment_ind, :); 4 | face_ind = reshape(face_ind, numel(face_ind), 1); 5 | face_ind(face_ind == 0) = []; 6 | face_ind = unique(face_ind); 7 | 8 | for i = 1 : length(face_ind) 9 | r = find(boundary_faces == face_ind(i)); 10 | if ~isnan(r) 11 | face_ind(i) = -1; 12 | end 13 | end 14 | face_ind(face_ind == -1) =[]; 15 | 16 | end -------------------------------------------------------------------------------- /common/render_labels.m: -------------------------------------------------------------------------------- 1 | function [colors] = render_labels(labels) 2 | 3 | colors = zeros(length(labels), 3); 4 | for i = 1 : length(labels) 5 | if labels(i) == 0 % skin 6 | colors(i, :) = [200, 86, 0]; 7 | elseif labels(i) == 1 % t-shirt 8 | colors(i, :) = [25, 114, 175]; 9 | else % pants 10 | colors(i, :) = [29, 139, 58]; 11 | end 12 | end 13 | 14 | end 15 | 16 | -------------------------------------------------------------------------------- /common/rodrigues.m: -------------------------------------------------------------------------------- 1 | function [out,dout]=rodrigues(in) 2 | 3 | % RODRIGUES Transform rotation matrix into rotation vector and viceversa. 4 | % 5 | % Sintax: [OUT]=RODRIGUES(IN) 6 | % If IN is a 3x3 rotation matrix then OUT is the 7 | % corresponding 3x1 rotation vector 8 | % if IN is a rotation 3-vector then OUT is the 9 | % corresponding 3x3 rotation matrix 10 | % 11 | 12 | %% 13 | %% Copyright (c) March 1993 -- Pietro Perona 14 | %% California Institute of Technology 15 | %% 16 | 17 | %% ALL CHECKED BY JEAN-YVES BOUGUET, October 1995. 18 | %% FOR ALL JACOBIAN MATRICES !!! LOOK AT THE TEST AT THE END !! 19 | 20 | %% BUG when norm(om)=pi fixed -- April 6th, 1997; 21 | %% Jean-Yves Bouguet 22 | 23 | %% Add projection of the 3x3 matrix onto the set of special ortogonal matrices SO(3) by SVD -- February 7th, 2003; 24 | %% Jean-Yves Bouguet 25 | 26 | % BUG FOR THE CASE norm(om)=pi fixed by Mike Burl on Feb 27, 2007 27 | 28 | 29 | [m,n] = size(in); 30 | %bigeps = 10e+4*eps; 31 | bigeps = 10e+20*eps; 32 | 33 | if ((m==1) & (n==3)) | ((m==3) & (n==1)) %% it is a rotation vector 34 | theta = norm(in); 35 | if theta < eps 36 | R = eye(3); 37 | 38 | %if nargout > 1, 39 | 40 | dRdin = [0 0 0; 41 | 0 0 1; 42 | 0 -1 0; 43 | 0 0 -1; 44 | 0 0 0; 45 | 1 0 0; 46 | 0 1 0; 47 | -1 0 0; 48 | 0 0 0]; 49 | 50 | %end; 51 | 52 | else 53 | if n==length(in) in=in'; end; %% make it a column vec. if necess. 54 | 55 | %m3 = [in,theta] 56 | 57 | dm3din = [eye(3);in'/theta]; 58 | 59 | omega = in/theta; 60 | 61 | %m2 = [omega;theta] 62 | 63 | dm2dm3 = [eye(3)/theta -in/theta^2; zeros(1,3) 1]; 64 | 65 | alpha = cos(theta); 66 | beta = sin(theta); 67 | gamma = 1-cos(theta); 68 | omegav=[[0 -omega(3) omega(2)];[omega(3) 0 -omega(1)];[-omega(2) omega(1) 0 ]]; 69 | A = omega*omega'; 70 | 71 | %m1 = [alpha;beta;gamma;omegav;A]; 72 | 73 | dm1dm2 = zeros(21,4); 74 | dm1dm2(1,4) = -sin(theta); 75 | dm1dm2(2,4) = cos(theta); 76 | dm1dm2(3,4) = sin(theta); 77 | dm1dm2(4:12,1:3) = [0 0 0 0 0 1 0 -1 0; 78 | 0 0 -1 0 0 0 1 0 0; 79 | 0 1 0 -1 0 0 0 0 0]'; 80 | 81 | w1 = omega(1); 82 | w2 = omega(2); 83 | w3 = omega(3); 84 | 85 | dm1dm2(13:21,1) = [2*w1;w2;w3;w2;0;0;w3;0;0]; 86 | dm1dm2(13: 21,2) = [0;w1;0;w1;2*w2;w3;0;w3;0]; 87 | dm1dm2(13:21,3) = [0;0;w1;0;0;w2;w1;w2;2*w3]; 88 | 89 | R = eye(3)*alpha + omegav*beta + A*gamma; 90 | 91 | dRdm1 = zeros(9,21); 92 | 93 | dRdm1([1 5 9],1) = ones(3,1); 94 | dRdm1(:,2) = omegav(:); 95 | dRdm1(:,4:12) = beta*eye(9); 96 | dRdm1(:,3) = A(:); 97 | dRdm1(:,13:21) = gamma*eye(9); 98 | 99 | dRdin = dRdm1 * dm1dm2 * dm2dm3 * dm3din; 100 | 101 | 102 | end; 103 | out = R; 104 | dout = dRdin; 105 | 106 | %% it is prob. a rot matr. 107 | elseif ((m==n) & (m==3) & (norm(in' * in - eye(3)) < bigeps)... 108 | & (abs(det(in)-1) < bigeps)) 109 | R = in; 110 | 111 | % project the rotation matrix to SO(3); 112 | [U,S,V] = svd(R); 113 | R = U*V'; 114 | 115 | tr = (trace(R)-1)/2; 116 | dtrdR = [1 0 0 0 1 0 0 0 1]/2; 117 | theta = real(acos(tr)); 118 | 119 | 120 | if sin(theta) >= 1e-4, 121 | 122 | dthetadtr = -1/sqrt(1-tr^2); 123 | 124 | dthetadR = dthetadtr * dtrdR; 125 | % var1 = [vth;theta]; 126 | vth = 1/(2*sin(theta)); 127 | dvthdtheta = -vth*cos(theta)/sin(theta); 128 | dvar1dtheta = [dvthdtheta;1]; 129 | 130 | dvar1dR = dvar1dtheta * dthetadR; 131 | 132 | 133 | om1 = [R(3,2)-R(2,3), R(1,3)-R(3,1), R(2,1)-R(1,2)]'; 134 | 135 | dom1dR = [0 0 0 0 0 1 0 -1 0; 136 | 0 0 -1 0 0 0 1 0 0; 137 | 0 1 0 -1 0 0 0 0 0]; 138 | 139 | % var = [om1;vth;theta]; 140 | dvardR = [dom1dR;dvar1dR]; 141 | 142 | % var2 = [om;theta]; 143 | om = vth*om1; 144 | domdvar = [vth*eye(3) om1 zeros(3,1)]; 145 | dthetadvar = [0 0 0 0 1]; 146 | dvar2dvar = [domdvar;dthetadvar]; 147 | 148 | 149 | out = om*theta; 150 | domegadvar2 = [theta*eye(3) om]; 151 | 152 | dout = domegadvar2 * dvar2dvar * dvardR; 153 | 154 | 155 | else 156 | if tr > 0; % case norm(om)=0; 157 | 158 | out = [0 0 0]'; 159 | 160 | dout = [0 0 0 0 0 1/2 0 -1/2 0; 161 | 0 0 -1/2 0 0 0 1/2 0 0; 162 | 0 1/2 0 -1/2 0 0 0 0 0]; 163 | else 164 | 165 | % case norm(om)=pi; 166 | if(0) 167 | 168 | %% fixed April 6th by Bouguet -- not working in all cases! 169 | out = theta * (sqrt((diag(R)+1)/2).*[1;2*(R(1,2:3)>=0)'-1]); 170 | %keyboard; 171 | 172 | else 173 | 174 | % Solution by Mike Burl on Feb 27, 2007 175 | % This is a better way to determine the signs of the 176 | % entries of the rotation vector using a hash table on all 177 | % the combinations of signs of a pairs of products (in the 178 | % rotation matrix) 179 | 180 | % Define hashvec and Smat 181 | hashvec = [0; -1; -3; -9; 9; 3; 1; 13; 5; -7; -11]; 182 | Smat = [1,1,1; 1,0,-1; 0,1,-1; 1,-1,0; 1,1,0; 0,1,1; 1,0,1; 1,1,1; 1,1,-1; 183 | 1,-1,-1; 1,-1,1]; 184 | 185 | M = (R+eye(3,3))/2; 186 | uabs = sqrt(M(1,1)); 187 | vabs = sqrt(M(2,2)); 188 | wabs = sqrt(M(3,3)); 189 | 190 | mvec = [M(1,2), M(2,3), M(1,3)]; 191 | syn = ((mvec > 1e-4) - (mvec < -1e-4)); % robust sign() function 192 | hash = syn * [9; 3; 1]; 193 | idx = find(hash == hashvec); 194 | svec = Smat(idx,:)'; 195 | 196 | out = theta * [uabs; vabs; wabs] .* svec; 197 | 198 | end; 199 | 200 | if nargout > 1, 201 | fprintf(1,'WARNING!!!! Jacobian domdR undefined!!!\n'); 202 | dout = NaN*ones(3,9); 203 | end; 204 | end; 205 | end; 206 | 207 | else 208 | error('Neither a rotation matrix nor a rotation vector were provided'); 209 | end; 210 | 211 | return; 212 | 213 | -------------------------------------------------------------------------------- /mesh_parser/cplusplus/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(mesh_parser) 4 | 5 | if(NOT WIN32) 6 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 8 | add_definitions(-D_GNU_SOURCE -fPIC) 9 | endif() 10 | 11 | find_package(glm REQUIRED) 12 | include_directories(${glm_INCLUDE_DIRS}) 13 | 14 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") 15 | find_package(OpenMesh REQUIRED) 16 | include_directories(${OPENMESH_INCLUDE_DIRS}) 17 | link_libraries(${OPENMESH_LIBRARIES}) 18 | 19 | add_executable(mesh_parser main.cpp mesh_parser.cpp) 20 | -------------------------------------------------------------------------------- /mesh_parser/cplusplus/cmake/FindOpenMesh.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Try to find OPENMESH 3 | # Once done this will define 4 | # 5 | # OPENMESH_FOUND - system has OPENMESH 6 | # OPENMESH_INCLUDE_DIRS - the OPENMESH include directories 7 | # OPENMESH_LIBRARIES - Link these to use OPENMESH 8 | # OPENMESH_LIBRARY_DIR - directory where the libraries are included 9 | # 10 | # Copyright 2015 Computer Graphics Group, RWTH Aachen University 11 | # Authors: Jan Möbius 12 | # Hans-Christian Ebke 13 | # 14 | # 15 | #=========================================================================== 16 | # 17 | # OpenMesh 18 | # Copyright (c) 2001-2015, RWTH-Aachen University 19 | # Department of Computer Graphics and Multimedia 20 | # All rights reserved. 21 | # www.openmesh.org 22 | # 23 | #--------------------------------------------------------------------------- 24 | # This file is part of OpenMesh. 25 | #--------------------------------------------------------------------------- 26 | # 27 | # Redistribution and use in source and binary forms, with or without 28 | # modification, are permitted provided that the following conditions 29 | # are met: 30 | # 31 | # 1. Redistributions of source code must retain the above copyright notice, 32 | # this list of conditions and the following disclaimer. 33 | # 34 | # 2. Redistributions in binary form must reproduce the above copyright 35 | # notice, this list of conditions and the following disclaimer in the 36 | # documentation and/or other materials provided with the distribution. 37 | # 38 | # 3. Neither the name of the copyright holder nor the names of its 39 | # contributors may be used to endorse or promote products derived from 40 | # this software without specific prior written permission. 41 | # 42 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 43 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 44 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 45 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 46 | # OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 47 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 48 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 49 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 50 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 51 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 52 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 | # 54 | #=========================================================================== 55 | 56 | cmake_minimum_required(VERSION 2.8.9) 57 | 58 | #if already found via finder or simulated finder in openmesh CMakeLists.txt, skip the search 59 | IF (NOT OPENMESH_FOUND) 60 | SET (SEARCH_PATHS 61 | /usr/local/ 62 | /usr/ 63 | "${CMAKE_SOURCE_DIR}/OpenMesh/src/OpenMesh" 64 | "${CMAKE_SOURCE_DIR}/libs_required/OpenMesh/src/OpenMesh" 65 | "${CMAKE_SOURCE_DIR}/../OpenMesh/src/OpenMesh" 66 | "C:/Program Files/OpenMesh 6.3" 67 | "C:/Program Files/OpenMesh 6.2" 68 | "C:/Program Files/OpenMesh 6.1" 69 | "C:/Program Files/OpenMesh 6.0" 70 | "C:/Program Files/OpenMesh 5.2" 71 | "C:/Program Files/OpenMesh 5.1" 72 | "C:/Program Files/OpenMesh 5.0" 73 | "C:/Program Files/OpenMesh 4.2" 74 | "C:/Program Files/OpenMesh 4.1" 75 | "C:/Program Files/OpenMesh 4.0" 76 | "C:/Program Files/OpenMesh 3.4" 77 | "C:/Program Files/OpenMesh 3.3" 78 | "C:/Program Files/OpenMesh 3.2" 79 | "C:/Program Files/OpenMesh 3.1" 80 | "C:/Program Files/OpenMesh 3.0" 81 | "C:/Program Files/OpenMesh 2.4.1" 82 | "C:/Program Files/OpenMesh 2.4" 83 | "C:/Program Files/OpenMesh 2.0/include" 84 | "C:/libs/OpenMesh 6.3" 85 | "C:/libs/OpenMesh 6.2" 86 | "C:/libs/OpenMesh 6.1" 87 | "C:/libs/OpenMesh 6.0" 88 | "C:/libs/OpenMesh 5.2" 89 | "C:/libs/OpenMesh 5.1" 90 | "C:/libs/OpenMesh 5.0" 91 | "C:/libs/OpenMesh 4.2" 92 | "C:/libs/OpenMesh 4.1" 93 | "C:/libs/OpenMesh 4.0" 94 | "C:/libs/OpenMesh 3.4" 95 | "C:/libs/OpenMesh 3.3" 96 | "C:/libs/OpenMesh 3.2" 97 | "C:/libs/OpenMesh 3.1" 98 | "C:/libs/OpenMesh 3.0" 99 | "C:/libs/OpenMesh 2.4.1" 100 | "C:/libs/OpenMesh 2.4" 101 | "${OPENMESH_LIBRARY_DIR}" 102 | ) 103 | 104 | FIND_PATH (OPENMESH_INCLUDE_DIR OpenMesh/Core/Mesh/PolyMeshT.hh 105 | PATHS ${SEARCH_PATHS} 106 | PATH_SUFFIXES include) 107 | 108 | FIND_LIBRARY(OPENMESH_CORE_LIBRARY_RELEASE NAMES OpenMeshCore 109 | PATHS ${SEARCH_PATHS} 110 | PATH_SUFFIXES lib lib64) 111 | 112 | FIND_LIBRARY(OPENMESH_CORE_LIBRARY_DEBUG NAMES OpenMeshCored 113 | PATHS ${SEARCH_PATHS} 114 | PATH_SUFFIXES lib lib64) 115 | 116 | FIND_LIBRARY(OPENMESH_TOOLS_LIBRARY_RELEASE NAMES OpenMeshTools 117 | PATHS ${SEARCH_PATHS} 118 | PATH_SUFFIXES lib lib64) 119 | 120 | FIND_LIBRARY(OPENMESH_TOOLS_LIBRARY_DEBUG NAMES OpenMeshToolsd 121 | PATHS ${SEARCH_PATHS} 122 | PATH_SUFFIXES lib lib64) 123 | 124 | #select configuration depending on platform (optimized... on windows) 125 | include(SelectLibraryConfigurations) 126 | select_library_configurations( OPENMESH_TOOLS ) 127 | select_library_configurations( OPENMESH_CORE ) 128 | 129 | set(OPENMESH_LIBRARIES ${OPENMESH_CORE_LIBRARY} ${OPENMESH_TOOLS_LIBRARY} ) 130 | set(OPENMESH_INCLUDE_DIRS ${OPENMESH_INCLUDE_DIR} ) 131 | 132 | #checks, if OPENMESH was found and sets OPENMESH_FOUND if so 133 | include(FindPackageHandleStandardArgs) 134 | find_package_handle_standard_args(OpenMesh DEFAULT_MSG 135 | OPENMESH_CORE_LIBRARY OPENMESH_TOOLS_LIBRARY OPENMESH_INCLUDE_DIR) 136 | 137 | #sets the library dir 138 | if ( OPENMESH_CORE_LIBRARY_RELEASE ) 139 | get_filename_component(_OPENMESH_LIBRARY_DIR ${OPENMESH_CORE_LIBRARY_RELEASE} PATH) 140 | else( OPENMESH_CORE_LIBRARY_RELEASE ) 141 | get_filename_component(_OPENMESH_LIBRARY_DIR ${OPENMESH_CORE_LIBRARY_DEBUG} PATH) 142 | endif( OPENMESH_CORE_LIBRARY_RELEASE ) 143 | set (OPENMESH_LIBRARY_DIR "${_OPENMESH_LIBRARY_DIR}" CACHE PATH "The directory where the OpenMesh libraries can be found.") 144 | 145 | 146 | mark_as_advanced(OPENMESH_INCLUDE_DIR OPENMESH_CORE_LIBRARY_RELEASE OPENMESH_CORE_LIBRARY_DEBUG OPENMESH_TOOLS_LIBRARY_RELEASE OPENMESH_TOOLS_LIBRARY_DEBUG OPENMESH_LIBRARY_DIR) 147 | endif() 148 | -------------------------------------------------------------------------------- /mesh_parser/cplusplus/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mesh_parser.h" 2 | 3 | #include 4 | #include 5 | 6 | int main(int argc, char** argv) 7 | { 8 | if (argc != 2) 9 | { 10 | std::cerr << "[error] no mesh filename specified, aborting ..." << std::endl; 11 | exit(EXIT_FAILURE); 12 | } 13 | std::string mesh_filename = std::string(argv[1]); 14 | 15 | MeshParser mesh_parser(mesh_filename); 16 | 17 | // output data to file 18 | std::ofstream out; 19 | 20 | out.open("d_vertices.mesh", std::ios::binary); 21 | for (auto& v : mesh_parser.vertices) 22 | out << v.x << " " << v.y << " " << v.z << std::endl; 23 | out.close(); 24 | 25 | out.open("d_normals.mesh", std::ios::binary); 26 | for (auto& n : mesh_parser.normals) 27 | out << n.x << " " << n.y << " " << n.z << std::endl; 28 | out.close(); 29 | 30 | out.open("d_faces.mesh", std::ios::binary); 31 | for (auto& f : mesh_parser.faces) 32 | out << f.x << " " << f.y << " " << f.z << std::endl; 33 | out.close(); 34 | 35 | out.open("d_edges.mesh", std::ios::binary); 36 | for (auto& e : mesh_parser.edges) 37 | out << e.x << " " << e.y << std::endl; 38 | out.close(); 39 | 40 | out.open("d_vert_faces.mesh", std::ios::binary); 41 | for (auto& vf : mesh_parser.vert_faces) 42 | { 43 | for (auto& f : vf.second) 44 | out << f << " "; 45 | out << std::endl; 46 | } 47 | out.close(); 48 | 49 | out.open("d_vert_edges.mesh", std::ios::binary); 50 | for (auto& ve : mesh_parser.vert_edges) 51 | { 52 | for (auto& e : ve.second) 53 | out << e << " "; 54 | out << std::endl; 55 | } 56 | out.close(); 57 | 58 | out.open("d_face_edges.mesh", std::ios::binary); 59 | for (auto& fe : mesh_parser.face_edges) 60 | { 61 | for (auto& e : fe.second) 62 | out << e << " "; 63 | out << std::endl; 64 | } 65 | out.close(); 66 | 67 | out.open("d_edge_faces.mesh", std::ios::binary); 68 | for (auto& ef : mesh_parser.edge_faces) 69 | { 70 | for (auto& f : ef.second) 71 | out << f << " "; 72 | out << std::endl; 73 | } 74 | out.close(); 75 | 76 | if (mesh_parser.textured()) 77 | { 78 | out.open("d_tex_map.mesh", std::ios::binary); 79 | out << mesh_parser.tex_map_name << std::endl; 80 | out.close(); 81 | 82 | out.open("d_tex_coords.mesh", std::ios::binary); 83 | for (auto& t : mesh_parser.tex_coords) 84 | out << t.x << " " << t.y << std::endl; 85 | out.close(); 86 | } 87 | 88 | return 0; 89 | } 90 | -------------------------------------------------------------------------------- /mesh_parser/cplusplus/mesh_parser.cpp: -------------------------------------------------------------------------------- 1 | #include "mesh_parser.h" 2 | 3 | #include 4 | #include 5 | 6 | MeshParser::MeshParser() 7 | { 8 | initialize(); 9 | } 10 | 11 | MeshParser::MeshParser(const std::string& mesh_name) 12 | { 13 | initialize(); 14 | load(mesh_name); 15 | } 16 | 17 | void MeshParser::load(const std::string& mesh_name) 18 | { 19 | if (!OpenMesh::IO::read_mesh(mesh_, mesh_name, opt_)) 20 | { 21 | std::cerr << "[error] Read mesh file failed, invalid mesh file ..." << std::endl; 22 | exit(EXIT_FAILURE); 23 | } 24 | 25 | if (!opt_.check(OpenMesh::IO::Options::VertexNormal)) 26 | { 27 | mesh_.request_face_normals(); 28 | mesh_.update_normals(); 29 | mesh_.release_face_normals(); 30 | } 31 | 32 | max_vert_face_num = 0; 33 | max_vert_edge_num = 0; 34 | 35 | // parse obj file 36 | for (auto v_it = mesh_.vertices_begin(); v_it != mesh_.vertices_end(); ++v_it) 37 | { 38 | vertices.emplace_back( 39 | mesh_.point(*v_it).data()[0], 40 | mesh_.point(*v_it).data()[1], 41 | mesh_.point(*v_it).data()[2]); 42 | 43 | std::vector faces; 44 | for (auto vf_it = mesh_.vf_iter(*v_it); vf_it.is_valid(); ++vf_it) 45 | faces.emplace_back(vf_it->idx()); 46 | vert_faces.insert(std::make_pair(v_it->idx(), faces)); 47 | 48 | std::vector edges; 49 | for (auto ve_it = mesh_.ve_iter(*v_it); ve_it.is_valid(); ++ve_it) 50 | edges.emplace_back(ve_it->idx()); 51 | vert_edges.insert(std::make_pair(v_it->idx(), edges)); 52 | 53 | auto face_n = faces.size(); 54 | max_vert_face_num = max_vert_face_num < face_n ? face_n : max_vert_face_num; 55 | 56 | auto edge_n = edges.size(); 57 | max_vert_edge_num = max_vert_edge_num < edge_n ? edge_n : max_vert_edge_num; 58 | } 59 | 60 | for (auto v_it = mesh_.vertices_begin(); v_it != mesh_.vertices_end(); ++v_it) 61 | { 62 | normals.emplace_back( 63 | mesh_.normal(*v_it).data()[0], 64 | mesh_.normal(*v_it).data()[1], 65 | mesh_.normal(*v_it).data()[2]); 66 | } 67 | 68 | for (auto v_it = mesh_.vertices_begin(); v_it != mesh_.vertices_end(); ++v_it) 69 | { 70 | tex_coords.emplace_back( 71 | mesh_.texcoord2D(*v_it).data()[0], 72 | mesh_.texcoord2D(*v_it).data()[1]); 73 | } 74 | 75 | for (auto f_it = mesh_.faces_begin(); f_it != mesh_.faces_end(); ++f_it) 76 | { 77 | std::vector vertices_ind; 78 | for (auto fv_it = mesh_.fv_iter(*f_it); fv_it.is_valid(); ++fv_it) 79 | vertices_ind.emplace_back(fv_it->idx()); 80 | assert(vertices_ind.size() == 3); 81 | faces.emplace_back(vertices_ind[0], vertices_ind[1], vertices_ind[2]); 82 | 83 | std::vector edges_ind; 84 | for (auto fe_it = mesh_.fe_iter(*f_it); fe_it.is_valid(); ++fe_it) 85 | edges_ind.emplace_back(fe_it->idx()); 86 | face_edges.insert(std::make_pair(f_it->idx(), edges_ind)); 87 | } 88 | 89 | for (auto e_it = mesh_.edges_begin(); e_it != mesh_.edges_end(); ++e_it) 90 | { 91 | auto he0 = mesh_.halfedge_handle(*e_it, 0); 92 | auto he1 = mesh_.halfedge_handle(*e_it, 1); 93 | 94 | auto from_v = mesh_.from_vertex_handle(he0); 95 | auto to_v = mesh_.to_vertex_handle(he0); 96 | edges.emplace_back(from_v.idx(), to_v.idx()); 97 | 98 | auto face0 = mesh_.face_handle(he0); 99 | auto face1 = mesh_.face_handle(he1); 100 | std::vector f; 101 | if (face0.is_valid()) 102 | f.emplace_back(face0.idx()); 103 | if (face1.is_valid()) 104 | f.emplace_back(face1.idx()); 105 | edge_faces.insert(std::make_pair(e_it->idx(), f)); 106 | } 107 | 108 | std::for_each(edges.begin(), edges.end(), [](glm::ivec2& edge) 109 | { 110 | if (edge[0] > edge[1]) 111 | std::swap(edge[0], edge[1]); 112 | }); 113 | auto p = std::unique(edges.begin(), edges.end(), [](const glm::ivec2& v1, const glm::ivec2& v2) 114 | { 115 | return v1[0] == v2[0] && v1[1] == v2[1]; 116 | }); 117 | edges.erase(p, edges.end()); 118 | 119 | std::for_each(vert_faces.begin(), vert_faces.end(), [&](std::pair>& pair) 120 | { 121 | pair.second.resize(max_vert_face_num, -1); 122 | }); 123 | 124 | std::for_each(vert_edges.begin(), vert_edges.end(), [&](std::pair>& pair) 125 | { 126 | pair.second.resize(max_vert_edge_num, -1); 127 | }); 128 | } 129 | 130 | void MeshParser::initialize() 131 | { 132 | mesh_.request_vertex_normals(); 133 | mesh_.request_vertex_texcoords2D(); 134 | 135 | if (!mesh_.has_vertex_normals() || !mesh_.has_vertex_texcoords2D()) 136 | { 137 | std::cerr << "[error] Vertex normal or texcoords2D invalid." << std::endl; 138 | exit(EXIT_FAILURE); 139 | } 140 | 141 | opt_ = OpenMesh::IO::Options(); 142 | opt_ += OpenMesh::IO::Options::VertexTexCoord; 143 | opt_ += OpenMesh::IO::Options::VertexNormal; 144 | } 145 | 146 | bool MeshParser::textured() 147 | { 148 | OpenMesh::MPropHandleT > property; 149 | if (mesh_.get_property_handle(property, "TextureMapping")) 150 | { 151 | auto texture_property = mesh_.property(property); 152 | tex_map_name = texture_property[1]; 153 | return true; 154 | } 155 | tex_map_name = ""; 156 | return false; 157 | } 158 | -------------------------------------------------------------------------------- /mesh_parser/cplusplus/mesh_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef _MESHPARSER_H_ 2 | #define _MESHPARSER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using DefaultMesh = OpenMesh::TriMesh_ArrayKernelT<>; 9 | 10 | class MeshParser 11 | { 12 | public: 13 | std::vector vertices; 14 | std::vector normals; 15 | std::vector tex_coords; 16 | 17 | std::vector faces; 18 | std::vector edges; 19 | std::map> vert_faces; 20 | std::map> vert_edges; 21 | std::map> edge_faces; 22 | std::map> face_edges; 23 | 24 | std::string tex_map_name; 25 | 26 | size_t max_vert_face_num; 27 | size_t max_vert_edge_num; 28 | 29 | MeshParser(); 30 | explicit MeshParser(const std::string& mesh_name); 31 | 32 | void initialize(); 33 | void load(const std::string& mesh_name); 34 | bool textured(); 35 | 36 | private: 37 | DefaultMesh mesh_; 38 | OpenMesh::IO::Options opt_; 39 | }; 40 | 41 | #endif // _MESHPARSER_H_ 42 | -------------------------------------------------------------------------------- /mesh_parser/mesh_exporter.m: -------------------------------------------------------------------------------- 1 | function mesh_exporter(varargin) 2 | 3 | n = numel(varargin); 4 | assert(n >= 2); 5 | 6 | mesh_filename = varargin{1}; 7 | mesh = varargin{2}; 8 | 9 | if n == 2 10 | color = false; 11 | tex = false; 12 | elseif n == 3 13 | color = varargin{3}; 14 | tex = false; 15 | elseif n == 4 16 | color = varargin{3}; 17 | tex = varargin{4}; 18 | end 19 | 20 | assert(~(color & tex)); 21 | 22 | fp = fopen(mesh_filename, 'wb'); 23 | 24 | [n_faces, ~] = size(mesh.faces); 25 | [n_vertices, ~] = size(mesh.vertices); 26 | 27 | if color == true 28 | for i = 1 : n_vertices 29 | fprintf(fp, 'v %f %f %f ', mesh.vertices(i, 1), mesh.vertices(i, 2), mesh.vertices(i, 3)); 30 | fprintf(fp, '%d %d %d\n', mesh.colors(i, 1), mesh.colors(i, 2), mesh.colors(i, 3)); 31 | end 32 | else 33 | for i = 1 : n_vertices 34 | fprintf(fp, 'v %f %f %f\n', mesh.vertices(i, 1), mesh.vertices(i, 2), mesh.vertices(i, 3)); 35 | end 36 | end 37 | 38 | for i = 1 : n_faces 39 | fprintf(fp, 'f %d %d %d\n', mesh.faces(i, 1), mesh.faces(i, 2), mesh.faces(i, 3)); 40 | end 41 | 42 | if tex == true 43 | for i = 1 : n_vertices 44 | fprintf(fp, 'vt %f %f\n', mesh.tex_coords(i, 1), mesh.tex_coords(i, 2)); 45 | end 46 | end 47 | 48 | fclose(fp); 49 | 50 | end -------------------------------------------------------------------------------- /mesh_parser/mesh_parser.m: -------------------------------------------------------------------------------- 1 | function [mesh] = mesh_parser(mesh_name, mesh_folder) 2 | 3 | cd('mesh_parser'); 4 | 5 | delete('*.mesh'); 6 | 7 | mesh_filename = ['..', filesep, mesh_folder, filesep, mesh_name]; 8 | if strncmp(computer, 'PC', 2) 9 | system(['mesh_parser.exe ', mesh_filename]); 10 | else 11 | system(['./mesh_parser ', mesh_filename]); 12 | end 13 | 14 | vertices = importdata('d_vertices.mesh'); 15 | normals = importdata('d_normals.mesh'); 16 | edges = importdata('d_edges.mesh') + 1; 17 | faces = importdata('d_faces.mesh') + 1; 18 | 19 | face_edges = importdata('d_face_edges.mesh') + 1; 20 | edge_faces = importdata('d_edge_faces.mesh') + 1; 21 | vert_faces = importdata('d_vert_faces.mesh') + 1; 22 | vert_edges = importdata('d_vert_edges.mesh') + 1; 23 | 24 | if exist('d_tex_coords.mesh', 'file') && exist('d_tex_map.mesh', 'file') 25 | tex_coords = importdata('d_tex_coords.mesh'); 26 | tex_name = importdata('d_tex_map.mesh'); 27 | mesh.tex_coords = tex_coords; 28 | mesh.uv_map = imread(['..', filesep, mesh_folder, filesep, tex_name{1}]); 29 | end 30 | 31 | mesh.vertices = vertices; 32 | mesh.normals = normals; 33 | mesh.edges = edges; 34 | mesh.faces = faces; 35 | 36 | mesh.face_edges = face_edges; 37 | mesh.edge_faces = edge_faces; 38 | mesh.vert_faces = vert_faces; 39 | mesh.vert_edges = vert_edges; 40 | 41 | n_edges = size(edges, 1); 42 | n_vertices = size(vertices, 1); 43 | adjacency_map = zeros(n_vertices, n_vertices); 44 | for i = 1 : n_edges 45 | adjacency_map(edges(i, 1), edges(i, 2)) = 1; 46 | adjacency_map(edges(i, 2), edges(i, 1)) = 1; 47 | end 48 | mesh.adjacency_map = adjacency_map; 49 | 50 | delete('*.mesh'); 51 | 52 | cd('..') 53 | 54 | end -------------------------------------------------------------------------------- /multi_cloth.m: -------------------------------------------------------------------------------- 1 | clear all; 2 | close all; 3 | clc; 4 | 5 | addpath('common'); 6 | addpath('smpl_model'); 7 | addpath('mesh_parser'); 8 | addpath('multi_cloth_alignment'); 9 | 10 | frame_start = 1; 11 | frame_end = 525; 12 | 13 | global is_first; 14 | global smpl_model; 15 | global mesh_folder; 16 | global mesh_prefix; 17 | global mesh_prefix_last; 18 | global result_dir; 19 | global result_dir_base; 20 | global use_python; 21 | 22 | use_python = false; 23 | 24 | smpl_model = load('smpl_m.mat'); 25 | smpl_model = smpl_model.model; 26 | 27 | mesh_folder = 'body_easy'; 28 | mesh_format = '20171227-body-easy_texture_%08d_gop.obj'; 29 | 30 | result_dir_base = ['all_results', filesep, ... 31 | 'multi_cloth', filesep, mesh_folder]; 32 | mkdir(result_dir_base); 33 | 34 | % initial SMPL data from 1st frame 35 | [label_smpl, garments_smpl, mesh_smpl] = ... 36 | initial_smpl(mesh_folder); 37 | 38 | for frame = frame_start : frame_end 39 | 40 | mesh_prefix = sprintf(mesh_format, frame); 41 | mesh_prefix = mesh_prefix(1:end-4); 42 | disp(['multi-cloth alignment: ', mesh_prefix]); 43 | 44 | % for first frame 45 | if frame == 1 46 | is_first = 1; 47 | mesh_prefix_last = ''; 48 | else 49 | is_first = 0; 50 | mesh_prefix_last = sprintf(mesh_format, frame - 1); 51 | mesh_prefix_last = mesh_prefix_last(1:end-4); 52 | end 53 | 54 | result_dir = [result_dir_base, filesep, mesh_prefix]; 55 | mkdir(result_dir); 56 | 57 | % load scan label 58 | label_path = ['all_results', filesep, 'segmentation', ... 59 | filesep, mesh_folder, filesep, mesh_prefix]; 60 | label_name = [mesh_prefix, '_label_scan.mat']; 61 | label_scan = load([label_path, filesep, label_name]); 62 | label_scan = label_scan.seg_scan; 63 | 64 | % load scan mesh 65 | mesh_scan_name = [mesh_prefix, '.obj']; 66 | mesh_scan_path = ['scans', filesep, mesh_folder]; 67 | mesh_scan = mesh_parser(mesh_scan_name, mesh_scan_path); 68 | mesh_scan.vertices = mesh_scan.vertices ./ 1000; 69 | mesh_scan.colors = render_labels(label_scan); 70 | mesh_exporter([result_dir, filesep, mesh_prefix, ... 71 | '_seg_scan.obj'], mesh_scan, true); 72 | 73 | % load scan garments 74 | garment_path = ['all_results', filesep, 'segmentation', ... 75 | filesep, mesh_folder, filesep, mesh_prefix]; 76 | garment_name = [mesh_prefix, '_garments_scan.mat']; 77 | garments_scan = load([garment_path, filesep, garment_name]); 78 | garments_scan = garments_scan.garments_scan; 79 | 80 | % garments fitting 81 | garment_fitting( ... 82 | mesh_scan, label_scan, garments_scan, ... 83 | mesh_smpl, label_smpl, garments_smpl); 84 | 85 | end -------------------------------------------------------------------------------- /multi_cloth_alignment/align_cloth.m: -------------------------------------------------------------------------------- 1 | function [vertices, pose] = align_cloth(garment_scan, garment_smpl, ... 2 | mesh_scan, mesh_smpl, label_smpl) 3 | 4 | global smpl_param; 5 | global mesh_prefix; 6 | global result_dir; 7 | 8 | tmp_result_folder = [result_dir, filesep, mesh_prefix, '_clothing']; 9 | mkdir(tmp_result_folder); 10 | 11 | L = mesh_smpl.vertices; 12 | theta = reshape(smpl_param(11:82), 24, 3); 13 | 14 | mesh_tmp = mesh_smpl; 15 | 16 | for iter = 1 : 10 17 | mesh_tmp.vertices = L; 18 | mesh_tmp.normals = calNormal(mesh_tmp.faces, mesh_tmp.vertices); 19 | 20 | param = [theta; L]; 21 | 22 | options = optimoptions(@lsqnonlin, 'Algorithm', 'levenberg-marquardt', ... 23 | 'Display', 'iter-detailed', 'MaxIter', 10); 24 | param_opt = lsqnonlin(@(x) energy_cloth(x, mesh_scan, mesh_tmp, ... 25 | garment_smpl, garment_scan, smpl_param), param, [], [], options); 26 | 27 | L = param_opt(25:end, :); 28 | theta = param_opt(1:24, :); 29 | 30 | mesh_full = mesh_tmp; 31 | mesh_full.vertices = L; 32 | mesh_full.colors = render_labels(label_smpl); 33 | mesh_exporter([tmp_result_folder, filesep, ... 34 | sprintf('cloth_full_iter_%02d.obj', iter)], mesh_full, true); 35 | end 36 | 37 | mesh_exporter([result_dir, filesep, mesh_prefix, '_cloth_full.obj'], mesh_full, true); 38 | 39 | vertices = L; 40 | pose = reshape(theta, 1, 72); 41 | 42 | end 43 | 44 | -------------------------------------------------------------------------------- /multi_cloth_alignment/align_garment.m: -------------------------------------------------------------------------------- 1 | function [vertices, pose] = align_garment(garment_scan, garment_smpl, ... 2 | mesh_scan, mesh_smpl, label_smpl, pose_init, garment_prefix, iter_num) 3 | 4 | global n_smpl; 5 | global is_first; 6 | global mesh_prefix; 7 | global result_dir; 8 | global use_python; 9 | global smpl_param; 10 | 11 | % variables to be optimized 12 | theta = reshape(pose_init, 24, 3); 13 | L = mesh_smpl.vertices(garment_smpl.vertices_ind, :); 14 | 15 | % use MATLAB optimization 16 | if use_python == false 17 | 18 | tmp_result_folder = [result_dir, filesep, ... 19 | mesh_prefix, '_garments_', garment_prefix]; 20 | mkdir(tmp_result_folder); 21 | 22 | mesh_smpl_tmp = mesh_smpl; 23 | 24 | for iter = 1 : 1 25 | 26 | mesh_smpl_tmp.vertices(garment_smpl.vertices_ind, :) = L; 27 | mesh_smpl_tmp.normals = calNormal( ... 28 | mesh_smpl_tmp.faces, mesh_smpl_tmp.vertices); 29 | 30 | param = [theta; L]; 31 | 32 | options = optimoptions(@lsqnonlin, 'Algorithm', 'levenberg-marquardt', ... 33 | 'Display', 'iter-detailed', 'MaxIter', iter_num); 34 | param_opt = lsqnonlin(@(x) energy_garment(x, mesh_scan, mesh_smpl_tmp, ... 35 | garment_smpl, garment_scan), param, [], [], options); 36 | 37 | L = param_opt(25:end, :); 38 | theta = param_opt(1:24, :); 39 | 40 | mesh_name_full = sprintf([garment_prefix, '_full_iter_%02d.obj'], iter); 41 | mesh_name_part = sprintf([garment_prefix, '_part_iter_%02d.obj'], iter); 42 | 43 | mesh_full = mesh_smpl_tmp; 44 | mesh_full.vertices(garment_smpl.vertices_ind, :) = L; 45 | mesh_full.colors = render_labels(label_smpl); 46 | mesh_exporter([tmp_result_folder, filesep, mesh_name_full], mesh_full, true); 47 | 48 | mesh_part = mesh_full; 49 | mesh_part.faces = mesh_full.faces(garment_smpl.faces_ind, :); 50 | mesh_exporter([tmp_result_folder, filesep, mesh_name_part], mesh_part, true); 51 | end 52 | 53 | mesh_exporter([result_dir, filesep, mesh_prefix, ... 54 | '_', garment_prefix, '_full.obj'], mesh_full, true); 55 | mesh_exporter([result_dir, filesep, mesh_prefix, ... 56 | '_', garment_prefix, '_part.obj'], mesh_part, true); 57 | 58 | vertices = L; 59 | pose = reshape(theta, 1, 72); 60 | 61 | % use python scipy solver 62 | else 63 | 64 | path_data = ['python', filesep, 'data']; 65 | mkdir(path_data); 66 | 67 | m_scan.vertices = mesh_scan.vertices; 68 | m_scan.normals = mesh_scan.normals; 69 | m_scan.faces = mesh_scan.faces; 70 | 71 | m_smpl.vertices = mesh_smpl.vertices; 72 | m_smpl.normals = mesh_smpl.normals; 73 | m_smpl.faces = mesh_smpl.faces; 74 | m_smpl.adjacency_map = mesh_smpl.adjacency_map; 75 | 76 | save([path_data, filesep, 'multi_cloth_opt_params.mat'], 'L', 'theta'); 77 | save([path_data, filesep, 'multi_cloth_opt_meshes.mat'], 'm_scan', 'm_smpl'); 78 | save([path_data, filesep, 'multi_cloth_opt_garments.mat'], 'garment_smpl', 'garment_scan'); 79 | save([path_data, filesep, 'multi_cloth_opt_smpl_param.mat'], 'smpl_param'); 80 | save([path_data, filesep, 'multi_cloth_opt_extra.mat'], 'is_first', 'n_smpl'); 81 | 82 | end 83 | 84 | end -------------------------------------------------------------------------------- /multi_cloth_alignment/align_skin.m: -------------------------------------------------------------------------------- 1 | function [vertices, pose] = align_skin(skin_garment_scan, ... 2 | skin_garment_smpl, mesh_scan, mesh_smpl, label_smpl) 3 | 4 | global smpl_param; 5 | global mesh_prefix; 6 | global result_dir; 7 | 8 | tmp_result_folder = [result_dir, filesep, mesh_prefix, '_garments_skin']; 9 | mkdir(tmp_result_folder); 10 | 11 | L = mesh_smpl.vertices(skin_garment_smpl.vertices_ind, :); 12 | theta = reshape(smpl_param(11:82), 24, 3); 13 | 14 | mesh_smpl_tmp = mesh_smpl; 15 | 16 | for iter = 1 : 10 17 | mesh_smpl_tmp.vertices(skin_garment_smpl.vertices_ind, :) = L; 18 | mesh_smpl_tmp.normals = calNormal(mesh_smpl_tmp.faces, mesh_smpl_tmp.vertices); 19 | 20 | param = [theta; L]; 21 | 22 | options = optimoptions(@lsqnonlin, 'Algorithm', 'levenberg-marquardt', ... 23 | 'Display', 'iter-detailed', 'MaxIter', 10); 24 | param_opt = lsqnonlin(@(x) energy_skin(x, mesh_scan, mesh_smpl_tmp, ... 25 | skin_garment_smpl, skin_garment_scan, smpl_param), param, [], [], options); 26 | 27 | mesh_name_full = sprintf('skin_full_iter_%02d.obj', iter); 28 | mesh_name_part = sprintf('skin_iter_%02d.obj', iter); 29 | 30 | L = param_opt(25:end, :); 31 | theta = param_opt(1:24, :); 32 | 33 | mesh_full = mesh_smpl_tmp; 34 | mesh_full.vertices(skin_garment_smpl.vertices_ind, :) = L; 35 | mesh_full.colors = render_labels(label_smpl); 36 | mesh_exporter([tmp_result_folder, filesep, mesh_name_full], mesh_full, true); 37 | 38 | mesh_part = mesh_full; 39 | mesh_part.faces = mesh_full.faces(skin_garment_smpl.faces_ind, :); 40 | mesh_exporter([tmp_result_folder, filesep, mesh_name_part], mesh_part, true); 41 | end 42 | 43 | mesh_exporter([result_dir, filesep, mesh_prefix, '_skin_full.obj'], mesh_full, true); 44 | mesh_exporter([result_dir, filesep, mesh_prefix, '_skin.obj'], mesh_part, true); 45 | 46 | vertices = L; 47 | pose = reshape(theta, 1, 72); 48 | 49 | end 50 | -------------------------------------------------------------------------------- /multi_cloth_alignment/build_ring.m: -------------------------------------------------------------------------------- 1 | function [ring, boundary] = build_ring(boundary, adjacency_map, label) 2 | 3 | ring = zeros(size(boundary)); 4 | 5 | count = 2; 6 | start = boundary(1); 7 | stop = -1; 8 | 9 | while 1 10 | neighbors = find(adjacency_map(start, :) == 1); 11 | for i = 1 : length(neighbors) 12 | p = find(boundary == neighbors(i), 1); 13 | if isempty(p) || label(neighbors(i)) ~= label(start) 14 | neighbors(i) = -1; 15 | end 16 | end 17 | neighbors(neighbors == -1 | neighbors == stop) = []; 18 | 19 | ring(count) = start; 20 | count = count + 1; 21 | 22 | stop = start; 23 | start = neighbors(1); 24 | 25 | if start == boundary(1) 26 | ring(1) = stop; 27 | ring(count) = start; 28 | break; 29 | end 30 | end 31 | 32 | ring(ring == 0) = []; 33 | 34 | for i = 1 : length(ring) 35 | boundary(boundary == ring(i)) = []; 36 | end 37 | 38 | end 39 | 40 | -------------------------------------------------------------------------------- /multi_cloth_alignment/energy_cloth.m: -------------------------------------------------------------------------------- 1 | function [energy] = energy_cloth(x, mesh_scan, mesh_smpl, garment_smpl, garment_scan, smpl_param) 2 | 3 | L = x(25:end, :); 4 | theta = x(1:24, :); 5 | 6 | v_skin = L(garment_smpl.skin.vertices_ind, :); 7 | v_shirt = L(garment_smpl.shirt.vertices_ind, :); 8 | v_pants = L(garment_smpl.pants.vertices_ind, :); 9 | 10 | x_skin = [theta; v_skin]; 11 | x_shirt = [theta; v_shirt]; 12 | x_pants = [theta; v_pants]; 13 | 14 | energy1 = energy_skin(x_skin, mesh_scan, mesh_smpl, ... 15 | garment_smpl.skin, garment_scan.skin, smpl_param); 16 | 17 | energy2 = energy_garment(x_shirt, mesh_scan, mesh_smpl, ... 18 | garment_smpl.shirt, garment_scan.shirt, smpl_param); 19 | 20 | energy3 = energy_garment(x_pants, mesh_scan, mesh_smpl, ... 21 | garment_smpl.pants, garment_scan.pants, smpl_param); 22 | 23 | energy = energy1 + energy2 + energy3; 24 | 25 | end 26 | -------------------------------------------------------------------------------- /multi_cloth_alignment/energy_garment.m: -------------------------------------------------------------------------------- 1 | function [energy] = energy_garment(x, mesh_scan, mesh_smpl, garment_smpl, garment_scan) 2 | 3 | global n_smpl; 4 | global is_first; 5 | global smpl_model; 6 | global smpl_param; 7 | 8 | L = x(25:end, :); 9 | theta = reshape(x(1:24, :), 72, 1)'; 10 | 11 | sigma = 0.2; 12 | energy = 0; 13 | 14 | % w_g = 1000; 15 | % w_b = 20; 16 | % w_c = 1.5; 17 | % w_s = 200; 18 | % w_a = 20; 19 | 20 | if is_first == 1 21 | w_g = 1; 22 | w_b = 10; 23 | w_c = 1.5; 24 | w_s = 1000; 25 | w_a = 2000; 26 | else 27 | w_g = 1; 28 | w_b = 10; 29 | w_c = 1.5; 30 | w_s = 200; 31 | w_a = 2000; 32 | end 33 | 34 | % 1st data term 35 | vertices_scan = mesh_scan.vertices(garment_scan.vertices_ind, :); 36 | normals_scan = mesh_scan.normals(garment_scan.vertices_ind, :); 37 | normals_smpl = mesh_smpl.normals(garment_smpl.vertices_ind, :); 38 | 39 | nearest_ind_d2m = knnsearch(L, vertices_scan); 40 | error_n = dot(normals_scan.', normals_smpl(nearest_ind_d2m, :).').'; 41 | mask_d2m = find(error_n > 0.8); 42 | nearest_pts_d2m = vertices_scan(mask_d2m, :); 43 | nearest_ind_d2m = nearest_ind_d2m(mask_d2m); 44 | 45 | nearest_ind_m2d = knnsearch(vertices_scan, L); 46 | nearest_pts_m2d = vertices_scan(nearest_ind_m2d, :); 47 | error_n = dot(normals_scan(nearest_ind_m2d, :).', normals_smpl.').'; 48 | mask_m2d = find(error_n > 0.8); 49 | nearest_pts_m2d = nearest_pts_m2d(mask_m2d, :); 50 | 51 | error_d2m = nearest_pts_d2m - L(nearest_ind_d2m, :); 52 | error_m2d = nearest_pts_m2d - L(mask_m2d, :); 53 | 54 | energy = energy + w_g * (... 55 | sum(sum(error_d2m.^2 ./ (error_d2m.^2 + sigma^2))) + ... 56 | sum(sum(error_m2d.^2 ./ (error_m2d.^2 + sigma^2)))); 57 | 58 | % 2nd boundary term 59 | vertices_smpl_boundary = L(garment_smpl.boundary_local_ind, :); 60 | vertices_scan_boundary = vertices_scan(garment_scan.boundary_local_ind, :); 61 | normals_smpl_boundary = mesh_smpl.normals(garment_smpl.boundary_ind, :); 62 | normals_scan_boundary = mesh_scan.normals(garment_scan.boundary_ind, :); 63 | 64 | nearest_ind_d2m = knnsearch(vertices_smpl_boundary, vertices_scan_boundary); 65 | error_n = dot(normals_scan_boundary.', normals_smpl_boundary(nearest_ind_d2m, :).').'; 66 | mask_d2m = find(error_n > 0.5); 67 | nearest_pts_d2m = vertices_scan_boundary(mask_d2m, :); 68 | nearest_ind_d2m = nearest_ind_d2m(mask_d2m); 69 | 70 | nearest_ind_m2d = knnsearch(vertices_scan_boundary, vertices_smpl_boundary); 71 | nearest_pts_m2d = vertices_scan_boundary(nearest_ind_m2d, :); 72 | error_n = dot(normals_scan_boundary(nearest_ind_m2d, :).', normals_smpl_boundary.').'; 73 | mask_m2d = find(error_n > 0.5); 74 | nearest_pts_m2d = nearest_pts_m2d(mask_m2d, :); 75 | 76 | error_d2m = nearest_pts_d2m - vertices_smpl_boundary(nearest_ind_d2m, :); 77 | error_m2d = nearest_pts_m2d - vertices_smpl_boundary(mask_m2d, :); 78 | energy = energy + w_b * (... 79 | sum(sum(error_d2m.^2 ./ (error_d2m.^2 + sigma^2))) + ... 80 | sum(sum(error_m2d.^2 ./ (error_m2d.^2 + sigma^2)))); 81 | 82 | % 3rd coupling term 83 | [beta, ~, trans, scale] = divideParam(smpl_param); 84 | [v_shaped, j_shaped] = calShapedMesh(smpl_model, beta); 85 | [v_posed] = calPosedMesh(smpl_model, theta, v_shaped, j_shaped, 0); 86 | v_posed = repmat(trans, n_smpl, 1) + v_posed * scale; 87 | v_posed_garment = v_posed(garment_smpl.vertices_ind, :); 88 | 89 | error_coupling = norm(L - v_posed_garment, 'fro'); 90 | energy = energy + w_c * error_coupling; 91 | 92 | % 4th laplacian term: 93 | Z = mesh_smpl.adjacency_map( ... 94 | garment_smpl.vertices_ind, garment_smpl.vertices_ind); 95 | vertices_degree = sum(Z, 2); 96 | H = diag(vertices_degree); 97 | I = eye(length(vertices_degree)); 98 | G = I - H \ Z; 99 | product = G * L; 100 | 101 | error_laplacian = norm(product, 'fro'); 102 | energy = energy + w_s * error_laplacian; 103 | 104 | % 5th boundary smoothness term 105 | error_ring = 0; 106 | rings = garment_smpl.rings; 107 | for i = 1 : length(rings) 108 | ring = rings{i}; 109 | vertices = mesh_smpl.vertices(ring, :); 110 | for j = 2 : length(vertices) - 1 111 | err = vertices(j - 1, :) + vertices(j + 1, :) - 2 * vertices(j, :); 112 | error_ring = error_ring + sum(err.^2); 113 | end 114 | end 115 | energy = energy + w_a * error_ring; 116 | 117 | end 118 | 119 | -------------------------------------------------------------------------------- /multi_cloth_alignment/energy_skin.m: -------------------------------------------------------------------------------- 1 | function [energy] = energy_skin(x, mesh_scan, mesh_smpl, garment_smpl, garment_scan, smpl_param) 2 | 3 | global smpl_model; 4 | global n_smpl; 5 | 6 | L = x(25:end, :); 7 | theta = reshape(x(1:24, :), 72, 1)'; 8 | 9 | sigma = 0.1; 10 | energy = 0; 11 | 12 | % w_g = 1000; 13 | % w_b = 20; 14 | % w_c = 1.5; 15 | % w_s = 200; 16 | % w_a = 20; 17 | 18 | w_g = 10; 19 | w_b = 10; 20 | w_c = 3; 21 | w_s = 1000; 22 | w_a = 20; 23 | 24 | % 1st data term 25 | vertices_scan = mesh_scan.vertices(garment_scan.vertices_ind, :); 26 | normals_scan = mesh_scan.normals(garment_scan.vertices_ind, :); 27 | normals_smpl = mesh_smpl.normals(garment_smpl.vertices_ind, :); 28 | 29 | nearest_ind_d2m = knnsearch(L, vertices_scan); 30 | error_n = dot(normals_scan.', normals_smpl(nearest_ind_d2m, :).').'; 31 | mask_d2m = find(error_n > 0.8); 32 | nearest_pts_d2m = vertices_scan(mask_d2m, :); 33 | nearest_ind_d2m = nearest_ind_d2m(mask_d2m); 34 | 35 | nearest_ind_m2d = knnsearch(vertices_scan, L); 36 | nearest_pts_m2d = vertices_scan(nearest_ind_m2d, :); 37 | error_n = dot(normals_scan(nearest_ind_m2d, :).', normals_smpl.').'; 38 | mask_m2d = find(error_n > 0.8); 39 | nearest_pts_m2d = nearest_pts_m2d(mask_m2d, :); 40 | 41 | error_d2m = nearest_pts_d2m - L(nearest_ind_d2m, :); 42 | error_m2d = nearest_pts_m2d - L(mask_m2d, :); 43 | 44 | energy = energy + w_g * (... 45 | sum(sum(error_d2m.^2 ./ (error_d2m.^2 + sigma^2))) + ... 46 | sum(sum(error_m2d.^2 ./ (error_m2d.^2 + sigma^2)))); 47 | 48 | % nearest_ind = knnsearch(L, vertices_scan); 49 | % error_data = vertices_scan - L(nearest_ind, :); 50 | % energy = energy + w_g * sum(sum(error_data.^2 ./ (error_data.^2 + sigma^2))); 51 | 52 | % 2nd boundary term 53 | % vertices_smpl_boundary = L(garment_smpl.boundary_local_ind, :); 54 | % vertices_scan_boundary = vertices_scan(garment_scan.boundary_local_ind, :); 55 | % normals_smpl_boundary = mesh_smpl.normals(garment_smpl.boundary_ind, :); 56 | % normals_scan_boundary = mesh_scan.normals(garment_scan.boundary_ind, :); 57 | % 58 | % nearest_ind_d2m = knnsearch(vertices_smpl_boundary, vertices_scan_boundary); 59 | % error_n = dot(normals_scan_boundary.', normals_smpl_boundary(nearest_ind_d2m, :).').'; 60 | % mask_d2m = find(error_n > 0.5); 61 | % nearest_pts_d2m = vertices_scan_boundary(mask_d2m, :); 62 | % nearest_ind_d2m = nearest_ind_d2m(mask_d2m); 63 | % 64 | % nearest_ind_m2d = knnsearch(vertices_scan_boundary, vertices_smpl_boundary); 65 | % nearest_pts_m2d = vertices_scan_boundary(nearest_ind_m2d, :); 66 | % error_n = dot(normals_scan_boundary(nearest_ind_m2d, :).', normals_smpl_boundary.').'; 67 | % mask_m2d = find(error_n > 0.5); 68 | % nearest_pts_m2d = nearest_pts_m2d(mask_m2d, :); 69 | % 70 | % error_d2m = nearest_pts_d2m - vertices_smpl_boundary(nearest_ind_d2m, :); 71 | % error_m2d = nearest_pts_m2d - vertices_smpl_boundary(mask_m2d, :); 72 | % energy = energy + w_b * (... 73 | % sum(sum(error_d2m.^2 ./ (error_d2m.^2 + sigma^2))) + ... 74 | % sum(sum(error_m2d.^2 ./ (error_m2d.^2 + sigma^2)))); 75 | 76 | % 3rd coupling term 77 | [beta, ~, trans, scale] = divideParam(smpl_param); 78 | [v_shaped, j_shaped] = calShapedMesh(smpl_model, beta); 79 | [v_posed] = calPosedMesh(smpl_model, theta, v_shaped, j_shaped, 0); 80 | v_posed = repmat(trans, n_smpl, 1) + v_posed * scale; 81 | v_posed_garment = v_posed(garment_smpl.vertices_ind, :); 82 | 83 | error_coupling = norm(L - v_posed_garment, 'fro'); 84 | energy = energy + w_c * error_coupling; 85 | 86 | % 4th laplacian term: 87 | % Z = mesh_smpl.adjacency_map( ... 88 | % garment_smpl.vertices_ind, garment_smpl.vertices_ind); 89 | % vertices_degree = sum(Z, 2); 90 | % H = diag(vertices_degree); 91 | % I = eye(length(vertices_degree)); 92 | % G = I - H \ Z; 93 | % product = G * L; 94 | % 95 | % error_laplacian = norm(product, 'fro'); 96 | % energy = energy + w_s * error_laplacian; 97 | 98 | % 5th boundary smoothness term 99 | 100 | 101 | end -------------------------------------------------------------------------------- /multi_cloth_alignment/garment_fitting.m: -------------------------------------------------------------------------------- 1 | function [] = garment_fitting( ... 2 | mesh_scan, label_scan, garments_scan, ... 3 | mesh_smpl, label_smpl, garments_smpl) 4 | 5 | global n_smpl; 6 | global is_first; 7 | global result_dir; 8 | global smpl_param; 9 | global smpl_model; 10 | global mesh_folder; 11 | global mesh_prefix; 12 | global mesh_prefix_last; 13 | 14 | mesh_smpl_shirt = mesh_smpl; 15 | mesh_smpl_pants = mesh_smpl; 16 | 17 | param_path = ['all_results', filesep, 'single_mesh', ... 18 | filesep, mesh_folder, filesep, mesh_prefix]; 19 | param_name = [mesh_prefix, '_fit_param.mat']; 20 | smpl_param = load([param_path, filesep, param_name]); 21 | smpl_param = smpl_param.param; 22 | 23 | [beta, ~, trans, scale] = divideParam(smpl_param); 24 | [v_shaped, j_shaped] = calShapedMesh(smpl_model, beta); 25 | 26 | if is_first == 1 27 | pose_shirt = smpl_param(11:82); 28 | pose_pants = smpl_param(11:82); 29 | 30 | v_posed = calPosedMesh(smpl_model, ... 31 | smpl_param(11:82), v_shaped, j_shaped, 0); 32 | v_posed = repmat(trans, n_smpl, 1) + v_posed * scale; 33 | 34 | mesh_smpl_shirt.vertices = v_posed; 35 | mesh_smpl_pants.vertices = v_posed; 36 | else 37 | param_path = ['all_results', filesep, 'multi_cloth', ... 38 | filesep, mesh_folder, filesep, mesh_prefix_last]; 39 | param_name_shirt = [mesh_prefix_last, '_pose_shirt.mat']; 40 | param_name_pants = [mesh_prefix_last, '_pose_pants.mat']; 41 | pose_shirt = load([param_path, filesep, param_name_shirt]); 42 | pose_pants = load([param_path, filesep, param_name_pants]); 43 | pose_shirt = pose_shirt.theta_shirt; 44 | pose_pants = pose_pants.theta_pants; 45 | 46 | v_posed_shirt = calPosedMesh(smpl_model, ... 47 | pose_shirt, v_shaped, j_shaped, 0); 48 | v_posed_shirt = repmat(trans, n_smpl, 1) + v_posed_shirt * scale; 49 | 50 | v_posed_pants = calPosedMesh(smpl_model, ... 51 | pose_pants, v_shaped, j_shaped, 0); 52 | v_posed_pants = repmat(trans, n_smpl, 1) + v_posed_pants * scale; 53 | 54 | mesh_smpl_shirt.vertices = v_posed_shirt; 55 | mesh_smpl_pants.vertices = v_posed_pants; 56 | end 57 | 58 | mesh_exporter([result_dir, filesep, mesh_prefix, ... 59 | '_seg_smpl_shirt.obj'], mesh_smpl_shirt, true); 60 | mesh_exporter([result_dir, filesep, mesh_prefix, ... 61 | '_seg_smpl_pants.obj'], mesh_smpl_pants, true); 62 | 63 | % for skin 64 | % [vertices_skin, pose_skin] = align_skin(garments_scan.skin, garments_smpl.skin, ... 65 | % mesh_scan, mesh_smpl, label_smpl); 66 | % save([result_dir, filesep, mesh_prefix, '_pose_skin.mat'], 'pose_skin'); 67 | 68 | % for shirt 69 | [vertices_shirt, theta_shirt] = align_garment(garments_scan.shirt, garments_smpl.shirt, ... 70 | mesh_scan, mesh_smpl_shirt, label_smpl, pose_shirt, 'shirt', 5); 71 | save([result_dir, filesep, mesh_prefix, '_pose_shirt.mat'], 'theta_shirt'); 72 | 73 | % for pants 74 | [vertices_pants, theta_pants] = align_garment(garments_scan.pants, garments_smpl.pants, ... 75 | mesh_scan, mesh_smpl_pants, label_smpl, pose_pants, 'pants', 10); 76 | save([result_dir, filesep, mesh_prefix, '_pose_pants.mat'], 'theta_pants'); 77 | 78 | % combine all garments to one; 79 | % m_comined = mesh_smpl; 80 | % m_comined.vertices(garments_smpl.shirt.vertices_ind, :) = vertices_shirt; 81 | % m_comined.vertices(garments_smpl.pants.vertices_ind, :) = vertices_pants; 82 | % mesh_exporter([result_dir, filesep, mesh_prefix, ... 83 | % '_combined_full.obj'], m_comined, true); 84 | 85 | % for full mesh 86 | % [vertices_all, pose] = align_cloth(garments_scan, garments_smpl, ... 87 | % mesh_scan, mesh_combined, label_smpl); 88 | % save([result_dir, filesep, mesh_prefix, '_pose_pants.mat'], 'pose'); 89 | 90 | end -------------------------------------------------------------------------------- /multi_cloth_alignment/garment_ring.m: -------------------------------------------------------------------------------- 1 | function [rings_shirt, rings_pants] = garment_ring(garments, mesh, label) 2 | 3 | adj_map = mesh.adjacency_map; 4 | 5 | %% for shirt 6 | boundary = garments.shirt.boundary_ind; 7 | 8 | [ring1, boundary] = build_ring(boundary, adj_map, label); 9 | [ring2, boundary] = build_ring(boundary, adj_map, label); 10 | [ring3, boundary] = build_ring(boundary, adj_map, label); 11 | [ring4, boundary] = build_ring(boundary, adj_map, label); 12 | 13 | total_length = length(ring1) + length(ring2) + length(ring3) + length(ring4); 14 | if total_length ~= length(garments.shirt.boundary_ind) + 8 || ~isempty(boundary) 15 | disp("[warning] somthing wrong when finding garment boundary rings."); 16 | end 17 | 18 | rings_shirt = {ring1, ring2, ring3, ring4}; 19 | 20 | %% for pants 21 | boundary = garments.pants.boundary_ind; 22 | 23 | [ring1, boundary] = build_ring(boundary, adj_map, label); 24 | [ring2, boundary] = build_ring(boundary, adj_map, label); 25 | [ring3, boundary] = build_ring(boundary, adj_map, label); 26 | 27 | total_length = length(ring1) + length(ring2) + length(ring3); 28 | if total_length ~= length(garments.pants.boundary_ind) + 6 || ~isempty(boundary) 29 | disp("[warning] somthing wrong when finding garment boundary rings."); 30 | end 31 | 32 | rings_pants = {ring1, ring2, ring3}; 33 | 34 | end 35 | 36 | -------------------------------------------------------------------------------- /multi_cloth_alignment/initial_smpl.m: -------------------------------------------------------------------------------- 1 | function [label_smpl, garments_smpl, mesh_smpl] = initial_smpl(mesh_folder) 2 | 3 | global n_smpl; 4 | global result_dir_base; 5 | 6 | % load SMPL label from 1st frame 7 | label_smpl = load(['all_results', filesep, 'segmentation', filesep, ... 8 | mesh_folder, filesep, 'person_wise_label_smpl.mat']); 9 | label_smpl = label_smpl.seg_smpl; 10 | 11 | n_smpl = length(label_smpl); 12 | 13 | % load base smpl mesh 14 | mesh_smpl = mesh_parser('smpl_base_m.obj', 'smpl_model'); 15 | mesh_smpl.colors = render_labels(label_smpl); 16 | 17 | % get the smpl model garments 18 | [garments_smpl, mesh_garments_smpl] = extract_garments(mesh_smpl, label_smpl); 19 | 20 | % get the smpl garments ring 21 | [rings_shirt, rings_pants] = garment_ring(garments_smpl, mesh_smpl, label_smpl); 22 | garments_smpl.shirt.rings = rings_shirt; 23 | garments_smpl.pants.rings = rings_pants; 24 | 25 | mesh_exporter([result_dir_base, filesep, 'smpl_garments.obj'], ... 26 | mesh_garments_smpl, true); 27 | save([result_dir_base, filesep, 'smpl_garments.mat'], 'garments_smpl'); 28 | 29 | end 30 | -------------------------------------------------------------------------------- /publish/publish_results.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import argparse 4 | 5 | ALL_RESULTS_ORIGIN = "../all_results" 6 | ALL_RESULTS_PUBLISH = "./all_results" 7 | 8 | TYPES = ["single_mesh", "segmentation", "multi_cloth"] 9 | 10 | 11 | def publish_segmentation(frames_folder, result_folder_origin, result_folder_publish): 12 | for frame_result in frames_folder: 13 | path_origin = os.path.join(result_folder_origin, frame_result) 14 | if not os.path.isdir(path_origin): 15 | continue 16 | print("[info] publishing", frame_result) 17 | 18 | path_publish = os.path.join(result_folder_publish, frame_result) 19 | os.mkdir(path_publish) 20 | 21 | for _, _, files in os.walk(path_origin): 22 | for result_file in files: 23 | if result_file.endswith("_seg_scan.obj") or result_file.endswith("_seg_smpl.obj") or \ 24 | result_file.endswith("_colored.obj"): 25 | shutil.copy(os.path.join(path_origin, result_file), os.path.join(path_publish, result_file)) 26 | 27 | 28 | def publish_multi_cloth(frames_folder, result_folder_origin, result_folder_publish, scan_name): 29 | path_shirt = os.path.join(result_folder_publish, scan_name + '_shirt') 30 | os.mkdir(path_shirt) 31 | path_pants = os.path.join(result_folder_publish, scan_name + '_pants') 32 | os.mkdir(path_pants) 33 | 34 | for frame_result in frames_folder: 35 | path_origin = os.path.join(result_folder_origin, frame_result) 36 | if not os.path.isdir(path_origin): 37 | continue 38 | print("[info] publishing", frame_result) 39 | 40 | for _, _, files in os.walk(path_origin): 41 | for result_file in files: 42 | if result_file.endswith("_part_shirt.obj"): 43 | shutil.copy(os.path.join(path_origin, result_file), os.path.join(path_shirt, result_file)) 44 | elif result_file.endswith("_part_pants.obj"): 45 | shutil.copy(os.path.join(path_origin, result_file), os.path.join(path_pants, result_file)) 46 | 47 | 48 | def main(): 49 | # parse arguments 50 | parser = argparse.ArgumentParser() 51 | parser.add_argument("--type", dest="result_type", 52 | help="result type, [single_mesh|segmentation|multi_cloth].") 53 | parser.add_argument("--scan", dest="scan_name", help="scan sequences folder name.") 54 | parsed_args = parser.parse_args() 55 | 56 | result_type = parsed_args.result_type 57 | scan_name = parsed_args.scan_name 58 | 59 | if result_type not in TYPES: 60 | print("[error] invalid result type, [single_mesh|segmentation|multi_cloth].") 61 | print("[error] aborting ...") 62 | exit(-1) 63 | 64 | # origin results 65 | all_results_origin = os.path.join(os.getcwd(), ALL_RESULTS_ORIGIN) 66 | all_results_origin = os.path.realpath(all_results_origin) 67 | 68 | if not os.path.exists(all_results_origin): 69 | print("[error] please run 'publish_results.py' in 'publish' folder.") 70 | print("[error] aborting ...") 71 | exit(-1) 72 | 73 | results_folder_origin = os.path.join(all_results_origin, result_type) 74 | results_folder_origin = os.path.join(results_folder_origin, scan_name) 75 | 76 | if not os.path.exists(results_folder_origin): 77 | print("[error] no sequences", scan_name, "found.") 78 | print("[error] aborting ...") 79 | exit(-1) 80 | 81 | # published results folder 82 | all_results_publish = os.path.join(os.getcwd(), ALL_RESULTS_PUBLISH) 83 | all_results_publish = os.path.realpath(all_results_publish) 84 | 85 | if not os.path.exists(all_results_publish): 86 | os.mkdir(all_results_publish) 87 | 88 | results_folder_publish = os.path.join(all_results_publish, result_type) 89 | if not os.path.exists(results_folder_publish): 90 | os.mkdir(results_folder_publish) 91 | 92 | results_folder_publish = os.path.join(results_folder_publish, scan_name) 93 | if os.path.exists(results_folder_publish): 94 | shutil.rmtree(results_folder_publish) 95 | os.mkdir(results_folder_publish) 96 | 97 | frames_folder = os.listdir(results_folder_origin) 98 | 99 | if result_type == "segmentation": 100 | publish_segmentation(frames_folder, results_folder_origin, results_folder_publish) 101 | elif result_type == "multi_cloth": 102 | publish_multi_cloth(frames_folder, results_folder_origin, results_folder_publish, scan_name) 103 | 104 | 105 | if __name__ == "__main__": 106 | main() 107 | -------------------------------------------------------------------------------- /python/align_garment.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from common import calculation 3 | from common import importer 4 | from scipy.io import savemat 5 | from scipy.optimize import minimize 6 | from energy_garments import energy_garments 7 | 8 | 9 | def main(): 10 | # import data from MATLAB 11 | mesh_scan, mesh_smpl = \ 12 | importer.import_mesh() 13 | garment_scan, garment_smpl = \ 14 | importer.import_garments() 15 | L, theta = \ 16 | importer.import_params() 17 | is_first, n_smpl = \ 18 | importer.import_extra() 19 | smpl_param = \ 20 | importer.import_smpl_param() 21 | smpl_model = \ 22 | importer.import_smpl_model(gender="male") 23 | 24 | mesh_smpl_temp = mesh_smpl 25 | mesh_smpl_temp.vertices[garment_smpl.vertices_ind - 1, :] = L 26 | mesh_smpl_temp.normals = calculation.cal_normals(mesh_smpl_temp.faces, mesh_smpl_temp.vertices) 27 | 28 | # optimization 29 | param = np.hstack((np.reshape(theta, [1, -1]), np.reshape(L, [1, -1]))) 30 | args = (mesh_scan, mesh_smpl_temp, garment_scan, 31 | garment_smpl, smpl_param, smpl_model, is_first) 32 | 33 | param_opt = minimize(fun=energy_garments, x0=param, 34 | args=args, method='BFGS', options={'disp': True, 'maxiter': 10}) 35 | 36 | opt_param = param_opt.x 37 | opt_theta = np.reshape(opt_param[0, :72], [-1, 3]) 38 | opt_L = np.reshape(opt_param[0, 72:], [-1, 3]) 39 | 40 | savemat(file_name="data/opt_params", mdict={"L": opt_L, "theta": opt_theta}) 41 | 42 | # energy = energy_garments(param, args) 43 | # print(energy) 44 | 45 | 46 | if __name__ == '__main__': 47 | main() 48 | -------------------------------------------------------------------------------- /python/common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jizhu1023/ClothCap/5ca0c8a474a3a6eec053db8ebbd0359a393cb4b2/python/common/__init__.py -------------------------------------------------------------------------------- /python/common/calculation.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | import numpy.linalg as lg 4 | 5 | 6 | def divide_param(smpl_param): 7 | beta = smpl_param[0:10] 8 | pose = smpl_param[10:82] 9 | trans = smpl_param[82:85] 10 | scale = smpl_param[85] 11 | return beta, pose, trans, scale 12 | 13 | 14 | def cal_normals(faces, vertices): 15 | e1 = vertices[faces[:, 1] - 1, :] - vertices[faces[:, 0] - 1, :] 16 | e2 = vertices[faces[:, 2] - 1, :] - vertices[faces[:, 1] - 1, :] 17 | 18 | cross_product = np.cross(e1, e2) 19 | normal = lg.norm(cross_product, axis=1) 20 | cross_product = cross_product / np.transpose(np.tile(normal, [3, 1])) 21 | 22 | normals = np.zeros(vertices.shape) 23 | normals_count = np.zeros([vertices.shape[0], 1]) 24 | 25 | for i in range(cross_product.shape[0]): 26 | for j in range(3): 27 | ind = faces[i, j] - 1 28 | normals[ind, :] = normals[ind, :] + cross_product[i, :] 29 | normals_count[ind, 0] = normals_count[ind, 0] + 1 30 | 31 | normals = normals / np.tile(normals_count, [1, 3]) 32 | 33 | return normals 34 | 35 | 36 | def cal_shaped_mesh(model, beta): 37 | shape_coeffs = model.shapedirs.x 38 | 39 | v_offset = np.zeros(shape_coeffs.shape[0:2]) 40 | for i in range(beta.shape[0]): 41 | v_offset = v_offset + beta[i] * shape_coeffs[:, :, i] 42 | 43 | v_shaped = model.v_template + v_offset 44 | j_shaped = np.dot(model.J_regressor.toarray(), v_shaped) 45 | 46 | return v_shaped, j_shaped 47 | 48 | 49 | def cal_posed_mesh(model, theta, v_shaped, j_shaped, blend_pose): 50 | joints_num = model.J.shape[0] 51 | pose = np.reshape(theta, [3, -1], order='F') 52 | 53 | if blend_pose == 1: 54 | pose_rodrigues = np.zeros([(joints_num - 1) * 9, 1]) 55 | for i in range(1, joints_num): 56 | rotate = np.zeros([3, 3]) 57 | cv2.Rodrigues(pose[:, i], rotate) 58 | rotate = rotate - np.eye(3, 3) 59 | pose_rodrigues[(i - 1) * 9:i * 9, :] = np.reshape(rotate, [-1, 1]) 60 | 61 | pose_coeffs = model.posedirs 62 | v_offset = np.zeros(pose_coeffs.shape[0:2]) 63 | for j in range((joints_num - 1) * 9): 64 | v_offset = v_offset + pose_rodrigues[j, :] * pose_coeffs[:, :, j] 65 | v_posed = v_shaped + v_offset 66 | else: 67 | v_posed = v_shaped 68 | 69 | pose_mat = np.zeros([3, 3, joints_num]) 70 | for i in range(joints_num): 71 | rotate = np.zeros([3, 3]) 72 | cv2.Rodrigues(pose[:, i], rotate) 73 | pose_mat[:, :, i] = rotate 74 | 75 | id_to_col = model.kintree_table[1, :] 76 | parent = id_to_col[model.kintree_table[0, 1:id_to_col.shape[0]]] 77 | 78 | A = np.zeros([4, 4, joints_num]) 79 | A[:, :, 0] = with_zeros(pose_mat[:, :, 0], j_shaped[0, :]) 80 | 81 | for i in range(1, joints_num): 82 | parent_index = parent[i - 1] 83 | A[:, :, i] = np.dot(A[:, :, parent_index], with_zeros( 84 | pose_mat[:, :, i], j_shaped[i, :] - j_shaped[parent_index, :])) 85 | 86 | A_global = A.copy() 87 | 88 | for i in range(joints_num): 89 | A[:, :, i] = A[:, :, i] - pack(np.dot(A[:, :, i], extend(j_shaped[i, :], 0))) 90 | 91 | j_posed = np.zeros([joints_num, 3]) 92 | for i in range(joints_num): 93 | j_posed[i, :] = A_global[0:3, 3, i] 94 | 95 | A_col_wise = np.reshape(A, [-1, joints_num], order='F') 96 | A_per_vert = np.dot(A_col_wise, np.transpose(model.weights)) 97 | A_per_vert = np.reshape(A_per_vert, [4, 4, -1], order='F') 98 | 99 | for i in range(A_per_vert.shape[2]): 100 | new_pos = np.dot(A_per_vert[:, :, i], extend(v_posed[i, :], 1)) 101 | v_posed[i, :] = new_pos[0:3] 102 | 103 | return v_posed 104 | 105 | 106 | def pack(t): 107 | Rt = np.zeros([4, 4]) 108 | Rt[:, 3] = t 109 | return Rt 110 | 111 | 112 | def with_zeros(R, t): 113 | Rt = np.zeros([4, 4]) 114 | Rt[0:3, 0:3] = R 115 | Rt[0:3, 3] = t 116 | Rt[3, 3] = 1 117 | return Rt 118 | 119 | 120 | def extend(t, val): 121 | t_e = np.zeros([4, ]) 122 | t_e[0:3] = t 123 | t_e[3] = val 124 | return t_e 125 | -------------------------------------------------------------------------------- /python/common/importer.py: -------------------------------------------------------------------------------- 1 | import scipy.io as scio 2 | 3 | 4 | def import_mesh(): 5 | file_name = 'data/multi_cloth_opt_meshes.mat' 6 | data = scio.loadmat(file_name, squeeze_me=True, struct_as_record=False) 7 | 8 | mesh_scan = data['m_scan'] 9 | mesh_smpl = data['m_smpl'] 10 | return mesh_scan, mesh_smpl 11 | 12 | 13 | def import_extra(): 14 | file_name = 'data/multi_cloth_opt_extra.mat' 15 | data = scio.loadmat(file_name, squeeze_me=True, struct_as_record=False) 16 | 17 | is_first = data['is_first'] 18 | n_smpl = data['n_smpl'] 19 | return is_first, n_smpl 20 | 21 | 22 | def import_garments(): 23 | file_name = 'data/multi_cloth_opt_garments.mat' 24 | data = scio.loadmat(file_name, squeeze_me=True, struct_as_record=False) 25 | 26 | garment_scan = data['garment_scan'] 27 | garment_smpl = data['garment_smpl'] 28 | return garment_scan, garment_smpl 29 | 30 | 31 | def import_params(): 32 | file_name = 'data/multi_cloth_opt_params.mat' 33 | data = scio.loadmat(file_name, squeeze_me=True, struct_as_record=False) 34 | 35 | L = data['L'] 36 | theta = data['theta'] 37 | return L, theta 38 | 39 | 40 | def import_smpl_param(): 41 | file_name = 'data/multi_cloth_opt_smpl_param.mat' 42 | data = scio.loadmat(file_name, squeeze_me=True, struct_as_record=False) 43 | 44 | smpl_param = data['smpl_param'] 45 | return smpl_param 46 | 47 | 48 | def import_smpl_model(gender): 49 | if gender == "male": 50 | file_name = '../smpl_model/smpl_m.mat' 51 | else: 52 | file_name = '../smpl_model/smpl_f.mat' 53 | data = scio.loadmat(file_name, squeeze_me=True, struct_as_record=False) 54 | 55 | smpl_model = data['model'] 56 | return smpl_model 57 | -------------------------------------------------------------------------------- /python/energy_garments.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import numpy.linalg as lg 3 | from common import calculation 4 | from sklearn.neighbors import NearestNeighbors 5 | 6 | 7 | def energy_garments(param, *args): 8 | 9 | L = np.reshape(param[72:], [-1, 3]) 10 | theta = np.reshape(param[:72], [-1, 3]) 11 | 12 | mesh_scan = args[0] 13 | mesh_smpl = args[1] 14 | garment_scan = args[2] 15 | garment_smpl = args[3] 16 | smpl_param = args[4] 17 | smpl_model = args[5] 18 | is_first = args[6] 19 | 20 | sigma = 0.2 21 | energy = 0 22 | 23 | if is_first == 1: 24 | w_g = 1 25 | w_b = 10 26 | w_c = 1.5 27 | w_s = 1000 28 | w_a = 2000 29 | else: 30 | w_g = 1 31 | w_b = 10 32 | w_c = 1.5 33 | w_s = 200 34 | w_a = 2000 35 | 36 | # 1st data term 37 | normals_scan = mesh_scan.normals[garment_scan.vertices_ind - 1, :] 38 | normals_smpl = mesh_smpl.normals[garment_smpl.vertices_ind - 1, :] 39 | vertices_scan = mesh_scan.vertices[garment_scan.vertices_ind - 1, :] 40 | 41 | _, nearest_ind_d2m = NearestNeighbors( 42 | n_neighbors=1, algorithm='kd_tree').fit(L).kneighbors(vertices_scan) 43 | nearest_ind_d2m = nearest_ind_d2m.flatten() 44 | error_n = np.sum(normals_scan * normals_smpl[nearest_ind_d2m, :], axis=1) 45 | mask_d2m = np.where(error_n > 0.8) 46 | nearest_pts_d2m = vertices_scan[mask_d2m[0], :] 47 | nearest_ind_d2m = nearest_ind_d2m[mask_d2m[0]] 48 | 49 | _, nearest_ind_m2d = NearestNeighbors( 50 | n_neighbors=1, algorithm='kd_tree').fit(vertices_scan).kneighbors(L) 51 | nearest_ind_m2d = nearest_ind_m2d.flatten() 52 | nearest_pts_m2d = vertices_scan[nearest_ind_m2d, :] 53 | error_n = np.sum(normals_scan[nearest_ind_m2d, :] * normals_smpl, axis=1) 54 | mask_m2d = np.where(error_n > 0.8) 55 | nearest_pts_m2d = nearest_pts_m2d[mask_m2d[0], :] 56 | 57 | error_d2m = nearest_pts_d2m - L[nearest_ind_d2m, :] 58 | error_m2d = nearest_pts_m2d - L[mask_m2d[0], :] 59 | 60 | energy = energy + w_g * ( 61 | np.sum(error_d2m * error_d2m / (error_d2m * error_d2m + sigma * sigma)) + 62 | np.sum(error_m2d * error_m2d / (error_m2d * error_m2d + sigma * sigma))) 63 | 64 | # 2nd boundary term 65 | vertices_smpl_boundary = L[garment_smpl.boundary_local_ind - 1, :] 66 | vertices_scan_boundary = vertices_scan[garment_scan.boundary_local_ind - 1, :] 67 | normals_smpl_boundary = mesh_smpl.normals[garment_smpl.boundary_ind - 1, :] 68 | normals_scan_boundary = mesh_scan.normals[garment_scan.boundary_ind - 1, :] 69 | 70 | _, nearest_ind_d2m = NearestNeighbors( 71 | n_neighbors=1, algorithm='kd_tree').fit(vertices_smpl_boundary).kneighbors(vertices_scan_boundary) 72 | nearest_ind_d2m = nearest_ind_d2m.flatten() 73 | error_n = np.sum(normals_scan_boundary * normals_smpl_boundary[nearest_ind_d2m, :], axis=1) 74 | mask_d2m = np.where(error_n > 0.5) 75 | nearest_pts_d2m = vertices_scan_boundary[mask_d2m[0], :] 76 | nearest_ind_d2m = nearest_ind_d2m[mask_d2m[0]] 77 | 78 | _, nearest_ind_m2d = NearestNeighbors( 79 | n_neighbors=1, algorithm='kd_tree').fit(vertices_scan_boundary).kneighbors(vertices_smpl_boundary) 80 | nearest_ind_m2d = nearest_ind_m2d.flatten() 81 | nearest_pts_m2d = vertices_scan_boundary[nearest_ind_m2d, :] 82 | error_n = np.sum(normals_scan_boundary[nearest_ind_m2d, :] * normals_smpl_boundary, axis=1) 83 | mask_m2d = np.where(error_n > 0.5) 84 | nearest_pts_m2d = nearest_pts_m2d[mask_m2d[0], :] 85 | 86 | error_d2m = nearest_pts_d2m - vertices_smpl_boundary[nearest_ind_d2m, :] 87 | error_m2d = nearest_pts_m2d - vertices_smpl_boundary[mask_m2d[0], :] 88 | 89 | energy = energy + w_b * ( 90 | np.sum(error_d2m * error_d2m / (error_d2m * error_d2m + sigma * sigma)) + 91 | np.sum(error_m2d * error_m2d / (error_m2d * error_m2d + sigma * sigma))) 92 | 93 | # 3rd coupling term 94 | n_smpl = smpl_model.v_template.shape[0] 95 | beta, _, trans, scale = calculation.divide_param(smpl_param) 96 | v_shaped, j_shaped = calculation.cal_shaped_mesh(smpl_model, beta) 97 | v_posed = calculation.cal_posed_mesh(smpl_model, theta, v_shaped, j_shaped, 0) 98 | v_posed = np.tile(trans, [n_smpl, 1]) + v_posed * scale 99 | v_posed_garment = v_posed[garment_smpl.vertices_ind - 1, :] 100 | 101 | error_coupling = lg.norm(L - v_posed_garment, 'fro') 102 | energy = energy + w_c * error_coupling 103 | 104 | # 4th laplacian term 105 | rows = np.transpose(np.tile(garment_smpl.vertices_ind - 1, [garment_smpl.vertices_ind.shape[0], 1])) 106 | cols = np.tile(garment_smpl.vertices_ind - 1, [garment_smpl.vertices_ind.shape[0], 1]) 107 | Z = mesh_smpl.adjacency_map[rows, cols] 108 | vertices_degree = np.sum(Z, axis=1) 109 | H = np.diag(vertices_degree) 110 | I = np.eye(vertices_degree.shape[0], vertices_degree.shape[0]) 111 | G = I - np.dot(lg.inv(H), Z) 112 | product = np.dot(G, L) 113 | 114 | error_laplacian = lg.norm(product, 'fro') 115 | energy = energy + w_s * error_laplacian 116 | 117 | # 5th boundary smoothness term 118 | error_ring = 0 119 | rings = garment_smpl.rings 120 | for i in range(rings.shape[0]): 121 | ring = rings[i] - 1 122 | vertices = mesh_smpl.vertices[ring, :] 123 | for j in range(1, vertices.shape[0] - 1): 124 | err = vertices[j - 1, :] + vertices[j + 1, :] - 2 * vertices[j, :] 125 | error_ring = error_ring + np.dot(err, err) 126 | 127 | energy = energy + w_a * error_ring 128 | 129 | return energy 130 | -------------------------------------------------------------------------------- /sdf_extractor/cplusplus/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(sdf_extractor) 3 | 4 | set(CMAKE_CXX_STANDARD 14) 5 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") 6 | 7 | # CGAL and its components 8 | find_package(CGAL REQUIRED COMPONENTS) 9 | 10 | # include helper file 11 | include(${CGAL_USE_FILE}) 12 | 13 | find_package(OpenMesh REQUIRED) 14 | include_directories(${OPENMESH_INCLUDE_DIRS}) 15 | link_libraries(${OPENMESH_LIBRARIES}) 16 | 17 | include(CGAL_CreateSingleSourceCGALProgram) 18 | create_single_source_cgal_program("sdf_extractor.cpp") 19 | target_link_libraries(sdf_extractor ${OPENMESH_LIBRARIES}) 20 | -------------------------------------------------------------------------------- /sdf_extractor/cplusplus/cmake/FindOpenMesh.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Try to find OPENMESH 3 | # Once done this will define 4 | # 5 | # OPENMESH_FOUND - system has OPENMESH 6 | # OPENMESH_INCLUDE_DIRS - the OPENMESH include directories 7 | # OPENMESH_LIBRARIES - Link these to use OPENMESH 8 | # OPENMESH_LIBRARY_DIR - directory where the libraries are included 9 | # 10 | # Copyright 2015 Computer Graphics Group, RWTH Aachen University 11 | # Authors: Jan Möbius 12 | # Hans-Christian Ebke 13 | # 14 | # 15 | #=========================================================================== 16 | # 17 | # OpenMesh 18 | # Copyright (c) 2001-2015, RWTH-Aachen University 19 | # Department of Computer Graphics and Multimedia 20 | # All rights reserved. 21 | # www.openmesh.org 22 | # 23 | #--------------------------------------------------------------------------- 24 | # This file is part of OpenMesh. 25 | #--------------------------------------------------------------------------- 26 | # 27 | # Redistribution and use in source and binary forms, with or without 28 | # modification, are permitted provided that the following conditions 29 | # are met: 30 | # 31 | # 1. Redistributions of source code must retain the above copyright notice, 32 | # this list of conditions and the following disclaimer. 33 | # 34 | # 2. Redistributions in binary form must reproduce the above copyright 35 | # notice, this list of conditions and the following disclaimer in the 36 | # documentation and/or other materials provided with the distribution. 37 | # 38 | # 3. Neither the name of the copyright holder nor the names of its 39 | # contributors may be used to endorse or promote products derived from 40 | # this software without specific prior written permission. 41 | # 42 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 43 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 44 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 45 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 46 | # OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 47 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 48 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 49 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 50 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 51 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 52 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 | # 54 | #=========================================================================== 55 | 56 | cmake_minimum_required(VERSION 2.8.9) 57 | 58 | #if already found via finder or simulated finder in openmesh CMakeLists.txt, skip the search 59 | IF (NOT OPENMESH_FOUND) 60 | SET (SEARCH_PATHS 61 | /usr/local/ 62 | /usr/ 63 | "${CMAKE_SOURCE_DIR}/OpenMesh/src/OpenMesh" 64 | "${CMAKE_SOURCE_DIR}/libs_required/OpenMesh/src/OpenMesh" 65 | "${CMAKE_SOURCE_DIR}/../OpenMesh/src/OpenMesh" 66 | "C:/Program Files/OpenMesh 6.3" 67 | "C:/Program Files/OpenMesh 6.2" 68 | "C:/Program Files/OpenMesh 6.1" 69 | "C:/Program Files/OpenMesh 6.0" 70 | "C:/Program Files/OpenMesh 5.2" 71 | "C:/Program Files/OpenMesh 5.1" 72 | "C:/Program Files/OpenMesh 5.0" 73 | "C:/Program Files/OpenMesh 4.2" 74 | "C:/Program Files/OpenMesh 4.1" 75 | "C:/Program Files/OpenMesh 4.0" 76 | "C:/Program Files/OpenMesh 3.4" 77 | "C:/Program Files/OpenMesh 3.3" 78 | "C:/Program Files/OpenMesh 3.2" 79 | "C:/Program Files/OpenMesh 3.1" 80 | "C:/Program Files/OpenMesh 3.0" 81 | "C:/Program Files/OpenMesh 2.4.1" 82 | "C:/Program Files/OpenMesh 2.4" 83 | "C:/Program Files/OpenMesh 2.0/include" 84 | "C:/libs/OpenMesh 6.3" 85 | "C:/libs/OpenMesh 6.2" 86 | "C:/libs/OpenMesh 6.1" 87 | "C:/libs/OpenMesh 6.0" 88 | "C:/libs/OpenMesh 5.2" 89 | "C:/libs/OpenMesh 5.1" 90 | "C:/libs/OpenMesh 5.0" 91 | "C:/libs/OpenMesh 4.2" 92 | "C:/libs/OpenMesh 4.1" 93 | "C:/libs/OpenMesh 4.0" 94 | "C:/libs/OpenMesh 3.4" 95 | "C:/libs/OpenMesh 3.3" 96 | "C:/libs/OpenMesh 3.2" 97 | "C:/libs/OpenMesh 3.1" 98 | "C:/libs/OpenMesh 3.0" 99 | "C:/libs/OpenMesh 2.4.1" 100 | "C:/libs/OpenMesh 2.4" 101 | "${OPENMESH_LIBRARY_DIR}" 102 | ) 103 | 104 | FIND_PATH (OPENMESH_INCLUDE_DIR OpenMesh/Core/Mesh/PolyMeshT.hh 105 | PATHS ${SEARCH_PATHS} 106 | PATH_SUFFIXES include) 107 | 108 | FIND_LIBRARY(OPENMESH_CORE_LIBRARY_RELEASE NAMES OpenMeshCore 109 | PATHS ${SEARCH_PATHS} 110 | PATH_SUFFIXES lib lib64) 111 | 112 | FIND_LIBRARY(OPENMESH_CORE_LIBRARY_DEBUG NAMES OpenMeshCored 113 | PATHS ${SEARCH_PATHS} 114 | PATH_SUFFIXES lib lib64) 115 | 116 | FIND_LIBRARY(OPENMESH_TOOLS_LIBRARY_RELEASE NAMES OpenMeshTools 117 | PATHS ${SEARCH_PATHS} 118 | PATH_SUFFIXES lib lib64) 119 | 120 | FIND_LIBRARY(OPENMESH_TOOLS_LIBRARY_DEBUG NAMES OpenMeshToolsd 121 | PATHS ${SEARCH_PATHS} 122 | PATH_SUFFIXES lib lib64) 123 | 124 | #select configuration depending on platform (optimized... on windows) 125 | include(SelectLibraryConfigurations) 126 | select_library_configurations( OPENMESH_TOOLS ) 127 | select_library_configurations( OPENMESH_CORE ) 128 | 129 | set(OPENMESH_LIBRARIES ${OPENMESH_CORE_LIBRARY} ${OPENMESH_TOOLS_LIBRARY} ) 130 | set(OPENMESH_INCLUDE_DIRS ${OPENMESH_INCLUDE_DIR} ) 131 | 132 | #checks, if OPENMESH was found and sets OPENMESH_FOUND if so 133 | include(FindPackageHandleStandardArgs) 134 | find_package_handle_standard_args(OpenMesh DEFAULT_MSG 135 | OPENMESH_CORE_LIBRARY OPENMESH_TOOLS_LIBRARY OPENMESH_INCLUDE_DIR) 136 | 137 | #sets the library dir 138 | if ( OPENMESH_CORE_LIBRARY_RELEASE ) 139 | get_filename_component(_OPENMESH_LIBRARY_DIR ${OPENMESH_CORE_LIBRARY_RELEASE} PATH) 140 | else( OPENMESH_CORE_LIBRARY_RELEASE ) 141 | get_filename_component(_OPENMESH_LIBRARY_DIR ${OPENMESH_CORE_LIBRARY_DEBUG} PATH) 142 | endif( OPENMESH_CORE_LIBRARY_RELEASE ) 143 | set (OPENMESH_LIBRARY_DIR "${_OPENMESH_LIBRARY_DIR}" CACHE PATH "The directory where the OpenMesh libraries can be found.") 144 | 145 | 146 | mark_as_advanced(OPENMESH_INCLUDE_DIR OPENMESH_CORE_LIBRARY_RELEASE OPENMESH_CORE_LIBRARY_DEBUG OPENMESH_TOOLS_LIBRARY_RELEASE OPENMESH_TOOLS_LIBRARY_DEBUG OPENMESH_LIBRARY_DIR) 147 | endif() 148 | -------------------------------------------------------------------------------- /sdf_extractor/cplusplus/sdf_extractor.cpp: -------------------------------------------------------------------------------- 1 | #define CGAL_BGL_TESTSUITE 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; 15 | typedef OpenMesh::PolyMesh_ArrayKernelT<> Mesh; 16 | 17 | typedef boost::graph_traits::face_descriptor face_descriptor; 18 | typedef boost::graph_traits::face_iterator face_iterator; 19 | 20 | int main(int argc, char** argv) 21 | { 22 | if (argc != 2) 23 | { 24 | std::cerr << "[error] wrong number of arguments, aborting ..." << std::endl; 25 | exit(EXIT_FAILURE); 26 | } 27 | 28 | std::string mesh_filename = std::string(argv[1]); 29 | std::cout << "[info] extracting sdf feature: " << mesh_filename << std::endl; 30 | 31 | Mesh mesh; 32 | OpenMesh::IO::read_mesh(mesh, mesh_filename); 33 | if (!CGAL::is_triangle_mesh(mesh)) 34 | { 35 | std::cerr << "[error] input geometry is not triangulated." << std::endl; 36 | return EXIT_FAILURE; 37 | } 38 | std::cout << "[info] #F: " << num_faces(mesh) << std::endl; 39 | std::cout << "[info] #H: " << num_halfedges(mesh) << std::endl; 40 | std::cout << "[info] #V: " << num_vertices(mesh) << std::endl; 41 | 42 | // create a property-map for SDF values 43 | typedef std::map Facet_double_map; 44 | Facet_double_map internal_sdf_map; 45 | boost::associative_property_map sdf_property_map(internal_sdf_map); 46 | 47 | // compute SDF values 48 | std::pair min_max_sdf = CGAL::sdf_values(mesh, sdf_property_map); 49 | 50 | // It is possible to compute the raw SDF values and post-process them using 51 | // the following lines: 52 | // const std::size_t number_of_rays = 25; // cast 25 rays per facet 53 | // const double cone_angle = 2.0 / 3.0 * CGAL_PI; // set cone opening-angle 54 | // CGAL::sdf_values(mesh, sdf_property_map, cone_angle, number_of_rays, false); 55 | // std::pair min_max_sdf = 56 | // CGAL::sdf_values_postprocessing(mesh, sdf_property_map); 57 | 58 | // print minimum & maximum SDF values 59 | std::cout << "[info] minimum SDF: " << min_max_sdf.first << std::endl 60 | << "[info] maximum SDF: " << min_max_sdf.second << std::endl; 61 | 62 | // calculate vertices sdf from faces sdf 63 | std::vector> sdf_values; 64 | for (auto v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it) 65 | { 66 | int face_num = 0; 67 | double sdf_sum = 0.0; 68 | for (auto vf_it = mesh.vf_iter(*v_it); vf_it.is_valid(); ++vf_it) 69 | { 70 | sdf_sum += sdf_property_map[*vf_it]; 71 | face_num += 1; 72 | } 73 | sdf_sum /= face_num; 74 | sdf_values.emplace_back(std::make_pair(v_it->idx(), sdf_sum)); 75 | } 76 | 77 | // save SDF values 78 | std::ofstream out_file("sdf_value.txt"); 79 | for (const auto& sdf_value : sdf_values) 80 | out_file << sdf_value.first << " " << sdf_value.second << std::endl; 81 | out_file.close(); 82 | 83 | return 0; 84 | } 85 | -------------------------------------------------------------------------------- /sdf_extractor/sdf_extractor.m: -------------------------------------------------------------------------------- 1 | function [sdf_features] = sdf_extractor(mesh) 2 | 3 | cd('sdf_extractor'); 4 | 5 | delete('sdf_temp.obj'); 6 | delete('sdf_value.txt'); 7 | 8 | mesh_exporter('sdf_temp.obj', mesh); 9 | if strncmp(computer, 'PC', 2) 10 | system(['sdf_extractor.exe ', 'sdf_temp.obj']); 11 | else 12 | system(['./sdf_extractor ', 'sdf_temp.obj']); 13 | end 14 | 15 | sdf_features = importdata('sdf_value.txt'); 16 | sdf_features = sdf_features(:, 2); 17 | 18 | delete('sdf_temp.obj'); 19 | delete('sdf_value.txt'); 20 | 21 | cd('..'); 22 | 23 | end 24 | 25 | -------------------------------------------------------------------------------- /segmentation.m: -------------------------------------------------------------------------------- 1 | clear all; 2 | close all; 3 | clc; 4 | 5 | addpath('common'); 6 | addpath('smpl_model'); 7 | addpath('mesh_parser'); 8 | addpath('sdf_extractor'); 9 | addpath('segmentation'); 10 | 11 | addpath('3rdparty/gco-v3.0'); 12 | addpath('3rdparty/gco-v3.0/matlab'); 13 | 14 | frame_start = 1; 15 | frame_end = 525; 16 | 17 | % global varibles used in single mesh alignment 18 | global is_first; 19 | global mesh_prefix; 20 | global mesh_prefix_last; 21 | global result_dir; 22 | global result_dir_base; 23 | 24 | mesh_folder = 'body_easy'; 25 | mesh_format = '20171227-body-easy_texture_%08d_gop.obj'; 26 | 27 | result_dir_base = ['all_results', filesep, 'segmentation', filesep, mesh_folder]; 28 | mkdir(result_dir_base); 29 | 30 | % get smpl prior 31 | prior_smpl = get_smpl_prior(); 32 | 33 | for frame = frame_start : frame_end 34 | % for first frame 35 | if frame == 1 36 | is_first = 1; 37 | mesh_prefix_last = ''; 38 | else 39 | is_first = 0; 40 | mesh_prefix_last = sprintf(mesh_format, frame - 1); 41 | mesh_prefix_last = mesh_prefix_last(1:end-4); 42 | end 43 | 44 | mesh_prefix = sprintf(mesh_format, frame); 45 | mesh_prefix = mesh_prefix(1:end-4); 46 | disp(['segmentation: ', mesh_prefix]); 47 | 48 | result_dir = [result_dir_base, filesep, mesh_prefix]; 49 | mkdir(result_dir); 50 | 51 | mesh_scan_name = [mesh_prefix, '.obj']; 52 | mesh_scan_folder = ['scans', filesep, mesh_folder]; 53 | 54 | mesh_smpl_name = [mesh_prefix, '_fit.obj']; 55 | mesh_smpl_folder = ['all_results', filesep, 'single_mesh', ... 56 | filesep, mesh_folder, filesep, mesh_prefix]; 57 | 58 | % load scan mesh 59 | mesh_scan = mesh_parser(mesh_scan_name, mesh_scan_folder); 60 | mesh_scan.vertices = mesh_scan.vertices ./ 1000; 61 | mesh_scan.colors = render_texture(mesh_scan.tex_coords, mesh_scan.uv_map); 62 | mesh_exporter([result_dir, filesep, mesh_prefix, '_colored.obj'], mesh_scan, true); 63 | 64 | % load single mesh alignment 65 | mesh_smpl = mesh_parser(mesh_smpl_name, mesh_smpl_folder); 66 | 67 | % calculate scan prior 68 | prior_scan = get_scan_prior(mesh_smpl, mesh_scan, prior_smpl); 69 | 70 | % get unary term 71 | % for both scan and smpl 72 | [unary_scan, unary_smpl] = calculate_unary( ... 73 | mesh_scan, mesh_smpl, prior_scan, prior_smpl); 74 | 75 | % gco segmentation 76 | % for both scan and smpl 77 | [seg_scan, seg_smpl] = gco_segment( ... 78 | mesh_scan, mesh_smpl, unary_scan, unary_smpl); 79 | 80 | % map smpl segmentation back 81 | [seg_scan_map] = label_nearest( ... 82 | mesh_scan, mesh_smpl, seg_scan, seg_smpl); 83 | 84 | % manually modify the result, only for 1st frame 85 | if is_first == 1 86 | [seg_scan, seg_smpl] = manually_modify(... 87 | mesh_scan, mesh_smpl, seg_scan, seg_smpl); 88 | end 89 | 90 | % fit a GMM model 91 | gmm_fitting(mesh_scan, seg_scan); 92 | 93 | % render and save result 94 | mesh_scan_final = mesh_scan; 95 | mesh_scan_final.colors = render_labels(seg_scan); 96 | mesh_exporter([result_dir, filesep, mesh_prefix, '_seg_scan.obj'], ... 97 | mesh_scan_final, true); 98 | 99 | mesh_smpl_final = mesh_smpl; 100 | mesh_smpl_final.colors = render_labels(seg_smpl); 101 | mesh_exporter([result_dir, filesep, mesh_prefix, '_seg_smpl.obj'], ... 102 | mesh_smpl_final, true); 103 | 104 | save([result_dir, filesep, mesh_prefix, '_label_scan.mat'], 'seg_scan'); 105 | save([result_dir, filesep, mesh_prefix, '_label_smpl.mat'], 'seg_smpl'); 106 | 107 | % extract and save scan garments 108 | [garments_scan, mesh_garments_scan] = extract_garments(mesh_scan, seg_scan); 109 | mesh_exporter([result_dir, filesep, mesh_prefix, '_garments_scan.obj'], mesh_garments_scan, true); 110 | save([result_dir, filesep, mesh_prefix, '_garments_scan.mat'], 'garments_scan'); 111 | 112 | if is_first == 1 113 | save([result_dir_base, filesep, 'person_wise_label_scan.mat'], 'seg_scan'); 114 | save([result_dir_base, filesep, 'person_wise_label_smpl.mat'], 'seg_smpl'); 115 | end 116 | end -------------------------------------------------------------------------------- /segmentation/calculate_unary.m: -------------------------------------------------------------------------------- 1 | function [unary_scan, unary_smpl] = calculate_unary( ... 2 | mesh_scan, mesh_smpl, prior_scan, prior_smpl) 3 | 4 | global is_first; 5 | global mesh_prefix; 6 | global mesh_prefix_last; 7 | global result_dir; 8 | global result_dir_base; 9 | 10 | n_scan = size(mesh_scan.vertices, 1); 11 | n_smpl = size(mesh_smpl.vertices, 1); 12 | 13 | % unary: data likelihood 14 | % for both SMPL and scan model 15 | unary_scan_data = zeros(n_scan, 3); 16 | unary_smpl_data = zeros(n_smpl, 3); 17 | 18 | if is_first 19 | center_ind = [21888, 16836, 15667]; 20 | 21 | % color feature 22 | color_start = mesh_scan.colors(center_ind, :); 23 | color_start = rgb2hsv(double(color_start) / 255); 24 | color_hsv = rgb2hsv(double(mesh_scan.colors) / 255); 25 | 26 | % total feature; 27 | total_feature = color_hsv; 28 | total_start = color_start; 29 | 30 | % k-means on scan 31 | labels_k_means = kmeans(total_feature, 3, 'Start', total_start); 32 | 33 | m = mesh_scan; 34 | m.colors = render_labels(labels_k_means - 1); 35 | mesh_exporter([result_dir, filesep, mesh_prefix, '_kmeans.obj'], m, true); 36 | 37 | % for scan 38 | % find neighbors directly from adjacent map 39 | for i = 1 : n_scan 40 | adjacency = mesh_scan.adjacency_map(i, :); 41 | [~, neighbors_ind] = find(adjacency == 1); 42 | neighbor_label = labels_k_means(neighbors_ind); 43 | 44 | unary_scan_data(i, 1) = sum(-log((neighbor_label == 1) + 1e-6)); 45 | unary_scan_data(i, 2) = sum(-log((neighbor_label == 2) + 1e-6)); 46 | unary_scan_data(i, 3) = sum(-log((neighbor_label == 3) + 1e-6)); 47 | end 48 | 49 | % for smpl 50 | % node i -> scan point v -> v's neighbors 51 | ind_scan = matching_pair(mesh_scan, mesh_smpl, 1); 52 | for i = 1 : size(ind_scan, 1) 53 | neighbors = mesh_scan.adjacency_map(ind_scan(i), :); 54 | [~, neighbors_ind] = find(neighbors == 1); 55 | neighbor_label = labels_k_means(neighbors_ind); 56 | 57 | unary_smpl_data(i, 1) = sum(-log((neighbor_label == 1) + 1e-6)); 58 | unary_smpl_data(i, 2) = sum(-log((neighbor_label == 2) + 1e-6)); 59 | unary_smpl_data(i, 3) = sum(-log((neighbor_label == 3) + 1e-6)); 60 | end 61 | else 62 | gmm_path = [result_dir_base, filesep, mesh_prefix_last]; 63 | gmm_name = [mesh_prefix_last, '_gmm.mat']; 64 | gmm = load([gmm_path, filesep, gmm_name]); 65 | gmm = gmm.gmm; 66 | 67 | % for scan 68 | % find neighbors directly from adjacent map 69 | for i = 1 : n_scan 70 | adjacency = mesh_scan.adjacency_map(i, :); 71 | [~, neighbors_ind] = find(adjacency == 1); 72 | neighbor_colors = mesh_scan.colors(neighbors_ind, :); 73 | 74 | probs = gmm_sample(gmm, neighbor_colors); 75 | unary_scan_data(i, :) = sum(-log(probs + 1e-6)); 76 | end 77 | 78 | % for smpl 79 | % node i -> scan point v -> v's neighbors 80 | ind_scan = matching_pair(mesh_scan, mesh_smpl, 1); 81 | for i = 1 : size(ind_scan, 1) 82 | neighbors = mesh_scan.adjacency_map(ind_scan(i), :); 83 | [~, neighbors_ind] = find(neighbors == 1); 84 | neighbor_colors = mesh_scan.colors(neighbors_ind, :); 85 | 86 | probs = gmm_sample(gmm, neighbor_colors); 87 | unary_smpl_data(i, :) = sum(-log(probs + 1e-6)); 88 | end 89 | end 90 | 91 | [~, ind_smpl] = min(unary_smpl_data, [], 2); 92 | [~, ind_scan] = min(unary_scan_data, [], 2); 93 | 94 | m = mesh_smpl; 95 | m.colors = render_labels(ind_smpl - 1); 96 | mesh_exporter([result_dir, filesep, mesh_prefix, '_unary_smpl_data.obj'], m, true); 97 | m = mesh_scan; 98 | m.colors = render_labels(ind_scan - 1); 99 | mesh_exporter([result_dir, filesep, mesh_prefix, '_unary_scan_data.obj'], m, true); 100 | 101 | % unary: prior 102 | % for both SMPL and scan model 103 | unary_scan_prior = zeros(n_scan, 3); 104 | unary_smpl_prior = zeros(n_smpl, 3); 105 | 106 | % for scan 107 | l_skin_scan(prior_scan.skin == 0) = 100; 108 | l_skin_scan(prior_scan.skin == 1) = -200; 109 | l_skin_scan(prior_scan.skin == 0.5) = 0; 110 | 111 | l_shirt_scan(prior_scan.shirt == 0) = 100; 112 | l_shirt_scan(prior_scan.shirt == 1) = -200; 113 | l_shirt_scan(prior_scan.shirt == 0.5) = 0; 114 | 115 | l_pants_scan(prior_scan.pants == 0) = 100; 116 | l_pants_scan(prior_scan.pants == 1) = -200; 117 | l_pants_scan(prior_scan.pants == 0.5) = 0; 118 | 119 | unary_scan_prior(:, 1) = l_skin_scan; 120 | unary_scan_prior(:, 2) = l_shirt_scan; 121 | unary_scan_prior(:, 3) = l_pants_scan; 122 | 123 | % for smpl 124 | l_skin_smpl(prior_smpl.skin == 0) = 100; 125 | l_skin_smpl(prior_smpl.skin == 1) = -200; 126 | l_skin_smpl(prior_smpl.skin == 0.5) = 0; 127 | 128 | l_shirt_smpl(prior_smpl.shirt == 0) = 100; 129 | l_shirt_smpl(prior_smpl.shirt == 1) = -200; 130 | l_shirt_smpl(prior_smpl.shirt == 0.5) = 0; 131 | 132 | l_pants_smpl(prior_smpl.pants == 0) = 100; 133 | l_pants_smpl(prior_smpl.pants == 1) = -200; 134 | l_pants_smpl(prior_smpl.pants == 0.5) = 0; 135 | 136 | unary_smpl_prior(:, 1) = l_skin_smpl; 137 | unary_smpl_prior(:, 2) = l_shirt_smpl; 138 | unary_smpl_prior(:, 3) = l_pants_smpl; 139 | 140 | % total unary 141 | % for both SMPL and scan model 142 | unary_scan = unary_scan_data + unary_scan_prior; 143 | unary_smpl = unary_smpl_data + unary_smpl_prior; 144 | 145 | [~, ind_smpl] = min(unary_smpl, [], 2); 146 | [~, ind_scan] = min(unary_scan, [], 2); 147 | 148 | m = mesh_smpl; 149 | m.colors = render_labels(ind_smpl - 1); 150 | mesh_exporter([result_dir, filesep, mesh_prefix, '_unary_smpl_all.obj'], m, true); 151 | m = mesh_scan; 152 | m.colors = render_labels(ind_scan - 1); 153 | mesh_exporter([result_dir, filesep, mesh_prefix, '_unary_scan_all.obj'], m, true); 154 | 155 | end 156 | 157 | -------------------------------------------------------------------------------- /segmentation/gco_segment.m: -------------------------------------------------------------------------------- 1 | function [seg_scan, seg_smpl] = gco_segment( ... 2 | mesh_scan, mesh_smpl, unary_scan, unary_smpl) 3 | 4 | n_scan = size(unary_scan, 1); 5 | n_smpl = size(unary_smpl, 1); 6 | 7 | % for scan 8 | h_scan = GCO_Create(n_scan, 3); 9 | unary_scan_int = int32(unary_scan); 10 | 11 | GCO_SetDataCost(h_scan, unary_scan_int'); 12 | GCO_SetSmoothCost(h_scan, ... 13 | [0, 1, 1; 14 | 1, 0, 1; 15 | 1, 1, 0;] * 50); 16 | GCO_SetNeighbors(h_scan, sparse(triu(mesh_scan.adjacency_map))); 17 | GCO_Expansion(h_scan); 18 | seg_scan = GCO_GetLabeling(h_scan); 19 | 20 | % for smpl 21 | h_smpl = GCO_Create(n_smpl, 3); 22 | unary_smpl_int = int32(unary_smpl); 23 | 24 | GCO_SetDataCost(h_smpl, unary_smpl_int'); 25 | GCO_SetSmoothCost(h_smpl, ... 26 | [0, 1, 1; 27 | 1, 0, 1; 28 | 1, 1, 0;] * 200); 29 | GCO_SetNeighbors(h_smpl, sparse(triu(mesh_smpl.adjacency_map))); 30 | GCO_Expansion(h_smpl); 31 | seg_smpl = GCO_GetLabeling(h_smpl); 32 | 33 | seg_scan = seg_scan - 1; 34 | seg_smpl = seg_smpl - 1; 35 | 36 | GCO_Delete(h_scan); 37 | GCO_Delete(h_smpl); 38 | 39 | end 40 | 41 | -------------------------------------------------------------------------------- /segmentation/get_scan_prior.m: -------------------------------------------------------------------------------- 1 | function [prior_scan] = get_scan_prior(mesh_smpl, mesh_scan, prior_smpl) 2 | 3 | nearest_ind = knnsearch(mesh_smpl.vertices, mesh_scan.vertices); 4 | 5 | prior_scan_skin = prior_smpl.skin(nearest_ind); 6 | prior_scan_shirt = prior_smpl.shirt(nearest_ind); 7 | prior_scan_pants = prior_smpl.pants(nearest_ind); 8 | 9 | % render and return 10 | prior_scan.skin = prior_scan_skin; 11 | prior_scan.shirt = prior_scan_shirt; 12 | prior_scan.pants = prior_scan_pants; 13 | 14 | m = mesh_scan; 15 | m.colors = render_prior(prior_scan_skin); 16 | mesh_exporter('segmentation/prior_base/prior_scan_skin.obj', m, true); 17 | 18 | m.colors = render_prior(prior_scan_shirt); 19 | mesh_exporter('segmentation/prior_base/prior_scan_shirt.obj', m, true); 20 | 21 | m.colors = render_prior(prior_scan_pants); 22 | mesh_exporter('segmentation/prior_base/prior_scan_pants.obj', m, true); 23 | 24 | end 25 | 26 | -------------------------------------------------------------------------------- /segmentation/get_smpl_prior.m: -------------------------------------------------------------------------------- 1 | function [prior_smpl] = get_smpl_prior() 2 | 3 | mkdir('segmentation/prior_base'); 4 | smpl_base = mesh_parser('smpl_base_m.obj', 'smpl_model'); 5 | 6 | if exist('segmentation/prior_base/prior_base.mat', 'file') 7 | prior_smpl = load('segmentation/prior_base/prior_base.mat'); 8 | prior_smpl = prior_smpl.prior_smpl; 9 | else 10 | prior_smpl.skin = prior_skin(smpl_base); 11 | prior_smpl.shirt = prior_shirt(smpl_base); 12 | prior_smpl.pants = prior_pants(smpl_base); 13 | 14 | save('segmentation/prior_base/prior_base.mat', 'prior_smpl'); 15 | end 16 | 17 | smpl_base.colors = render_prior(prior_smpl.skin); 18 | mesh_exporter('segmentation/prior_base/prior_smpl_skin.obj', smpl_base, true); 19 | 20 | smpl_base.colors = render_prior(prior_smpl.shirt); 21 | mesh_exporter('segmentation/prior_base/prior_smpl_shirt.obj', smpl_base, true); 22 | 23 | smpl_base.colors = render_prior(prior_smpl.pants); 24 | mesh_exporter('segmentation/prior_base/prior_smpl_pants.obj', smpl_base, true); 25 | 26 | end -------------------------------------------------------------------------------- /segmentation/gmm_fitting.m: -------------------------------------------------------------------------------- 1 | function [] = gmm_fitting(mesh_scan, label_scan) 2 | 3 | global is_first; 4 | global mesh_prefix; 5 | global mesh_prefix_last; 6 | global result_dir; 7 | global result_dir_base; 8 | 9 | k = 3; 10 | 11 | colors_hsv = rgb2hsv(double(mesh_scan.colors) / 255); 12 | colors_skin = colors_hsv(label_scan == 0, :); 13 | colors_shirt = colors_hsv(label_scan == 1, :); 14 | colors_pants = colors_hsv(label_scan == 2, :); 15 | 16 | if is_first == 1 17 | mu = zeros(k, 3); 18 | mu(1, :) = mean(colors_skin); 19 | mu(2, :) = mean(colors_shirt); 20 | mu(3, :) = mean(colors_pants); 21 | 22 | sigma = zeros(3, 3, k); 23 | sigma(:, :, 1) = cov(colors_skin); 24 | sigma(:, :, 2) = cov(colors_shirt); 25 | sigma(:, :, 3) = cov(colors_pants); 26 | 27 | components = [size(colors_skin, 1), size(colors_shirt, 1), ... 28 | size(colors_pants, 1)] / size(mesh_scan.colors, 1); 29 | else 30 | colors_path = [result_dir_base, filesep, mesh_prefix_last]; 31 | colors_name = [mesh_prefix_last, '_colors.mat']; 32 | colors_prev = load([colors_path, filesep, colors_name]); 33 | colors_prev = colors_prev.colors_prev; 34 | 35 | colors_skin = [colors_skin; colors_prev.skin]; 36 | colors_shirt = [colors_shirt; colors_prev.shirt]; 37 | colors_pants = [colors_pants; colors_prev.pants]; 38 | colors_hsv = [colors_skin; colors_shirt; colors_pants]; 39 | 40 | gmm_path = [result_dir_base, filesep, mesh_prefix_last]; 41 | gmm_name = [mesh_prefix_last, '_gmm.mat']; 42 | gmm_prev = load([gmm_path, filesep, gmm_name]); 43 | gmm_prev = gmm_prev.gmm; 44 | 45 | mu = gmm_prev.mu; 46 | sigma = gmm_prev.Sigma; 47 | components = gmm_prev.ComponentProportion; 48 | end 49 | 50 | S = struct('mu', mu, 'Sigma', sigma, ... 51 | 'ComponentProportion', components); 52 | options = statset('Display', 'final', 'MaxIter', 1000); 53 | 54 | % gmm_skin = fitgmdist(colors_skin, 3, 'Start', S, 'Options', options); 55 | % gmm_shirt = fitgmdist(colors_shirt, 3, 'Start', S, 'Options', options); 56 | % gmm_pants = fitgmdist(colors_pants, 3, 'Start', S, 'Options', options); 57 | % 58 | % gmm.skin = gmm_skin; 59 | % gmm.shirt = gmm_shirt; 60 | % gmm.pants = gmm_pants; 61 | 62 | gmm = fitgmdist(colors_hsv, 3, 'Start', S, 'Options', options); 63 | save([result_dir, filesep, mesh_prefix, '_gmm.mat'], 'gmm'); 64 | 65 | colors_prev.skin = colors_skin; 66 | colors_prev.shirt = colors_shirt; 67 | colors_prev.pants = colors_pants; 68 | save([result_dir, filesep, mesh_prefix, '_colors.mat'], 'colors_prev'); 69 | 70 | end 71 | 72 | -------------------------------------------------------------------------------- /segmentation/gmm_sample.m: -------------------------------------------------------------------------------- 1 | function [probs] = gmm_sample(gmm, data) 2 | 3 | data_hsv = rgb2hsv(double(data) / 255); 4 | 5 | probs_skin = mvnpdf(data_hsv, gmm.mu(1, :), ... 6 | gmm.Sigma(:, :, 1)) * gmm.ComponentProportion(1); 7 | probs_shirt = mvnpdf(data_hsv, gmm.mu(2, :), ... 8 | gmm.Sigma(:, :, 2)) * gmm.ComponentProportion(2); 9 | probs_pants = mvnpdf(data_hsv, gmm.mu(3, :), ... 10 | gmm.Sigma(:, :, 3)) * gmm.ComponentProportion(3); 11 | 12 | probs_all = probs_skin + probs_shirt + probs_pants; 13 | 14 | probs_skin = probs_skin ./ probs_all; 15 | probs_shirt = probs_shirt ./ probs_all; 16 | probs_pants = probs_pants ./ probs_all; 17 | 18 | probs = [probs_skin, probs_shirt, probs_pants]; 19 | 20 | % probs_skin = ... 21 | % mvnpdf(data_hsv, gmm.skin.mu(1, :), gmm.skin.Sigma(:, :, 1)) * gmm.skin.ComponentProportion(1) + ... 22 | % mvnpdf(data_hsv, gmm.shirt.mu(1, :), gmm.shirt.Sigma(:, :, 1)) * gmm.shirt.ComponentProportion(1) + ... 23 | % mvnpdf(data_hsv, gmm.pants.mu(1, :), gmm.pants.Sigma(:, :, 1)) * gmm.pants.ComponentProportion(1); 24 | % 25 | % probs_shirt = ... 26 | % mvnpdf(data_hsv, gmm.skin.mu(2, :), gmm.skin.Sigma(:, :, 2)) * gmm.skin.ComponentProportion(2) + ... 27 | % mvnpdf(data_hsv, gmm.shirt.mu(2, :), gmm.shirt.Sigma(:, :, 2)) * gmm.shirt.ComponentProportion(2) + ... 28 | % mvnpdf(data_hsv, gmm.pants.mu(2, :), gmm.pants.Sigma(:, :, 2)) * gmm.pants.ComponentProportion(2); 29 | % 30 | % probs_pants = ... 31 | % mvnpdf(data_hsv, gmm.skin.mu(3, :), gmm.skin.Sigma(:, :, 3)) * gmm.skin.ComponentProportion(3) + ... 32 | % mvnpdf(data_hsv, gmm.shirt.mu(3, :), gmm.shirt.Sigma(:, :, 3)) * gmm.skin.ComponentProportion(3) + ... 33 | % mvnpdf(data_hsv, gmm.pants.mu(3, :), gmm.pants.Sigma(:, :, 3)) * gmm.skin.ComponentProportion(3); 34 | % 35 | % probs = [probs_skin, probs_shirt, probs_pants]; 36 | 37 | end 38 | 39 | -------------------------------------------------------------------------------- /segmentation/label_nearest.m: -------------------------------------------------------------------------------- 1 | function [seg_scan_map] = label_nearest(mesh_scan, mesh_smpl, seg_scan, seg_smpl) 2 | 3 | global mesh_prefix; 4 | global result_dir; 5 | 6 | nearest_ind = knnsearch(mesh_smpl.vertices, mesh_scan.vertices); 7 | seg_scan_map = seg_smpl(nearest_ind); 8 | 9 | m = mesh_scan; 10 | m.colors = render_labels(seg_scan_map); 11 | mesh_exporter([result_dir, filesep, mesh_prefix, '_seg_scan_map.obj'], m, true); 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /segmentation/manually_modify.m: -------------------------------------------------------------------------------- 1 | function [label_scan_n, label_smpl_n] = manually_modify( ... 2 | mesh_scan, mesh_smpl, label_scan, label_smpl) 3 | 4 | label_scan_n = label_scan; 5 | label_smpl_n = label_smpl; 6 | 7 | % for smpl 8 | add_skin = [ ... 9 | 6794; 6816; 4277; 4741] + 1; 10 | add_shirt = [ ... 11 | ] + 1; 12 | add_pants = [ ... 13 | ] + 1; 14 | 15 | label_smpl_n(add_skin, :) = 0; 16 | label_smpl_n(add_shirt, :) = 1; 17 | label_smpl_n(add_pants, :) = 2; 18 | 19 | % for scan 20 | 21 | end 22 | 23 | -------------------------------------------------------------------------------- /segmentation/manually_modify_sub.m: -------------------------------------------------------------------------------- 1 | function [label_scan_n, label_smpl_n] = manually_modify_sub(mesh_scan, mesh_smpl, label_scan, label_smpl) 2 | 3 | label_scan_n = label_scan; 4 | label_smpl_n = label_smpl; 5 | 6 | % for smpl 7 | add_skin = [ ... 8 | 1639; 4059; 3060; 4308; 570; 4307; 4074; 4073; 4190; 6497; 3061; 3168; 1309; 803; ... 9 | 3327; 1677; 1707; 6830; 4309; 4788; 4789; 702; 587; 1308; 2801;] + 1; 10 | add_shirt = [ ... 11 | 4350; 1807; 864; 863; 3094; 6549; 1678; 626; 629; 1675; 628; 1396; 1395; ... 12 | 1673; 1674; 1734; 1380; 1381; 1382; 1397; 1396; 1383; 1398; 1737; 787; 1668; ... 13 | 791; 778; 1431; 786; 1430; 1903; 792; 1680; 1682; 1376; 790; 2993; 775; ... 14 | 2974; 2977; 776; 2976; 2975; 1902; 2901; 2900; 1261; 1258; 1259; 1260; ... 15 | 793; 1413; 1414; 1904; 1541; 1393; 1389; 1390; 1709; 1714; 1719; 1718; ... 16 | 1387; 1669; 1234; 1388; 1233; 1232; 1231; 1386; 1384; 1385; 1671; 1672; ... 17 | 4276; 4116; 6280; 4117; 4764; 4763; 4272; 4273; 4275; 4281; 4268; 5326; 5328; ... 18 | 4974; 4265; 4171; 4170; 4169; 4882; 4883; 4884; 4855; 4869; 5206; 4868; 4854; ... 19 | 4853; 4267; 4857; 4856; 4859; 6445; 4849; 4851; 4852; 4864; 4865; 4866; 5010; ... 20 | 6357; 5011; 6440; 6358; 6357; 6356; 6355; 6441; 6443; 6438; 6453; 6454; 6442; ... 21 | 4741; 4682; 5365; 4861; 4867; 4744; 4716; 4863; 6451; 6452; 4264; 4261; 4262; ... 22 | 4263; 5364; 5339; 6437; 6439; 6436; 4862; 5362; 4906; 4887; 4743; 4717; 4279; ... 23 | 4886; 6360; 4742; 6359; 6433; 6434; 6435; 4274; 4280; 4278; 4858; 4714; 4715; ... 24 | 4860; 5151; 5149; 5148; 5185; 5145; 5144; 5143; 5142; 5178; 5181; 5188; 5183; ... 25 | 5138; 5189; 5140; 5139; 5203; 5141; 4900; 1810; 743; 742; 1237; 818; 804; ... 26 | 2972; 744; 805; 814; 4300; 4293; 6431; 4230; 4231; 4232; 4294; 4306; 1828; ... 27 | 2948; 2938; 2941; 1839; 1838; 6397; 6408; 6424; 6398; 6423; 6422; 6444; ... 28 | 4833; 6449; 6446; 5338; 4904; 4903; 5363; 4912; 6354; 4977; 6283; 6282; 4850; ... 29 | 4794; 4878; 4880; 4881; 1311; 1310; 1314; 1679; 1505; 1315; 1379; 1377; 716; ... 30 | 1378; 1394; 1391; 1392; 1710; 2897; 2980; 2899; 2983; 2947; 3001; 3002; 2946; ... 31 | 1888; 2996; 2945; 2991; 1892; 1876; 784; 1281; 1866; 1282; 1867; 1869; 785; ... 32 | 719; 4830; 4202; 4203; 5340; 4756; 4755; 4754; 5341; 4168; 4205; 4905; 5330; ... 33 | 5008; 5012; 4266; 4889; 4879; 4277; 6281; 4791; 4790; 4795; 4870; 4871; 4115; ... 34 | 4114; 4888; 2821; 683; 1272; 1273; 1868; 1879; 1432; ] + 1; 35 | add_pants = [ ... 36 | 3083; 3092; 3096; 3095; 3088; 3481; 4371; 6513; 6519; 6520; 6516; 6506; 883; ... 37 | 6550; 4400; 4398; 4985; 6554; 6549; 3128; 3129; 3130; 3094; 3093; 3103; 3105; ... 38 | 3104; 3139; 3140; 3141; 6560; 6528; 6527; 6526; 6518; 6517; 6551; 4608; 6574; ... 39 | 6596; 6594; 6601; 6610; 6590; 6604; 6606; 6598; 6599; 6834; 6836; 6835; 6730; ... 40 | 6729; 6731; 3207; 3204; 3180; 3330; 3331; 3435; 3329; 3323; 1123; 3201; 3436; ... 41 | 3210; 3199; 3198; 3434; 6561; 4368; 882; 1374; 3190; 4593; 6580; 6720; ] + 1; 42 | 43 | label_smpl_n(add_skin, :) = 0; 44 | label_smpl_n(add_shirt, :) = 1; 45 | label_smpl_n(add_pants, :) = 2; 46 | 47 | % for scan 48 | tmp1 = mesh_scan.vertices(:, 2) > 1.16583; 49 | label_scan_n(tmp1) = 0; 50 | 51 | tmp2 = mesh_scan.vertices(:, 1) < 0.157183 & mesh_scan.vertices(:, 1) > -0.295671; 52 | tmp3 = mesh_scan.vertices(:, 2) < 1.055070 & mesh_scan.vertices(:, 2) > 0.4682570; 53 | label_scan_n(tmp2 & tmp3) = 1; 54 | 55 | tmp4 = mesh_scan.vertices(:, 2) < 1.02526 & mesh_scan.vertices(:, 2) > 0.866156; 56 | label_scan_n(tmp4) = 1; 57 | 58 | tmp5 = mesh_scan.vertices(:, 2) < 0.397021 & mesh_scan.vertices(:, 2) > -0.377195; 59 | label_scan_n(tmp5) = 2; 60 | 61 | tmp6 = mesh_scan.vertices(:, 2) < -0.45; 62 | label_scan_n(tmp6) = 0; 63 | 64 | add_skin = [ ... 65 | 6581; 13188; 9477; 8521; 14285; 5322; 5176; 2027; 9078; 3968; 3822; 15463; 6779; 8982; ... 66 | 19108; 19499; 7722; 12400; 16782; 2678; 7641; 8427; 5316; 3519; 9362; 8428; 9262; 5360; ... 67 | 9223; ] + 1; 68 | add_shirt = [ ... 69 | 5179; 17487; 7977; 13520; 9076; 9473; 9629; 10131; 4358; 18870; 23226; 21820; 14892; 14631; ... 70 | 9782; 9783; 18761; 14699; 18760; 9359; 3960; 9475; 14627; 9778; 14697; 4035; 14630; ... 71 | 14628; 3884; 3804; 7978; 18402; 14215; 18403; 18137; 8390; 13893; 8140; 18005; 8139; 3042; ... 72 | 8405; 7867; 5238; 7868; 3230; 2517; 7423; 17652; 7293; 2646; 4876; 7295; 7294; 2647; 7299; ... 73 | 7435; 4380; 10018; 10138; 10016; 4313; 10015; 18872; 4231; 10020; 4151; 9921; 5400; 14818; ... 74 | 10021; 14749; 10014; 9919; 4379; 14748; 14046; 3592; 9252; 7297; 7298; 4230; 7427; 2746; ... 75 | 8540; 8003; 8699; 3504; 7380; 2704; 7831; 7377; 13348; 17627; 7376; 17628; 17626; 17627; ... 76 | 5230; 8124; 3567; 10252; 18998; 23205; 10116; 14993; 14994; 14983; 18997; 19081; 10114; 10115; ... 77 | 10116; 23192; 4352; 18866; 18672; 21745; 18803; 9348; 9618; 14527; 4421; 18868; 23194; 21753; ... 78 | 18867; 23193; 5417; 10249; 4422; 4421; 3717; 10250; 18806; 10118; 4029; 9465; 9759; 9758; 4201; ... 79 | 23188; 9996; 9345; 8792; 3106; 7969; 13666; 8793; 13598; 2909; 7684; 3101; 8122; 3104; 3205; ... 80 | 8123; 8377; 6247; 16227; 5522; 5520; 5517; 3; 5545; 6; 19; 5547; 5544; 11795; 11793; 16212; ... 81 | 4; 5; 11809; 5593; 11863; 11953; 104; 5762; 5614; 16301; 11954; 5518; 16368; 11804; 5521; ... 82 | 5535; 11840; 5574; 5741; 10484; 7077; 11794; 5519; 16211; 16225; 5536; 16224; 14; 11934; 5671; ... 83 | 316; 11935; 5742; 12083; 16399; 11882; 5573; 5558; 11819; 5557; 11821; 11820; 5559; 11865; ... 84 | 3988; 9788; 9505; 8979; 3748; 3385; 2805; 7523; 18401; 5322] + 1; 85 | add_pants = [ ... 86 | 5839; 12120; 4876; 4971; 11624; 16174; 11550; 5492; 4441; 4693; 4745; 11394; 4900; ... 87 | 6427; 20117; 11780; 11291; 10942; 10426; 19115; 19118; 9391; 15870; 11621; 16024; 19840; ... 88 | 11401; 3752; 3911; 4167; 18467; 18407; 8985; 3139; 3138; 8558; 14471; 9524; 18407; 8985; ... 89 | 8984; 18408; 9935; 4323; 10552; 10675; 4866; 19504; 15772; 4598; 11410; 4908; 18825; 3680; ... 90 | 8850; 8849; 14145; 3599; 18348; 5315; 4719; 11009; 19540; 11534; 19157; 11348; 11456; 19701; ... 91 | 11728; 11672; 11498; 9393; 5338; 11241; 15391; 18545; 8314; 3423; 3770; 15896; 19870; 11582; ... 92 | 9566; ] + 1; 93 | 94 | label_scan_n(add_skin, :) = 0; 95 | label_scan_n(add_shirt, :) = 1; 96 | label_scan_n(add_pants, :) = 2; 97 | 98 | end 99 | 100 | -------------------------------------------------------------------------------- /segmentation/matching_pair.m: -------------------------------------------------------------------------------- 1 | function [ind_nearest] = matching_pair(mesh_scan, mesh_smpl, n_points) 2 | 3 | n_smpl = size(mesh_smpl.vertices, 1); 4 | 5 | [ind_scan, distance] = knnsearch(mesh_scan.vertices, ... 6 | mesh_smpl.vertices, 'K', n_points); 7 | ratio = zeros(size(distance)); 8 | for i = 1 : n_points 9 | ind = ind_scan(:, i); 10 | cross_dot = dot(mesh_smpl.normals.', mesh_scan.normals(ind, :).').'; 11 | ratio(:, i) = cross_dot; 12 | end 13 | [~, max_ind] = max(ratio, [], 2); 14 | 15 | ind_nearest = zeros(n_smpl, 1); 16 | for i = 1 : n_smpl 17 | ind_nearest(i) = ind_scan(i, max_ind(i)); 18 | end 19 | 20 | end -------------------------------------------------------------------------------- /segmentation/meanfield.m: -------------------------------------------------------------------------------- 1 | function [seg_scan, seg_smpl] = meanfield(mesh_scan, mesh_smpl, unary_scan, unary_smpl) 2 | 3 | % cd(['3rdparty', filesep, 'meanfield-matlab-master']); 4 | % 5 | % n_scan = size(mesh_scan.vertices, 1); 6 | % n_smpl = size(mesh_smpl.vertices, 1); 7 | % 8 | % im = uint8(zeros(2048, 2048, 3)); 9 | % un = single(zeros(2048, 2048, 3)); 10 | % 11 | % D = Densecrf(im, un); 12 | % 13 | % D.vertices = mesh_scan.vertices; 14 | % D.vertices_unary = unary_scan; 15 | % D.colors = mesh_scan.colors; 16 | % 17 | % D.gaussian_x_stddev = 3; 18 | % D.gaussian_y_stddev = 3; 19 | % D.gaussian_weight = 1; 20 | % 21 | % D.bilateral_x_stddev = 30; 22 | % D.bilateral_y_stddev = 30; 23 | % D.bilateral_r_stddev = 10; 24 | % D.bilateral_g_stddev = 10; 25 | % D.bilateral_b_stddev = 10; 26 | % D.bilateral_weight = 1; 27 | 28 | % D.mean_field2; 29 | % seg_scan = D.seg; 30 | 31 | [~, seg_scan] = min(unary_scan, [], 2); 32 | [~, seg_smpl] = min(unary_smpl, [], 2); 33 | 34 | seg_scan = seg_scan - 1; 35 | seg_smpl = seg_smpl - 1; 36 | 37 | % cd(['..', filesep, '..']); 38 | 39 | end 40 | 41 | -------------------------------------------------------------------------------- /segmentation/prior_pants.m: -------------------------------------------------------------------------------- 1 | function [label] = prior_pants(mesh) 2 | 3 | %% likely to be pants 4 | mask_likely = mesh.vertices(:, 2) > -0.947551 & mesh.vertices(:, 2) < -0.384934; 5 | 6 | %% unlikely to be pants 7 | mask_unlikely_0 = mesh.vertices(:, 2) > -0.091166; 8 | % mask_unlikely_1 = mesh.vertices(:, 2) < -1.16301; 9 | mask_unlikely = mask_unlikely_0; 10 | 11 | %% get the label 12 | label = ones(size(mesh.vertices, 1), 1) * 0.5; 13 | label(mask_likely) = 1; 14 | label(mask_unlikely) = 0; 15 | 16 | end 17 | 18 | -------------------------------------------------------------------------------- /segmentation/prior_shirt.m: -------------------------------------------------------------------------------- 1 | function [label] = prior_shirt(mesh) 2 | 3 | %% likely to be t-shirt 4 | mask_likely_0 = mesh.vertices(:, 2) < 0.134339 & mesh.vertices(:, 2) > -0.166764; 5 | mask_likely = mask_likely_0; 6 | 7 | %% unlikely to be t-shirt 8 | mask_unlikely_0 = mesh.vertices(:, 2) > 0.329678; 9 | mask_unlikely_1 = mesh.vertices(:, 2) < -0.380797; 10 | mask_unlikely_2 = mesh.vertices(:, 1) > 0.442744; 11 | mask_unlikely_3 = mesh.vertices(:, 1) < -0.42223; 12 | mask_unlikely = mask_unlikely_0 | mask_unlikely_1 | mask_unlikely_2 | mask_unlikely_3; 13 | 14 | %% get the label 15 | label = ones(size(mesh.vertices, 1), 1) * 0.5; 16 | label(mask_likely) = 1; 17 | label(mask_unlikely) = 0; 18 | 19 | end 20 | 21 | -------------------------------------------------------------------------------- /segmentation/prior_skin.m: -------------------------------------------------------------------------------- 1 | function [label] = prior_skin(mesh) 2 | 3 | %% likely to be skin 4 | mask_likely_0 = mesh.vertices(:, 2) > 0.329678; 5 | % mask_likely_1 = mesh.vertices(:, 2) < -1.17301; 6 | mask_likely_2 = mesh.vertices(:, 1) > 0.442744; 7 | mask_likely_3 = mesh.vertices(:, 1) < -0.42223; 8 | mask_likely = mask_likely_0 | mask_likely_2 | mask_likely_3; 9 | 10 | %% unlikely to be skin 11 | mask_unlikely_0 = mesh.vertices(:, 2) < 0.134339 & mesh.vertices(:, 2) > -0.166764; 12 | mask_unlikely_1 = mesh.vertices(:, 2) < -0.235831 & mesh.vertices(:, 2) > -0.967478; 13 | mask_unlikely = mask_unlikely_0 | mask_unlikely_1; 14 | 15 | %% get the label 16 | label = ones(size(mesh.vertices, 1), 1) * 0.5; 17 | label(mask_likely) = 1; 18 | label(mask_unlikely) = 0; 19 | 20 | end -------------------------------------------------------------------------------- /segmentation/render_prior.m: -------------------------------------------------------------------------------- 1 | function [colors] = render_prior(labels) 2 | 3 | colors = zeros(length(labels), 3); 4 | for i = 1 : length(labels) 5 | if labels(i) == 0 6 | colors(i, :) = [255, 0, 0]; 7 | elseif labels(i) == 1 8 | colors(i, :) = [0, 255, 0]; 9 | else 10 | colors(i, :) = [128, 128, 128]; 11 | end 12 | end 13 | 14 | end 15 | 16 | -------------------------------------------------------------------------------- /segmentation/render_texture.m: -------------------------------------------------------------------------------- 1 | function [colors] = render_texture(tex_coords, uv_map) 2 | 3 | [width, height, ~] = size(uv_map); 4 | [n_point, ~] = size(tex_coords); 5 | 6 | colors = zeros(n_point, 3); 7 | for i = 1 : n_point 8 | u = tex_coords(i, 1) * width; 9 | v = tex_coords(i, 2) * height; 10 | colors(i, :) = uv_map(height - floor(v) - 1, floor(u) + 1, :); 11 | end 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /single_mesh.m: -------------------------------------------------------------------------------- 1 | clear all; 2 | close all; 3 | clc; 4 | 5 | addpath('common'); 6 | addpath('smpl_model'); 7 | addpath('mesh_parser'); 8 | addpath('single_mesh_alignment') 9 | 10 | mesh_folder = 'body_easy'; 11 | mesh_format = '20171227-body-easy_texture_%08d_gop.obj'; 12 | 13 | frame_start = 1; 14 | frame_end = 525; 15 | 16 | global is_first; 17 | global smpl_model; 18 | global mesh_prefix; 19 | global mesh_prefix_last; 20 | global result_dir; 21 | global result_dir_base; 22 | 23 | result_dir_base = ['all_results', filesep, 'single_mesh', filesep, mesh_folder]; 24 | mkdir(result_dir_base); 25 | 26 | smpl_model = load('smpl_m.mat'); 27 | smpl_model = smpl_model.model; 28 | 29 | for frame = frame_start : frame_end 30 | % for first frame 31 | if frame == frame_start 32 | is_first = 1; 33 | mesh_prefix_last = ''; 34 | else 35 | is_first = 0; 36 | mesh_prefix_last = sprintf(mesh_format, frame - 1); 37 | mesh_prefix_last = mesh_prefix_last(1:end-4); 38 | end 39 | 40 | mesh_prefix = sprintf(mesh_format, frame); 41 | mesh_prefix = mesh_prefix(1:end-4); 42 | disp(['single_mesh: ', mesh_prefix]); 43 | 44 | result_dir = [result_dir_base, filesep, mesh_prefix]; 45 | mkdir(result_dir); 46 | 47 | mesh_scan_name = [mesh_prefix, '.obj']; 48 | mesh_scan_folder = ['scans', filesep, mesh_folder]; 49 | 50 | mesh = mesh_parser(mesh_scan_name, mesh_scan_folder); 51 | 52 | % single mesh alignment 53 | 54 | % s1_scale_scan 55 | mesh_scaled_scan = single_mesh_scale(mesh, 1000); 56 | 57 | % s2_align_scan 58 | param_init = single_mesh_trans(mesh_scaled_scan); 59 | 60 | % s3_fit_scan 61 | [param_fitted_smpl, mesh_fitted_smpl] = single_mesh_fitting(mesh_scaled_scan, param_init); 62 | 63 | % % s4_opt_single_mesh_3s_GMdist 64 | % [m_smpl, m_A, param] = single_mesh_align(mesh_scaled_scan, mesh_fitted_smpl, param_fitted_smpl); 65 | 66 | end 67 | 68 | 69 | -------------------------------------------------------------------------------- /single_mesh_alignment/SingleMeshEnergy1.m: -------------------------------------------------------------------------------- 1 | function [energy] = SingleMeshEnergy1(x, model, A_vertices, nearest_pts_s2a, nearest_ind_s2a, n_smpl) 2 | 3 | [beta, pose, trans, scale] = divideParam(x(1:86)); 4 | [v_shaped, J_shaped] = calShapedMesh(model, beta); 5 | [v_posed] = calPosedMesh(model, pose, v_shaped, J_shaped, 0); 6 | v_posed = repmat(trans, n_smpl, 1) + v_posed * scale; 7 | 8 | energy = 0; 9 | 10 | f = reshape(model.f + 1, 1, numel(model.f)); 11 | eng = v_posed(f, :) - A_vertices(f, :); 12 | eng = norm(eng, 'fro'); 13 | energy = energy + eng; 14 | 15 | end 16 | -------------------------------------------------------------------------------- /single_mesh_alignment/SingleMeshEnergy2.m: -------------------------------------------------------------------------------- 1 | function [energy] = SingleMeshEnergy2(x, model, v_posed, nearest_pts_s2a, nearest_ind_s2a) 2 | 3 | A_vertices = x; 4 | A_vertices = reshape(A_vertices, [3, floor(numel(A_vertices) / 3)])'; 5 | 6 | energy = 0; 7 | 8 | f = reshape(model.f + 1, 1, numel(model.f)); 9 | eng = v_posed(f, :) - A_vertices(f, :); 10 | eng = norm(eng, 'fro'); 11 | energy = energy + eng; 12 | 13 | end 14 | -------------------------------------------------------------------------------- /single_mesh_alignment/SingleMeshEnergy_GMdist.m: -------------------------------------------------------------------------------- 1 | function [energy] = SingleMeshEnergy_GMdist(x, model, A_vertices, nearst_pts_s2a, ... 2 | nearest_ind_s2a, nearest_pts_a2s, mask_a2s) 3 | 4 | A_vertices = x; 5 | v_posed = reshape(A_vertices, [3, floor(numel(A_vertices) / 3)])'; 6 | 7 | sigma = 0.1; 8 | error = nearst_pts_s2a - v_posed(nearest_ind_s2a, :); 9 | 10 | energy = 0; 11 | energy = energy + sum(sum(error.^2 ./ (sigma^2 + error.^2))); 12 | 13 | error = nearest_pts_a2s - v_posed(mask_a2s,:); 14 | energy = energy + sum(sum(error.^2 ./ (sigma^2 + error.^2))); 15 | 16 | end 17 | -------------------------------------------------------------------------------- /single_mesh_alignment/SingleMeshEnergy_OneLoss.m: -------------------------------------------------------------------------------- 1 | function [energy] = SingleMeshEnergy_OneLoss(x, model, nearest_pts_s2a, nearest_ind_s2a) 2 | 3 | [beta, pose, trans, scale] = dividePara(x(1:86)); 4 | A_vertices = x(87:end); 5 | [v_shaped, j_shaped] = calShapedMesh(model, beta); 6 | [v_posed] = calPosedMesh(model, pose, v_shaped, j_shaped, 0); 7 | 8 | v_posed = repmat(trans, 6890, 1) + v_posed * scale; 9 | A_vertices = reshape(A_vertices, [floor(numel(A_vertices) / 3), 3]); 10 | 11 | error = nearest_pts_s2a - A_vertices(nearest_ind_s2a, :); 12 | Eg = 0; 13 | Ec = 0; 14 | wg = 2; 15 | wc = 1; 16 | sigma = 0.01; 17 | Eg = Eg + sum(sum(error.^2 ./ (error.^2 + sigma^2))); 18 | f = reshape(model.f+1, 1, numel(model.f)); 19 | eng = v_posed(f,:) - A_vertices(f,:); 20 | eng = norm(eng, 'fro'); 21 | Ec = Ec + eng; 22 | energy = Eg * wg + Ec * wc; 23 | 24 | end 25 | -------------------------------------------------------------------------------- /single_mesh_alignment/estimate_fitting.m: -------------------------------------------------------------------------------- 1 | function [param] = estimate_fitting(smpl_model, vertices, normals, max_iter, if_shape, ... 2 | param_init, mesh_prefix, result_dir) 3 | 4 | param = param_init; 5 | params_per_iter = []; 6 | 7 | n_smpl = size(smpl_model.v_template, 1); 8 | 9 | mesh_tmp_dir = [result_dir, filesep, mesh_prefix, '_fit_tmp']; 10 | mkdir(mesh_tmp_dir); 11 | 12 | for loop = 1 : max_iter 13 | [betas, pose, trans, scale] = divideParam(param); 14 | [v_shaped, j_shaped] = calShapedMesh(smpl_model, betas); 15 | [v_posed] = calPosedMesh(smpl_model, pose, v_shaped, j_shaped, 0); 16 | v_posed = repmat(trans, n_smpl, 1) + v_posed * scale; 17 | normal_posed = calNormal(smpl_model.f + 1, v_posed); 18 | 19 | params_per_iter = [params_per_iter; param]; 20 | 21 | nearest_ind_d2m = knnsearch(v_posed, vertices); 22 | error_n = dot(normals.', normal_posed(nearest_ind_d2m, :).').'; 23 | mask_d2m = find(error_n > 0); 24 | % rand_select = randperm(length(mask_d2m)); 25 | % mask_d2m = mask_d2m(rand_select(1:round(length(rand_select)/3))); 26 | fprintf('normal match number %d\n', length(mask_d2m)); 27 | nearest_pts_d2m = vertices(mask_d2m, :); 28 | nearest_ind_d2m = nearest_ind_d2m(mask_d2m); 29 | 30 | nearest_ind_m2d = knnsearch(vertices, v_posed); 31 | nearest_pts_m2d = vertices(nearest_ind_m2d, :); 32 | error_n = dot(normals(nearest_ind_m2d, :).', normal_posed.').'; 33 | mask_m2d = find(error_n > 0); 34 | % rand_select = randperm(length(mask_m2d)); 35 | % mask_m2d = mask_m2d(rand_select(1:round(length(rand_select)/3))); 36 | nearest_ind_m2d = nearest_ind_m2d(mask_m2d); 37 | nearest_pts_m2d = nearest_pts_m2d(mask_m2d, :); 38 | 39 | options = optimoptions('fmincon', 'GradObj', 'off', 'Display', ... 40 | 'iter-detailed', 'MaxIter', 10); 41 | mask = [1:10, 86]; 42 | % mask = [1:85]; 43 | I_matrix = eye(86); 44 | A_eq = I_matrix(mask, :); 45 | b_eq = param(mask)'; 46 | 47 | para_opt = fmincon(@(x) ICPEnergy(x, smpl_model, nearest_pts_d2m, nearest_ind_d2m, ... 48 | nearest_pts_m2d, mask_m2d, n_smpl), param, [], [], A_eq, b_eq, [], [], [], options); 49 | 50 | param = para_opt; 51 | 52 | if if_shape 53 | [betas, pose, trans, scale] = divideParam(param); 54 | [v_shaped, j_shaped] = calShapedMesh(smpl_model, betas); 55 | [v_posed] = calPosedMesh(smpl_model, pose, v_shaped, j_shaped, 0); 56 | v_posed = repmat(trans, n_smpl, 1) + v_posed * scale; 57 | normal_posed = calNormal(smpl_model.f+1, v_posed); 58 | 59 | mesh_file = [mesh_tmp_dir, filesep, sprintf('v_iter_pose_%04d.obj', loop - 1)]; 60 | mesh.vertices = v_posed; 61 | mesh.faces = smpl_model.f + 1; 62 | mesh_exporter(mesh_file, mesh); 63 | 64 | params_per_iter = [params_per_iter; param]; 65 | 66 | nearest_ind_d2m = knnsearch(v_posed, vertices); 67 | error_n = dot(normals.', normal_posed(nearest_ind_d2m, :).').'; 68 | mask_d2m = find(error_n > 0.5); 69 | fprintf('normal match number %d\n', length(mask_d2m)); 70 | nearest_pts_d2m = vertices(mask_d2m, :); 71 | nearest_ind_d2m = nearest_ind_d2m(mask_d2m); 72 | 73 | nearest_ind_m2d = knnsearch(vertices,v_posed); 74 | nearest_pts_m2d = vertices(nearest_ind_m2d, :); 75 | error_n = dot(normals(nearest_ind_m2d, :).', normal_posed.').'; 76 | mask_m2d = find(error_n > 0.5); 77 | nearest_ind_m2d = nearest_ind_m2d(mask_m2d); 78 | nearest_pts_m2d = nearest_pts_m2d(mask_m2d, :); 79 | 80 | options = optimoptions('fmincon', 'GradObj', 'off', 'Display', ... 81 | 'iter-detailed', 'MaxIter', 10); 82 | mask = [11:82, 86]; 83 | % mask = [1:85]; 84 | I_matrix = eye(86); 85 | A_eq = I_matrix(mask, :); 86 | b_eq = param(mask)'; 87 | 88 | para_opt = fmincon(@(x) ICPEnergy(x, smpl_model, nearest_pts_d2m, nearest_ind_d2m, ... 89 | nearest_pts_m2d, mask_m2d, n_smpl), param, [], [], A_eq, b_eq, [], [], [], options); 90 | 91 | param = para_opt; 92 | end 93 | 94 | param_file = [mesh_tmp_dir, filesep, sprintf('param_iter_%04d.mat', loop - 1)]; 95 | save(param_file, 'param'); 96 | 97 | end 98 | 99 | end 100 | 101 | function [energy] = ICPEnergy(x, model, nearest_pts_d2m, nearest_ind_d2m, ... 102 | nearest_pts_m2d, mask_m2d, n_smpl) 103 | 104 | [betas, pose, trans, scale] = divideParam(x); 105 | [v_shaped, j_shaped] = calShapedMesh(model, betas); 106 | [v_posed] = calPosedMesh(model, pose, v_shaped, j_shaped, 0); 107 | v_posed = repmat(trans, n_smpl, 1) + v_posed * scale; 108 | 109 | error = nearest_pts_d2m - v_posed(nearest_ind_d2m, :); 110 | energy = 0; 111 | 112 | sigma = 0.1; 113 | energy = energy + sum(sum(error.^2 ./ (error.^2 + sigma^2))); 114 | 115 | error = nearest_pts_m2d - v_posed(mask_m2d, :); 116 | energy = energy + sum(sum(error.^2 ./ (error.^2 + sigma^2))); 117 | 118 | end 119 | -------------------------------------------------------------------------------- /single_mesh_alignment/estimate_trans.m: -------------------------------------------------------------------------------- 1 | function [param] = estimate_trans(smpl_model, vertices, normals, max_iter, param_init) 2 | 3 | param = param_init; 4 | params_per_iter = []; 5 | 6 | n_smpl = size(smpl_model.v_template, 1); 7 | 8 | for loop = 1 : max_iter 9 | [betas, pose, trans, scale] = divideParam(param); 10 | [v_shaped, j_shaped] = calShapedMesh(smpl_model, betas); 11 | [v_posed] = calPosedMesh(smpl_model, pose, v_shaped, j_shaped, 0); 12 | v_posed = repmat(trans, n_smpl, 1) + v_posed * scale; 13 | trans = mean(vertices, 1) - mean(v_posed, 1); 14 | v_posed = repmat(trans, n_smpl, 1) + v_posed * scale; 15 | normal_posed = calNormal(smpl_model.f+1, v_posed); 16 | 17 | params_per_iter = [params_per_iter; param]; 18 | 19 | nearest_ind_d2m = knnsearch(v_posed, vertices); 20 | error_n = dot(normals.', normal_posed(nearest_ind_d2m, :).').'; 21 | mask_d2m = find(error_n > 0.5); 22 | % rand_select = randperm(length(mask_d2m)); 23 | % mask_d2m = mask_d2m(rand_select(1:round(length(rand_select)/3))); 24 | fprintf('normal match number %d\n', length(mask_d2m)); 25 | nearest_pts_d2m = vertices(mask_d2m, :); 26 | nearest_ind_d2m = nearest_ind_d2m(mask_d2m); 27 | 28 | nearest_ind_m2d = knnsearch(vertices, v_posed); 29 | nearest_pts_m2d = vertices(nearest_ind_m2d, :); 30 | error_n = dot(normals(nearest_ind_m2d,:).', normal_posed.').'; 31 | mask_m2d = find(error_n > 0.8); 32 | % rand_select = randperm(length(mask_m2d)); 33 | % mask_m2d = mask_m2d(rand_select(1:round(length(rand_select)/3))); 34 | nearest_ind_m2d = nearest_ind_m2d(mask_m2d); 35 | nearest_pts_m2d = nearest_pts_m2d(mask_m2d, :); 36 | 37 | options = optimoptions('fmincon', 'GradObj', 'off', ... 38 | 'Display', 'iter-detailed', 'MaxIter', 20); 39 | mask = [1:82, 86]; 40 | % mask = [1:85]; 41 | I_matrix = eye(86); 42 | A_eq = I_matrix(mask, :); 43 | b_eq = param(mask)'; 44 | 45 | para_opt = fmincon(@(x) ICPEnergy(x, smpl_model, nearest_pts_d2m, nearest_ind_d2m, ... 46 | nearest_pts_m2d, mask_m2d, n_smpl), param, [], [], A_eq, b_eq, [], [], [], options); 47 | 48 | param = para_opt; 49 | end 50 | 51 | end 52 | 53 | function [energy] = ICPEnergy(x, model, nearest_pts_d2m, nearest_ind_d2m, ... 54 | nearest_pts_m2d, mask_m2d, n_smpl) 55 | 56 | [betas, pose, trans, scale] = divideParam(x); 57 | [v_shaped, j_shaped] = calShapedMesh(model, betas); 58 | [v_posed] = calPosedMesh(model, pose, v_shaped, j_shaped, 0); 59 | v_posed = repmat(trans, n_smpl, 1) + v_posed * scale; 60 | 61 | error = nearest_pts_d2m - v_posed(nearest_ind_d2m, :); 62 | energy = 0; 63 | 64 | sigma = 0.2; 65 | energy = energy + sum(sum(error.^2 ./ (error.^2 + sigma^2))); 66 | 67 | error = nearest_pts_m2d - v_posed(mask_m2d, :); 68 | energy = energy + sum(sum(error.^2 ./ (error.^2 + sigma^2))); 69 | 70 | end 71 | -------------------------------------------------------------------------------- /single_mesh_alignment/single_mesh_align.m: -------------------------------------------------------------------------------- 1 | function [m_smpl, m_A, param] = single_mesh_align(mesh_scan, mesh_smpl, param_init) 2 | 3 | global is_first; 4 | global smpl_model; 5 | global mesh_prefix; 6 | global mesh_prefix_last; 7 | global result_dir; 8 | 9 | % vertices in SMPL model 10 | n_smpl = size(smpl_model.v_template, 1); 11 | 12 | max_iter = 20; 13 | if is_first == 0 14 | max_iter = 10; 15 | end 16 | 17 | mesh_tmp_dir = [result_dir, filesep, mesh_prefix, '_align_tmp']; 18 | mkdir(mesh_tmp_dir); 19 | 20 | A_init_vertices = mesh_smpl.vertices; 21 | A_init_faces = mesh_smpl.faces; 22 | 23 | scan_vertices = mesh_scan.vertices; 24 | scan_faces = mesh_scan.faces; 25 | 26 | param = param_init; 27 | beta = param(1:10); 28 | pose = param(11:82); 29 | trans = param(83:85); 30 | scale = 1; 31 | 32 | [A_init_normals] = calNormal(A_init_faces, A_init_vertices); 33 | [scan_normals] = calNormal(scan_faces, scan_vertices); 34 | 35 | param_init = param; 36 | param = param_init; 37 | 38 | A_vertices = A_init_vertices; 39 | 40 | for loop = 1 : max_iter 41 | A_normals = calNormal(A_init_faces, A_vertices); 42 | m_name = sprintf([mesh_tmp_dir, filesep, 'A1_iter_%04d.obj'], loop-1); 43 | if exist(m_name, 'file') 44 | m_name = sprintf([mesh_tmp_dir, filesep, 'A1_iter_%04d_%3f.obj'], loop-1, now); 45 | end 46 | m.vertices = A_vertices; 47 | m.faces = A_init_faces; 48 | mesh_exporter(m_name, m); 49 | 50 | 51 | %% 1 52 | nearest_ind_s2a = knnsearch(A_vertices, scan_vertices); 53 | error_n = dot(scan_normals.', A_normals(nearest_ind_s2a, :).').'; 54 | mask_s2a = find(error_n > 0); 55 | fprintf('normal match number %d\n', length(mask_s2a)); 56 | nearest_pts_s2a = scan_vertices(mask_s2a, :); 57 | nearest_ind_s2a = nearest_ind_s2a(mask_s2a); 58 | 59 | nearest_ind_a2s = knnsearch(scan_vertices, A_vertices); 60 | error_n = dot(scan_normals(nearest_ind_a2s,:).', A_normals.').'; 61 | mask_a2s = find(error_n > 0); 62 | rand_ind = randperm(length(mask_a2s)); 63 | mask_a2s = mask_a2s(rand_ind(1:min(2000, length(mask_a2s)))); 64 | nearest_pts_a2s = scan_vertices(nearest_ind_a2s, :); 65 | nearest_pts_a2s = nearest_pts_a2s(mask_a2s,:); 66 | 67 | % solve param1 68 | param1 = [beta, pose, trans, scale]; 69 | options = optimoptions(@lsqnonlin, 'Algorithm', 'levenberg-marquardt', ... 70 | 'Display', 'iter-detailed', 'MaxIter', 10); 71 | param_opt1 = lsqnonlin(@(x) SingleMeshEnergy1(x, smpl_model, A_vertices, nearest_pts_s2a, ... 72 | nearest_ind_s2a, n_smpl), param1, [], [], options); 73 | 74 | 75 | %% 2 76 | [beta, pose, trans, scale] = divideParam(param_opt1(1:86)); 77 | [v_shaped, j_shaped] = calShapedMesh(smpl_model, beta); 78 | [v_posed] = calPosedMesh(smpl_model, pose, v_shaped, j_shaped, 0); 79 | v_posed = repmat(trans, n_smpl, 1) + v_posed * scale; 80 | 81 | m_name = sprintf([mesh_tmp_dir, filesep, 'Model_iter_%04d.obj'], loop-1); 82 | if exist(m_name, 'file') 83 | m_name = sprintf([mesh_tmp_dir, filesep, 'Model_iter_%04d_%3f.obj'], loop-1, now); 84 | end 85 | m.vertices = v_posed; 86 | m.faces = smpl_model.f + 1; 87 | mesh_exporter(m_name, m); 88 | 89 | % solve param2 90 | A_vertices = reshape(A_vertices', [numel(A_vertices), 1]); 91 | param2 = A_vertices; 92 | % options = optimoptions(@lsqnonlin, 'Algorithm', 'levenberg-marquardt', ... 93 | % 'Display', 'iter-detailed', 'MaxIter', 10, 'MaxFunEvals', 40000); 94 | options = optimoptions(@fminunc, 'Algorithm', 'quasi-newton', ... 95 | 'Display', 'iter-detailed', 'MaxIter', 10, 'MaxFunEvals', 1000000); 96 | param_opt2 = fminunc(@(x) SingleMeshEnergy2(x, smpl_model, v_posed, nearest_pts_s2a, ... 97 | nearest_ind_s2a), param2, options); 98 | 99 | 100 | %% 3 101 | A_vertices = param_opt2; 102 | A_vertices = reshape(A_vertices, [3, floor(numel(A_vertices)/ 3)])'; 103 | 104 | m_name = sprintf([mesh_tmp_dir, filesep, 'A2_iter_%04d.obj'], loop-1); 105 | if exist(m_name, 'file') 106 | m_name = sprintf([mesh_tmp_dir, filesep, 'A2_iter_%04d_%3f.obj'], loop-1, now); 107 | end 108 | m.vertices = A_vertices; 109 | m.faces = A_init_faces; 110 | mesh_exporter(m_name, m); 111 | 112 | mask_s2a2 = unique(nearest_ind_s2a); 113 | mask = union(mask_a2s, mask_s2a2); 114 | mask = setdiff((1:size(A_vertices, 1)), mask); 115 | mask1 = (mask - 1) * 3 + 1; 116 | mask2 = (mask - 1) * 3 + 2; 117 | mask3 = (mask - 1) * 3 + 3; 118 | mask = [mask1, mask2, mask3]; 119 | 120 | A_vertices = reshape(A_vertices', [numel(A_vertices), 1]); 121 | param = A_vertices; 122 | I_matrix = eye(numel(param)); 123 | A_eq = I_matrix(mask,:); 124 | b_eq = param(mask,:); 125 | 126 | % para_opt = lsqnonlin(@(x) SingleMeshEnergy2(x,model,A_pts, nearstPts_s2a,nearstIdx_s2a),para, [], [], options); 127 | 128 | options = optimoptions('fmincon', 'GradObj', 'off', 'Display', 'iter-detailed', ... 129 | 'MaxIter', 10, 'MaxFunEvals', 300000); 130 | param_opt = fmincon(@(x) SingleMeshEnergy_GMdist(x, smpl_model, A_vertices, nearest_pts_s2a, ... 131 | nearest_ind_s2a, nearest_pts_a2s, mask_a2s), param, [], [], A_eq, b_eq, [], [], [], options); 132 | 133 | A_vertices = reshape(param_opt, [3, floor(numel(param_opt)/ 3)])'; 134 | 135 | m_name = sprintf([mesh_tmp_dir, filesep, 'A3_iter_%d.obj'], loop-1); 136 | if exist(m_name, 'file') 137 | m_name = sprintf([mesh_tmp_dir, filesep, 'A3_iter_%d_%3f.obj'], loop-1, now); 138 | end 139 | m.vertices = A_vertices; 140 | m.faces = A_init_faces; 141 | mesh_exporter(m_name, m); 142 | 143 | end 144 | 145 | [v_shaped, j_shaped] = calShapedMesh(smpl_model, beta); 146 | [v_posed] = calPosedMesh(smpl_model, pose, v_shaped, j_shaped, 0); 147 | v_posed = repmat(trans, n_smpl, 1) + v_posed * scale; 148 | 149 | % fitted and aligned SMPL 150 | m_smpl.vertices = v_posed; 151 | m_smpl.faces = smpl_model.f + 1; 152 | mesh_smpl_file = [result_dir, filesep, mesh_prefix, '_aligned_SMPL.obj']; 153 | mesh_exporter(mesh_smpl_file, m_smpl); 154 | 155 | % fitted and aligned A 156 | m_A.vertices = A_vertices; 157 | m_A.faces = smpl_model.f + 1; 158 | mesh_A_file = [result_dir, filesep, mesh_prefix, '_aligned_A.obj']; 159 | mesh_exporter(mesh_A_file, m_A); 160 | 161 | % SMPL parameters 162 | param_smpl_file = [result_dir, filesep, mesh_prefix, '_aligned_param.mat']; 163 | param = [beta, pose, trans, scale]; 164 | save(param_smpl_file, 'param') 165 | 166 | mesh_prefix_last = param_smpl_file; 167 | 168 | end 169 | 170 | -------------------------------------------------------------------------------- /single_mesh_alignment/single_mesh_fitting.m: -------------------------------------------------------------------------------- 1 | function [param_fitted, mesh_fitted] = single_mesh_fitting(mesh, param_init) 2 | 3 | global is_first; 4 | global smpl_model; 5 | global mesh_prefix; 6 | global result_dir; 7 | 8 | % vertices in SMPL model 9 | n_smpl = size(smpl_model.v_template, 1); 10 | 11 | max_iter = 20; 12 | if_shape = 1; 13 | 14 | % not first frame 15 | if is_first == 0 16 | max_iter = 10; 17 | if_shape = 0; 18 | end 19 | 20 | param_tmp = param_init; 21 | param_tmp(end) = 1; 22 | 23 | param_fitted = estimate_fitting(smpl_model, mesh.vertices, mesh.normals, ... 24 | max_iter, if_shape, param_tmp, mesh_prefix, result_dir); 25 | 26 | [betas, pose, trans, scale] = divideParam(param_fitted); 27 | [v_shaped, j_shaped] = calShapedMesh(smpl_model, betas); 28 | [v_posed] = calPosedMesh(smpl_model, pose, v_shaped, j_shaped, 0); 29 | [v_posed] = repmat(trans, n_smpl, 1) + v_posed * scale; 30 | 31 | mesh_fitted.vertices = v_posed; 32 | mesh_fitted.faces = smpl_model.f + 1; 33 | 34 | mesh_file = [result_dir, filesep, mesh_prefix, '_fit.obj']; 35 | param_file = [result_dir, filesep, mesh_prefix, '_fit_param.mat']; 36 | 37 | mesh_exporter(mesh_file, mesh_fitted); 38 | param = param_fitted; 39 | save(param_file, 'param'); 40 | 41 | end 42 | 43 | -------------------------------------------------------------------------------- /single_mesh_alignment/single_mesh_scale.m: -------------------------------------------------------------------------------- 1 | function [mesh_scaled] = single_mesh_scale(mesh, scale) 2 | 3 | global mesh_prefix; 4 | global result_dir; 5 | 6 | mesh_scaled = mesh; 7 | mesh_scaled.vertices = mesh.vertices ./ scale; 8 | 9 | mesh_exporter([result_dir, filesep, mesh_prefix, '_scale.obj'], mesh_scaled); 10 | 11 | end 12 | -------------------------------------------------------------------------------- /single_mesh_alignment/single_mesh_trans.m: -------------------------------------------------------------------------------- 1 | function [param] = single_mesh_trans(mesh) 2 | 3 | global smpl_model; 4 | global mesh_prefix; 5 | global mesh_prefix_last; 6 | global result_dir; 7 | global result_dir_base; 8 | 9 | max_iter = 1; 10 | 11 | % vertices in SMPL model 12 | n_smpl = size(smpl_model.v_template, 1); 13 | 14 | n_rand = size(mesh.normals, 1); 15 | rand_ind = randperm(size(mesh.vertices, 1)); 16 | 17 | vertices = mesh.vertices(rand_ind(1:n_rand), :); 18 | normals = mesh.normals(rand_ind(1:n_rand), :); 19 | 20 | % no previous frame, init param 21 | if isempty(mesh_prefix_last) 22 | betas = zeros(1, 10); 23 | pose = zeros(1, 72); 24 | pose(2) = pi; 25 | pose(51) = -5 * pi/24; 26 | pose(54) = 5 * pi/24; 27 | trans = zeros(1, 3); 28 | trans(2) = 1.1448; 29 | scale = 1; 30 | param = combineParam(betas, pose, trans, scale); 31 | else 32 | param_path = [result_dir_base, filesep, mesh_prefix_last]; 33 | param_name = [mesh_prefix_last, '_fit_param.mat']; 34 | param = load([param_path, filesep, param_name]); 35 | param = param.param; 36 | end 37 | 38 | [betas, pose, trans, scale] = divideParam(param); 39 | [v_shaped, j_shaped] = calShapedMesh(smpl_model, betas); 40 | [v_posed] = calPosedMesh(smpl_model, pose, v_shaped, j_shaped, 0); 41 | [v_posed] = repmat(trans, n_smpl, 1) + v_posed * scale; 42 | 43 | mesh_out.vertices = v_posed; 44 | mesh_out.faces = smpl_model.f + 1; 45 | 46 | mesh_file = [result_dir, filesep, mesh_prefix, '_init.obj']; 47 | mesh_exporter(mesh_file, mesh_out); 48 | 49 | param = estimate_trans(smpl_model, vertices, normals, max_iter, param); 50 | 51 | [betas, pose, trans, scale] = divideParam(param); 52 | [v_shaped, j_shaped] = calShapedMesh(smpl_model, betas); 53 | [v_posed] = calPosedMesh(smpl_model, pose, v_shaped, j_shaped, 0); 54 | [v_posed] = repmat(trans, n_smpl, 1) + v_posed * scale; 55 | 56 | mesh_out.vertices = v_posed; 57 | mesh_out.faces = smpl_model.f + 1; 58 | 59 | mesh_file = [result_dir, filesep, mesh_prefix, '_trans.obj']; 60 | param_file = [result_dir, filesep, mesh_prefix, '_trans_param.mat']; 61 | 62 | mesh_exporter(mesh_file, mesh_out); 63 | save(param_file, 'param'); 64 | 65 | end -------------------------------------------------------------------------------- /smpl_model/smpl_f.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jizhu1023/ClothCap/5ca0c8a474a3a6eec053db8ebbd0359a393cb4b2/smpl_model/smpl_f.mat -------------------------------------------------------------------------------- /smpl_model/smpl_m.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jizhu1023/ClothCap/5ca0c8a474a3a6eec053db8ebbd0359a393cb4b2/smpl_model/smpl_m.mat -------------------------------------------------------------------------------- /summary.md: -------------------------------------------------------------------------------- 1 | # ClothCap 2 | 3 | ## Segmentation 4 | 5 | ### 原始方法 6 | 1. segmentation这一步的作用在于对scan的序列进行`per-vertex`的分割,得到每个顶点所属的类别(上衣、裤子、皮肤)。按照paper的说法由于实际获取的scan序列含有噪声等因素,直接对scan做分割效果不好,所以先采用`single-mesh-alignment`的方法,使用`SMPL`模型去fit每一帧的scan,得到一个序列的alignments,然后在此基础上做分割。 7 | 2. segmentation的主要方法是使用`MRF(Markov Random Field)`来解分割的优化问题,其中的能量函数分为两项,unary term和pairwise term,详细的介绍见paper。简略的来说就是unary term主要是计算每个顶点的energy,pairwise term根据顶点的拓扑关系计算每一对顶点的energy,这两项energy均与每个顶点的label相关,因此优化最小化总体energy就可以获得每个顶点的label。这是一个很典型的MRF或CRF问题。 8 | 9 | ### 问题与解决 10 | > - 在paper的描述中,由第一步single-mesh-alignment得到了对应于原始序列scan的一系列alignments,接下来在segmentation这一步中,采用的是对alignments分割的方法,而不是对scan进行分割。因此在计算unary term时,采用了寻找最近邻点(KNN)的方法,从scan中获取颜色信息来计算unary term。由于在single-mesh-alignment这一步中,获得的alignments并不完全准确(fit效果不好),因此在计算unary时就会出现误差,从而影响分割结果。 11 | > - 现有的解CRF或MRF的库主要都是应用在图像分割上,因此拓展到3D Mesh场景下有一定困难,传统方法如meanfield等效果不佳。造成困难的原因在于这些库没有充分利用上3D Mesh的拓扑连接关系,因此无法很好地解决问题 12 | 13 | 针对以上问题采用的相应的解决方法为: 14 | 15 | 1. 直接对scan序列进行分割,这样就不再需要第一步single-mesh-alignment的结果,根据实验结果来看直接对scan做分割比按照paper里的方法来做效果要好。可能的原因在于咱们自己的scan序列本身效果就很好,因此直接分割可以有较好的结果。 16 | 2. 对于解优化问题,最后使用了`gco`库来解决。这个库的优势在于可以手动制定数据的拓扑结构,这样就可以泛华的解决在任意graph上的优化问题。 17 | 18 | ### 关键点 19 | 1. 在paper中第一帧在计算unary时,首先对这一帧采用`kmeans`的方法,利用颜色信息得到一个初始分类,根据这个初始分类label来计算第一帧的unary。而在之后的每一帧,则采用`GMM`的方法来计算unary。这里需要注意的是,**在从第二帧之后开始,每一帧计算unary都需要训练一个GMM模型,而训练数据应该使用之前所有帧的分割label,而不是只采用前一帧的分割label**,这样可以保证随着分割的不断进行,每一帧训练得到的GMM效果会更加的趋于稳定,特别是在衣服皮肤颜色区别不是很大的时候,也会有较好的结果。 20 | 2. 分割主要利用的是HSV颜色特征,之前曾加入了SDF(Shape Diameter Function)特征,但是在某些情况下会影响分割结果,比如手的上臂和小腿部分,两者的sdf特征区别就很不明显,会引入误分类的问题。 21 | 22 | 23 | -------------------------------------------------------------------------------- /transfer/cplusplus/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(mesh_parser) 4 | 5 | if(NOT WIN32) 6 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") 7 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 8 | add_definitions(-D_GNU_SOURCE -fPIC) 9 | endif() 10 | 11 | find_package(glm REQUIRED) 12 | include_directories(${glm_INCLUDE_DIRS}) 13 | 14 | find_package(Boost REQUIRED) 15 | include_directories(${Boost_INCLUDE_DIRS}) 16 | link_libraries(${Boost_LIBRARIES}) 17 | 18 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") 19 | find_package(OpenMesh REQUIRED) 20 | include_directories(${OPENMESH_INCLUDE_DIRS}) 21 | link_libraries(${OPENMESH_LIBRARIES}) 22 | 23 | add_executable(transfer main.cpp OptionParser.cpp transfer.cpp) 24 | target_link_libraries(transfer boost_system boost_filesystem) -------------------------------------------------------------------------------- /transfer/cplusplus/OptionParser.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2010 Johannes Weißl 3 | * License: MIT License 4 | * URL: https://github.com/weisslj/cpp-optparse 5 | */ 6 | 7 | #ifndef OPTIONPARSER_H_ 8 | #define OPTIONPARSER_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace optparse { 19 | 20 | class OptionParser; 21 | class OptionGroup; 22 | class Option; 23 | class Values; 24 | class Value; 25 | class Callback; 26 | 27 | typedef std::map strMap; 28 | typedef std::map > lstMap; 29 | typedef std::map optMap; 30 | 31 | const char* const SUPPRESS_HELP = "SUPPRESS" "HELP"; 32 | const char* const SUPPRESS_USAGE = "SUPPRESS" "USAGE"; 33 | 34 | //! Class for automatic conversion from string -> anytype 35 | class Value { 36 | public: 37 | Value() : str(), valid(false) {} 38 | Value(const std::string& v) : str(v), valid(true) {} 39 | operator const char*() { return str.c_str(); } 40 | operator bool() { bool t; return (valid && (std::istringstream(str) >> t)) ? t : false; } 41 | operator short() { short t; return (valid && (std::istringstream(str) >> t)) ? t : 0; } 42 | operator unsigned short() { unsigned short t; return (valid && (std::istringstream(str) >> t)) ? t : 0; } 43 | operator int() { int t; return (valid && (std::istringstream(str) >> t)) ? t : 0; } 44 | operator unsigned int() { unsigned int t; return (valid && (std::istringstream(str) >> t)) ? t : 0; } 45 | operator long() { long t; return (valid && (std::istringstream(str) >> t)) ? t : 0; } 46 | operator unsigned long() { unsigned long t; return (valid && (std::istringstream(str) >> t)) ? t : 0; } 47 | operator float() { float t; return (valid && (std::istringstream(str) >> t)) ? t : 0; } 48 | operator double() { double t; return (valid && (std::istringstream(str) >> t)) ? t : 0; } 49 | operator long double() { long double t; return (valid && (std::istringstream(str) >> t)) ? t : 0; } 50 | private: 51 | const std::string str; 52 | bool valid; 53 | }; 54 | 55 | class Values { 56 | public: 57 | Values() : _map() {} 58 | const std::string& operator[] (const std::string& d) const; 59 | std::string& operator[] (const std::string& d) { return _map[d]; } 60 | bool is_set(const std::string& d) const { return _map.find(d) != _map.end(); } 61 | bool is_set_by_user(const std::string& d) const { return _userSet.find(d) != _userSet.end(); } 62 | void is_set_by_user(const std::string& d, bool yes); 63 | Value get(const std::string& d) const { return (is_set(d)) ? Value((*this)[d]) : Value(); } 64 | 65 | typedef std::list::iterator iterator; 66 | typedef std::list::const_iterator const_iterator; 67 | std::list& all(const std::string& d) { return _appendMap[d]; } 68 | const std::list& all(const std::string& d) const { return _appendMap.find(d)->second; } 69 | 70 | private: 71 | strMap _map; 72 | lstMap _appendMap; 73 | std::set _userSet; 74 | }; 75 | 76 | class Option { 77 | public: 78 | Option(const OptionParser& p) : 79 | _parser(p), _action("store"), _type("string"), _nargs(1), _callback(0) {} 80 | virtual ~Option() {} 81 | 82 | Option& action(const std::string& a); 83 | Option& type(const std::string& t); 84 | Option& dest(const std::string& d) { _dest = d; return *this; } 85 | Option& set_default(const std::string& d) { _default = d; return *this; } 86 | template 87 | Option& set_default(T t) { std::ostringstream ss; ss << t; _default = ss.str(); return *this; } 88 | Option& nargs(size_t n) { _nargs = n; return *this; } 89 | Option& set_const(const std::string& c) { _const = c; return *this; } 90 | template 91 | Option& choices(InputIterator begin, InputIterator end) { 92 | _choices.assign(begin, end); type("choice"); return *this; 93 | } 94 | #if __cplusplus >= 201103L 95 | Option& choices(std::initializer_list ilist) { 96 | _choices.assign(ilist); type("choice"); return *this; 97 | } 98 | #endif 99 | Option& help(const std::string& h) { _help = h; return *this; } 100 | Option& metavar(const std::string& m) { _metavar = m; return *this; } 101 | Option& callback(Callback& c) { _callback = &c; return *this; } 102 | 103 | const std::string& action() const { return _action; } 104 | const std::string& type() const { return _type; } 105 | const std::string& dest() const { return _dest; } 106 | const std::string& get_default() const; 107 | size_t nargs() const { return _nargs; } 108 | const std::string& get_const() const { return _const; } 109 | const std::list& choices() const { return _choices; } 110 | const std::string& help() const { return _help; } 111 | const std::string& metavar() const { return _metavar; } 112 | Callback* callback() const { return _callback; } 113 | 114 | private: 115 | std::string check_type(const std::string& opt, const std::string& val) const; 116 | std::string format_option_help(unsigned int indent = 2) const; 117 | std::string format_help(unsigned int indent = 2) const; 118 | 119 | const OptionParser& _parser; 120 | 121 | std::set _short_opts; 122 | std::set _long_opts; 123 | 124 | std::string _action; 125 | std::string _type; 126 | std::string _dest; 127 | std::string _default; 128 | size_t _nargs; 129 | std::string _const; 130 | std::list _choices; 131 | std::string _help; 132 | std::string _metavar; 133 | Callback* _callback; 134 | 135 | friend class OptionContainer; 136 | friend class OptionParser; 137 | }; 138 | 139 | class OptionContainer { 140 | public: 141 | OptionContainer(const std::string& d = "") : _description(d) {} 142 | virtual ~OptionContainer() {} 143 | 144 | virtual OptionContainer& description(const std::string& d) { _description = d; return *this; } 145 | virtual const std::string& description() const { return _description; } 146 | 147 | Option& add_option(const std::string& opt); 148 | Option& add_option(const std::string& opt1, const std::string& opt2); 149 | Option& add_option(const std::string& opt1, const std::string& opt2, const std::string& opt3); 150 | Option& add_option(const std::vector& opt); 151 | 152 | std::string format_option_help(unsigned int indent = 2) const; 153 | 154 | protected: 155 | std::string _description; 156 | 157 | std::list