├── src ├── TriMesh.cc ├── TriMesh.h ├── DeformGraph.h ├── DeformGraphTest.cpp └── DeformGraph.cpp ├── DeformationGraph.vcxproj.user ├── .gitignore ├── README.md ├── DeformationGraph.vcxproj.filters └── DeformationGraph.vcxproj /src/TriMesh.cc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JiaxiangZheng/DeformationGraph/HEAD/src/TriMesh.cc -------------------------------------------------------------------------------- /DeformationGraph.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DeformationGraph 2 | ================ 3 | 4 | A implemetation of deformation graph which was widely used in mesh deformation and non-rigid reconstruction. I write this for the reason that I will use it in later animation reconstruction work. 5 | 6 | ##about the code## 7 | [Eigen](http://eigen.tuxfamily.org), [FLANN](http://www.cs.ubc.ca/~mariusm/index.php/FLANN/FLANN) and [PCL](http://www.pointclouds.org/) are used as third party library. 8 | 9 | `DeformGraph.h` and `DeformGraph.cpp` contains the core code of deformation 10 | graph, and `DeformGraphTest.cpp` is an example for how to use the code. It 11 | should be note that the initial data should be aligned together and normalized. 12 | 13 | 14 | -------------------------------------------------------------------------------- /DeformationGraph.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/TriMesh.h: -------------------------------------------------------------------------------- 1 | #ifndef _TRI_MESH_HH_ 2 | #define _TRI_MESH_HH_ 3 | #include 4 | #include 5 | using namespace std; 6 | #include 7 | 8 | #define ERROR_LOG(ERROR_INFO) \ 9 | fprintf(stderr, "%s\t%s\t%d\n", \ 10 | ERROR_INFO, __FILE__, __LINE__) 11 | 12 | #define DATA_09 13 | typedef enum DisplayMode {POINT_MODE, EDGE_MODE, FACE_MODE} DisplayMode; 14 | 15 | class TriMesh 16 | { 17 | public: 18 | struct PolyIndex 19 | { 20 | unsigned int vert_index[3]; 21 | unsigned int norm_index[3]; 22 | }; 23 | vector vertex_coord;// 24 | vector norm_coord; 25 | vector polyIndex; 26 | vector face_norm; 27 | Eigen::Vector3d BoundingBox_Min, BoundingBox_Max; //min max 28 | int vert_num; 29 | int poly_num; 30 | 31 | TriMesh() : vert_num(0), poly_num(0) {} 32 | TriMesh(const char* filename); 33 | void updateNorm(); 34 | void render(DisplayMode mode); 35 | void prepareFaceNeighbours(std::vector>& neighbours); 36 | void saveOBJ(const char* filename); 37 | void savePLY(const char* filename); 38 | void getBoundingBox(Eigen::Vector3d& Min, Eigen::Vector3d& Max); 39 | private: 40 | void readPLY(const char* filename); 41 | void readOBJ(const char* filename); 42 | }; 43 | 44 | struct Skeleton 45 | { 46 | std::vector pos; 47 | std::vector parents; 48 | Skeleton() {} 49 | Skeleton(const char* filename) {read(filename);} 50 | void read(const char* filename); 51 | void render(float sphereSize = 0.05, float lineWidth = 3.0); 52 | }; 53 | 54 | #endif/*_TRI_MESH_HH_*/ 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/DeformGraph.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "TriMesh.h" 5 | 6 | #define dprintf fprintf 7 | 8 | struct DGraph { 9 | const TriMesh* p_mesh; // for data backup 10 | std::vector node_index; 11 | 12 | std::vector node_pos; 13 | std::vector node_norm; 14 | 15 | std::vector> node_neigh; 16 | std::vector node_rots; 17 | std::vector node_trans; 18 | 19 | double max_dist_neigh; // used for building neighbours 20 | int k_neigh; 21 | DGraph() { 22 | p_mesh = NULL; 23 | max_dist_neigh = 0.0; 24 | k_neigh = 0; 25 | } 26 | void build_graph(const TriMesh& mesh, std::vector& sample_index, 27 | double _max_dist_neigh = 0.1, int k_nn = 10); 28 | void build_neigh(); 29 | void update_graph(const Eigen::VectorXd& X); 30 | void deform(TriMesh& d_mesh); 31 | }; 32 | 33 | struct Deformer { 34 | DGraph* p_graph; 35 | std::vector weights; //rot, smooth, regular 36 | std::vector constraints_index; 37 | std::vector node_constraints; 38 | 39 | void init(DGraph& graph, const std::vector& corr_indexs, 40 | const std::vector& cons); 41 | void set_weights(double* _weights, int n) { 42 | weights.resize(n); 43 | for (int i=0; i& fJac); 47 | void build_values(const Eigen::VectorXd& x, Eigen::VectorXd& fVec); 48 | 49 | int rows, cols; 50 | }; 51 | 52 | void mesh_sampling(const TriMesh& mesh, std::vector& sample_index, double max_dist); 53 | double minimize_guass_newton(Deformer& deformer, Eigen::VectorXd& X); 54 | double minimize_levenberg_marquardt(Deformer& deformer, Eigen::VectorXd& X); 55 | double optimize_once(DGraph& graph, std::vector& constraints_index, 56 | const std::vector& cons); //constraints_index is in respect to graph nodes' index 57 | 58 | -------------------------------------------------------------------------------- /DeformationGraph.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | {ACB93890-4121-4A2E-952F-73CAD23E3736} 24 | Win32Proj 25 | DeformationGraph 26 | 27 | 28 | 29 | Application 30 | true 31 | Unicode 32 | 33 | 34 | Application 35 | false 36 | true 37 | Unicode 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | true 51 | false 52 | 53 | 54 | false 55 | 56 | 57 | 58 | 59 | 60 | Level3 61 | Disabled 62 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 63 | D:\Program Files\Eigen\include;D:\Program Files\flann\include;D:\Program Files\PCL 1.6.0\include\pcl-1.6;D:\Program Files\Boost\include; 64 | 65 | 66 | Console 67 | true 68 | D:\Program Files\flann\lib;D:\Program Files\PCL 1.6.0\lib;D:\Program Files\Boost\lib; 69 | flann.lib;pcl_keypoints_debug.lib;pcl_kdtree_debug.lib;pcl_common_debug.lib;pcl_search_debug.lib 70 | 71 | 72 | 73 | 74 | Level3 75 | 76 | 77 | MaxSpeed 78 | true 79 | true 80 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 81 | D:\Program Files\Eigen\include;D:\Program Files\flann\include;D:\Program Files\PCL 1.6.0\include\pcl-1.6;D:\Program Files\Boost\include; 82 | 83 | 84 | Console 85 | true 86 | true 87 | true 88 | D:\Program Files\flann\lib;D:\Program Files\PCL 1.6.0\lib;D:\Program Files\Boost\lib; 89 | flann.lib;pcl_keypoints_release.lib;pcl_kdtree_release.lib;pcl_common_release.lib;pcl_search_release.lib; 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/DeformGraphTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "TriMesh.h" 3 | #include "DeformGraph.h" 4 | /* debug utility functions */ 5 | //////////////////////////////////////////////////////////////////////////////// 6 | static void _write_graphs(const char* filename, const std::vector>& neighs) { 7 | FILE* fp = fopen(filename, "w"); 8 | if (!fp) return; 9 | fprintf(fp, "%d\n", neighs.size()); 10 | for (int i=0; i node_pos, 20 | std::vector node_norm, const char* filename) { 21 | assert (node_pos.size() == node_norm.size()); 22 | FILE* fp = fopen(filename, "w"); 23 | if (!fp) { 24 | fprintf(stderr, "warning : unable to open %s when saving pcd to ply file.\n", filename); 25 | return; 26 | } 27 | fprintf(fp, "ply\n"); 28 | fprintf(fp, "format ascii 1.0\n"); 29 | fprintf(fp, "element vertex %d\n", node_pos.size()); 30 | fprintf(fp, "property float x\n"); 31 | fprintf(fp, "property float y\n"); 32 | fprintf(fp, "property float z\n"); 33 | fprintf(fp, "property float nx\n"); 34 | fprintf(fp, "property float ny\n"); 35 | fprintf(fp, "property float nz\n"); 36 | fprintf(fp, "property uchar red\n"); 37 | fprintf(fp, "property uchar green\n"); 38 | fprintf(fp, "property uchar blue\n"); 39 | fprintf(fp, "end_header\n"); 40 | 41 | for (size_t i=0; i node_pos, 50 | std::vector node_norm, const char* filename) { 51 | TriMesh mesh; 52 | mesh.vert_num = node_norm.size(); 53 | mesh.vertex_coord = node_pos; 54 | mesh.norm_coord = node_norm; 55 | mesh.saveOBJ(filename); 56 | } 57 | //////////////////////////////////////////////////////////////////////////////// 58 | 59 | int main() { 60 | // make sure these meshes are normalized, aligned rigidly and all vertex is valid (normal is valid) 61 | const char* src_filename = "./data/hand_000.obj"; 62 | const char* dst_filename = "./data/hand_050.obj"; 63 | TriMesh mesh_in(src_filename); 64 | TriMesh mesh_out(dst_filename); 65 | 66 | std::vector sampling_index; 67 | double sampling_dist = 0.03; 68 | mesh_sampling(mesh_in, sampling_index, sampling_dist); 69 | 70 | DGraph graph; 71 | graph.build_graph(mesh_in, sampling_index, 2.0*sampling_dist, 8); 72 | _write_graphs("graph.txt", graph.node_neigh); // for debug 73 | 74 | double *raw_dataset = new double[3*mesh_out.vertex_coord.size()]; 75 | for (size_t i=0; i flann_dataset(raw_dataset, mesh_out.vertex_coord.size(), 3); 81 | flann::Index< flann::L2 > flann_index(flann_dataset, flann::KDTreeIndexParams(1)); 82 | flann_index.buildIndex(); 83 | flann::Matrix query_node(new double[3], 1, 3); // num of querys * dimension 84 | flann::Matrix indices_node(new int[1], 1, 1); // num of querys * knn 85 | flann::Matrix dists_node(new double[1], 1, 1); 86 | 87 | double max_dist_neigh = 2.0*sampling_dist; 88 | char save_filename[512]; 89 | int iter = 0; 90 | double pre_energy = DBL_MAX; 91 | do { 92 | // 1. build nearest correspondence from graph to mesh_out 93 | std::vector corres_indexs; std::vector corres_constraints; 94 | for (size_t i=0; i max_dist_neigh*max_dist_neigh) continue; //TODO: replace with a stronger condition for matching pairs 98 | corres_indexs.push_back(i); 99 | corres_constraints.push_back(mesh_out.vertex_coord[indices_node[0][0]]); 100 | } 101 | dprintf(stdout, "%d:\tfind pairs %d\n", iter, corres_indexs.size()); 102 | 103 | // 2. use the correspondence to initialize a Deformer and call optimize_once and update graph 104 | double cur_energy = optimize_once(graph, corres_indexs, corres_constraints); 105 | dprintf(stdout, "min_energy = %lf\n", cur_energy); 106 | if (fabs(pre_energy-cur_energy) < 1e-5) break; 107 | pre_energy = cur_energy; 108 | } while (++iter < 50); 109 | sprintf(save_filename, "d_graph.ply"); 110 | _save_pcd2ply(graph.node_pos, graph.node_norm, save_filename); 111 | 112 | TriMesh source_mesh(src_filename); 113 | graph.deform(source_mesh); 114 | source_mesh.savePLY("deformed_mesh.ply"); 115 | 116 | // FILE* fp = fopen("trans.txt", "w"); 117 | // for (int i=0; i 2 | #include 3 | #include 4 | #include "DeformGraph.h" 5 | 6 | //for debug the graph 7 | static void _save_pcd2ply(std::vector node_pos, 8 | std::vector node_norm, const char* filename) { 9 | assert (node_pos.size() == node_norm.size()); 10 | FILE* fp = fopen(filename, "w"); 11 | if (!fp) { 12 | fprintf(stderr, "warning : unable to open %s when saving pcd to ply file.\n", filename); 13 | return; 14 | } 15 | fprintf(fp, "ply\n"); 16 | fprintf(fp, "format ascii 1.0\n"); 17 | fprintf(fp, "element vertex %d\n", node_pos.size()); 18 | fprintf(fp, "property float x\n"); 19 | fprintf(fp, "property float y\n"); 20 | fprintf(fp, "property float z\n"); 21 | fprintf(fp, "property float nx\n"); 22 | fprintf(fp, "property float ny\n"); 23 | fprintf(fp, "property float nz\n"); 24 | fprintf(fp, "property uchar red\n"); 25 | fprintf(fp, "property uchar green\n"); 26 | fprintf(fp, "property uchar blue\n"); 27 | fprintf(fp, "end_header\n"); 28 | 29 | for (size_t i=0; i node_pos, 38 | std::vector node_norm, const char* filename) { 39 | TriMesh mesh; 40 | mesh.vert_num = node_norm.size(); 41 | mesh.vertex_coord = node_pos; 42 | mesh.norm_coord = node_norm; 43 | mesh.saveOBJ(filename); 44 | } 45 | void DGraph::build_graph(const TriMesh& mesh, std::vector& sample_index, double _max_dist_neigh/* = 0.1 */, int k_nn/* = 10 */) { 46 | p_mesh = &mesh; 47 | node_index.resize(sample_index.size()); 48 | std::copy(sample_index.begin(), sample_index.end(), node_index.begin()); 49 | node_pos.resize(node_index.size()); 50 | node_norm.resize(node_index.size()); 51 | for (size_t i=0; ivertex_coord[node_index[i]]; 53 | node_norm[i] = p_mesh->norm_coord[node_index[i]]; 54 | } 55 | node_rots.resize(node_index.size(), Eigen::Matrix3d::Identity()); 56 | node_trans.resize(node_index.size(), Eigen::Vector3d::Zero()); 57 | 58 | max_dist_neigh = _max_dist_neigh; 59 | k_neigh = k_nn; 60 | build_neigh(); 61 | _save_pcd2ply(node_pos, node_norm, "graph.ply"); // save the graph for debugging 62 | _save_pcd2obj(node_pos, node_norm, "graph.obj"); 63 | } 64 | #include 65 | void DGraph::build_neigh() { 66 | if (k_neigh == 0 || fabs(max_dist_neigh) <= 1e-9) { 67 | fprintf(stderr, "error : set neigh parameters first!!!\n"); 68 | return; 69 | } 70 | double *raw_dataset = new double[3*node_pos.size()]; 71 | for (size_t i=0; i flann_dataset(raw_dataset, node_pos.size(), 3); 77 | flann::Index< flann::L2 > flann_index(flann_dataset, flann::KDTreeIndexParams(1)); 78 | flann_index.buildIndex(); 79 | 80 | flann::Matrix query_node(new double[3], 1, 3); 81 | flann::Matrix indices_node(new int[k_neigh+1], 1, k_neigh+1); 82 | flann::Matrix dists_node(new double[k_neigh+1], 1, k_neigh+1); 83 | 84 | int node_index = 0; 85 | node_neigh.resize(node_pos.size()); 86 | for (size_t i=0; i max_dist_neigh*max_dist_neigh) break; 91 | node_neigh[i].push_back(indices_node[0][j]); 92 | } 93 | } 94 | 95 | delete []query_node.ptr(); 96 | delete []dists_node.ptr(); 97 | delete []indices_node.ptr(); 98 | delete []raw_dataset; 99 | } 100 | void DGraph::update_graph(const Eigen::VectorXd& X) { 101 | int vn = node_pos.size(); 102 | std::vector rots(vn); std::vector trans(vn); 103 | for (int i=0; ip_mesh) { 121 | fprintf(stderr, "error : address conflict, not allow to deform the mesh.\n"); 122 | return; 123 | } 124 | 125 | double *raw_dataset = new double[3*node_pos.size()]; 126 | for (size_t i=0; i flann_dataset(raw_dataset, node_pos.size(), 3); 132 | flann::Index< flann::L2 > flann_index(flann_dataset, flann::KDTreeIndexParams(1)); 133 | flann_index.buildIndex(); 134 | 135 | int knn = this->k_neigh; double max_dist = this->max_dist_neigh; 136 | flann::Matrix query_node(new double[3], 1, 3); 137 | flann::Matrix indices_node(new int[knn+1], 1, knn+1); 138 | flann::Matrix dists_node(new double[knn+1], 1, knn+1); 139 | 140 | FILE* fp = fopen("weights.txt", "w"); 141 | std::vector weights; 142 | std::vector& verts = d_mesh.vertex_coord; 143 | std::vector& norms = d_mesh.norm_coord; 144 | std::vector vs(this->node_pos.size()); 145 | for (size_t i=0; ip_mesh->vertex_coord[this->node_index[i]]; 147 | } 148 | for (size_t i=0; i= 2); 156 | weights.resize(n_neigh - 1); // last one for the max dist 157 | double d_max = sqrt(dists_node[0][n_neigh-1]); 158 | n_neigh -= 1; double weight_sum = 0; 159 | for (int j=0; jnode_rots[index_cur_node]*(vert - vs[index_cur_node]) + this->node_trans[index_cur_node] + vs[index_cur_node])); 171 | rot += weights[j]*this->node_rots[index_cur_node].inverse().transpose(); 172 | } 173 | norms[i] = rot * norms[i]; 174 | norms[i].normalize(); 175 | for (int j=0; j& corr_indexs, const std::vector& cons) { 187 | p_graph = &graph; 188 | if (corr_indexs.size() != cons.size()) { 189 | fprintf(stderr, "fatal error : cons_index size should be equal to cons_vert size\n"); 190 | exit(-1); 191 | } 192 | constraints_index = corr_indexs; 193 | node_constraints = cons; 194 | rows = 0; 195 | int unknown_vn = p_graph->node_pos.size(); 196 | rows += 6*unknown_vn; 197 | for (int i=0; inode_neigh[i].size(); 199 | rows += 3*constraints_index.size(); 200 | 201 | cols = 12*unknown_vn; 202 | } 203 | void Deformer::build_values(const Eigen::VectorXd& x, Eigen::VectorXd& fVec) { 204 | int unknown_vn = p_graph->node_pos.size(); 205 | int row = 0; 206 | for (int i=0; inode_neigh[i].size(); ++_j) { 228 | int j = p_graph->node_neigh[i][_j]; 229 | int col_j = 12*j; Eigen::Vector3d t_j(x(col_j+9), x(col_j+10), x(col_j+11)); 230 | Eigen::Vector3d g = p_graph->node_pos[i] - p_graph->node_pos[j]; //g_i - g_j 231 | g = (-R_i*g + g + t_i-t_j); 232 | g = weights[1]*g; 233 | fVec[row+0] = g[0]; fVec[row+1] = g[1]; fVec[row+2] = g[2]; 234 | row += 3; 235 | } 236 | } 237 | 238 | for (size_t _i=0; _inode_pos[i] + t - node_constraints[_i]); 243 | fVec[row + 0] = new_pos[0]; fVec[row + 1] = new_pos[1]; fVec[row + 2] = new_pos[2]; 244 | row += 3; 245 | } 246 | assert (row == rows); 247 | return; 248 | } 249 | 250 | void Deformer::build_jacobi(const Eigen::VectorXd& x, Eigen::SparseMatrix& fJac) { 251 | int unknown_vn = p_graph->node_pos.size(); 252 | fJac.resize(this->rows, this->cols); //re-initialize 253 | fJac.reserve(Eigen::VectorXi::Constant(this->cols, 3*this->p_graph->k_neigh/*3+2*iMaxNeigb*/)); /* */ 254 | int row = 0; 255 | //build the rot part 256 | double weight_rot = weights[0]; 257 | for (int i=0; inode_neigh[i].size(); ++_j) { 296 | int j = p_graph->node_neigh[i][_j]; 297 | int col_i = 12*i, col_j = 12*j; 298 | Eigen::Vector3d g = p_graph->node_pos[j] - p_graph->node_pos[i]; //v_j - v_i 299 | fJac.coeffRef(row, col_i+0) = weight_smooth*g[0]; 300 | fJac.coeffRef(row+1, col_i+3) = weight_smooth*g[0]; 301 | fJac.coeffRef(row+2, col_i+6) = weight_smooth*g[0]; 302 | 303 | fJac.coeffRef(row, col_i+1) = weight_smooth*g[1]; 304 | fJac.coeffRef(row+1, col_i+4) = weight_smooth*g[1]; 305 | fJac.coeffRef(row+2, col_i+7) = weight_smooth*g[1]; 306 | 307 | fJac.coeffRef(row, col_i+2) = weight_smooth*g[2]; 308 | fJac.coeffRef(row+1, col_i+5) = weight_smooth*g[2]; 309 | fJac.coeffRef(row+2, col_i+8) = weight_smooth*g[2]; 310 | 311 | //t_i t_j 312 | fJac.coeffRef(row, col_i+9) = weight_smooth; 313 | fJac.coeffRef(row+1, col_i+10) = weight_smooth; 314 | fJac.coeffRef(row+2, col_i+11) = weight_smooth; 315 | 316 | fJac.coeffRef(row, col_j+9) = -weight_smooth; 317 | fJac.coeffRef(row+1, col_j+10) = -weight_smooth; 318 | fJac.coeffRef(row+2, col_j+11) = -weight_smooth; 319 | row += 3; 320 | } 321 | } 322 | 323 | //build regular part 324 | double weight_regular = weights[2]; //is the con part of orginal paper 325 | for (size_t _i=0; _inode_pos.size(); 339 | fJac.resize(rows, cols); 340 | fJac.setZero(rows, cols); 341 | 342 | int row = 0; 343 | //build the rot part 344 | double weight_rot = weights[0]; 345 | for (int i=0; inode_neigh[i].size(); ++_j) { 384 | int j = p_graph->node_neigh[i][_j]; 385 | int col_i = 12*i, col_j = 12*j; 386 | Eigen::Vector3d g = p_graph->node_pos[j] - p_graph->node_pos[i]; //v_j - v_i 387 | fJac(row, col_i+0) = weight_smooth*g[0]; 388 | fJac(row+1, col_i+3) = weight_smooth*g[0]; 389 | fJac(row+2, col_i+6) = weight_smooth*g[0]; 390 | 391 | fJac(row, col_i+1) = weight_smooth*g[1]; 392 | fJac(row+1, col_i+4) = weight_smooth*g[1]; 393 | fJac(row+2, col_i+7) = weight_smooth*g[1]; 394 | 395 | fJac(row, col_i+2) = weight_smooth*g[2]; 396 | fJac(row+1, col_i+5) = weight_smooth*g[2]; 397 | fJac(row+2, col_i+8) = weight_smooth*g[2]; 398 | 399 | //t_i t_j 400 | fJac(row, col_i+9) = weight_smooth; 401 | fJac(row+1, col_i+10) = weight_smooth; 402 | fJac(row+2, col_i+11) = weight_smooth; 403 | 404 | fJac(row, col_j+9) = -weight_smooth; 405 | fJac(row+1, col_j+10) = -weight_smooth; 406 | fJac(row+2, col_j+11) = -weight_smooth; 407 | row += 3; 408 | } 409 | } 410 | 411 | //build regular part 412 | double weight_regular = weights[2]; //is the con part of orginal paper 413 | for (size_t _i=0; _i 427 | class Timer { 428 | public: 429 | inline void tic() { 430 | _tic = getTime(); 431 | } 432 | inline long toc() { 433 | _toc = getTime(); 434 | return _toc - _tic; 435 | } 436 | private: 437 | inline long getTime() { 438 | SYSTEMTIME t; 439 | GetSystemTime(&t); 440 | return ((t.wHour*60 + t.wMinute)*60 +t.wSecond)*1000 + t.wMilliseconds; 441 | } 442 | private: 443 | long _tic, _toc; 444 | }; 445 | double minimize_guass_newton(Deformer& deformer, Eigen::VectorXd& X) { 446 | Eigen::VectorXd fVec(deformer.rows); fVec.setZero(); 447 | Eigen::SparseMatrix fJac(deformer.rows, deformer.cols); 448 | fJac.reserve(Eigen::VectorXi::Constant(deformer.cols, 3*deformer.p_graph->k_neigh)); /* or reserve bigger space for each column */ 449 | Eigen::SimplicialCholesky> solver; 450 | double eps_break_condition[3] = {1e-6, 1e-2, 1e-3}; 451 | double pre_energy = 0, cur_energy = 0, best_energy = 1e10; 452 | Eigen::VectorXd pre_X, best_X; 453 | Eigen::VectorXd pre_fVec, best_fVec; 454 | 455 | deformer.build_values(X, fVec); 456 | cur_energy = best_energy = sqrt(fVec.dot(fVec))*0.5; 457 | 458 | int iter = 0; 459 | while (iter < 20) { 460 | // printf("\titer = %d\n", iter); 461 | deformer.build_jacobi(X, fJac); 462 | Eigen::SparseMatrix A = fJac.transpose(); 463 | Eigen::VectorXd g = A*fVec; 464 | A = A*fJac; 465 | solver.compute(A); 466 | if (solver.info() != Eigen::Success) { 467 | fprintf(stdout, "unable to defactorize fJac^T*fJac\n"); 468 | break; 469 | } 470 | Eigen::VectorXd h = solver.solve(-g); // ['-' is request] 471 | 472 | X = X + h; 473 | pre_fVec = fVec; 474 | pre_energy = cur_energy; 475 | 476 | deformer.build_values(X, fVec); 477 | cur_energy = sqrt(fVec.dot(fVec))*0.5; 478 | 479 | if (cur_energy < best_energy) { 480 | best_energy = cur_energy; 481 | best_X = X; 482 | } 483 | 484 | // printf("best_energy = %lf\tcur_energy : %lf\n", best_energy, cur_energy); 485 | ++iter; 486 | if (iter >= 2) { 487 | if (fabs(cur_energy - pre_energy) < eps_break_condition[0]*(1+cur_energy)) break; 488 | Eigen::VectorXd gradient = fJac.transpose() * pre_fVec; 489 | double gradient_max = gradient.maxCoeff(); 490 | if (gradient_max < eps_break_condition[1]*(1+cur_energy)) break; 491 | } 492 | double delta_max = h.maxCoeff(); 493 | if (delta_max < eps_break_condition[2]*(1+delta_max)) break; 494 | } 495 | printf("iter = %d\tbest_energy = %lf\n", iter, best_energy); 496 | X = best_X; 497 | return best_energy; 498 | } 499 | static double _getMaxDiagnalCoeff(const Eigen::SparseMatrix& sp_mat) 500 | { 501 | double max_coeff = DBL_MIN; 502 | assert (sp_mat.rows() == sp_mat.cols()); 503 | for (int k=0; k::InnerIterator it(sp_mat, k); it; ++it) { 505 | if (it.row() == it.col() && max_coeff < it.value()) 506 | max_coeff = it.value(); 507 | } 508 | } 509 | return max_coeff; 510 | } 511 | double minimize_levenberg_marquardt(Deformer& deformer, Eigen::VectorXd& X) { 512 | Timer tt; 513 | Eigen::VectorXd fVec(deformer.rows); fVec.setZero(); 514 | Eigen::SparseMatrix fJac(deformer.rows, deformer.cols); 515 | fJac.reserve(Eigen::VectorXi::Constant(deformer.cols, 3*deformer.p_graph->k_neigh/*3+2*iMaxNeigb*/)); /* */ 516 | Eigen::SparseMatrix _Identity(deformer.cols, deformer.cols); 517 | _Identity.reserve(Eigen::VectorXi::Constant(deformer.cols, 1)); /* */ 518 | for (int k=0; k A = fJac.transpose(); 523 | Eigen::VectorXd g = A*fVec; 524 | A = A*fJac; 525 | double best_energy = sqrt(fVec.dot(fVec)); best_energy *= 0.5; 526 | double cur_energy = best_energy; 527 | const double epsilon_1 = 1e-8, epsilon_2 = 1e-6; 528 | const double tau = 1e-3; //if init guess is a good approximation, tau should be small, else tau = 10^-3 or even 1 529 | const int ITER_MAX = 100; 530 | double mu = tau*_getMaxDiagnalCoeff(A); 531 | double nu = 2.0; 532 | int iter = 0; bool found = g.maxCoeff() < epsilon_1; 533 | Eigen::SimplicialCholesky> solver; 534 | tt.tic(); 535 | while (!found && iter++ < ITER_MAX) { 536 | //TODO: after uncomment this line, the min energy is bigger than comment condition, however, this condition can speedup 537 | //each factorization time. 538 | A = A + _Identity*mu; 539 | solver.compute(A); 540 | if (solver.info() != Eigen::Success) 541 | { 542 | printf("!!!Cholesky factorization on Jac failed. solver.info() = %d\n", solver.info()); 543 | break; 544 | } 545 | Eigen::VectorXd h = solver.solve(-g); 546 | if (sqrt(h.dot(h)) <= epsilon_2*(sqrt(X.dot(X))+epsilon_2)) { 547 | found = true; 548 | break; 549 | } 550 | tt.tic(); 551 | Eigen::VectorXd _vX = X + h; //new X 552 | deformer.build_values(_vX, fVec); 553 | cur_energy = sqrt(fVec.dot(fVec)); cur_energy *= 0.5f; //F(x_new) 554 | 555 | // dprintf(stdout, "solver: iter = %d\t", iter); 556 | // dprintf(stdout, "energy_min = %lf\tenergy_cur = %lf\n", best_energy, cur_energy); 557 | 558 | double varrho = (best_energy - cur_energy) / (0.5*(h.dot(mu*h-g))); 559 | // dprintf(stdout, "varrho = %lf\n", varrho); 560 | if (varrho > 0) { //accept 561 | best_energy = cur_energy; 562 | X = _vX; 563 | deformer.build_jacobi(X, fJac); 564 | A = (fJac).transpose(); 565 | g = A*fVec; 566 | A = A*fJac; 567 | 568 | found = g.maxCoeff() < epsilon_1; 569 | mu = mu*std::max(1.0/3, 1-(2*varrho-1)*(2*varrho-1)*(2*varrho-1)); 570 | nu = 2.0; 571 | } else { 572 | mu = mu*nu; nu = 2*nu; 573 | } 574 | } 575 | printf("iter = %d\tbest_energy = %lf\n", iter, best_energy); 576 | return best_energy; 577 | } 578 | 579 | double optimize_once(DGraph& graph, std::vector& constraints_index, 580 | const std::vector& cons) { 581 | Deformer* p_deformer = new Deformer; 582 | p_deformer->init(graph, constraints_index, cons); 583 | double weights[3] = {1.0, 10.0*sqrt(100.0), 10*sqrt(100.0)}; 584 | p_deformer->set_weights(weights, 3); 585 | 586 | Eigen::VectorXd x(p_deformer->cols);x.setZero(); 587 | for (int i=0; i 612 | #include 613 | #include 614 | #include 615 | #pragma warning(pop) 616 | 617 | //convert the trimesh to the point cloud data with positions and normals. 618 | static pcl::PointCloud::Ptr 619 | _trimesh2pcl_data(const TriMesh& trimesh) { 620 | pcl::PointCloud::Ptr out(new pcl::PointCloud); 621 | out->points.reserve(trimesh.vert_num); 622 | int vi = 0; 623 | for (int i=0; ipoints.push_back(temp_v); 632 | ++vi; 633 | } 634 | out->width = vi; out->height = 1; out->is_dense = false; 635 | return out; 636 | } 637 | 638 | void mesh_sampling(const TriMesh& mesh, std::vector& sample_index, double max_dist) { 639 | pcl::PointCloud::Ptr src_pcd = _trimesh2pcl_data(mesh); 640 | std::vector normal_sampling_indices; 641 | std::vector uniform_sampling_indices; 642 | 643 | const int NORMAL_SAMPLE_BIN_NUMBER = 30; 644 | const int NORMAL_SAMPLE_NUMBER = 200; 645 | if (1) 646 | { 647 | pcl::NormalSpaceSampling normal_sampler; 648 | pcl::PointCloud::Ptr tri_normal(new pcl::PointCloud); 649 | { 650 | tri_normal->resize(src_pcd->points.size()); 651 | for (int i=0; ipoints.size(); ++i) { 652 | tri_normal->points[i].normal_x = src_pcd->points[i].normal_x; 653 | tri_normal->points[i].normal_y = src_pcd->points[i].normal_y; 654 | tri_normal->points[i].normal_z = src_pcd->points[i].normal_z; 655 | } 656 | } 657 | normal_sampler.setSeed(time(NULL)); 658 | normal_sampler.setInputCloud(src_pcd); 659 | normal_sampler.setNormals(tri_normal); 660 | normal_sampler.setBins(NORMAL_SAMPLE_BIN_NUMBER, NORMAL_SAMPLE_BIN_NUMBER, NORMAL_SAMPLE_BIN_NUMBER); 661 | normal_sampler.setSample(NORMAL_SAMPLE_NUMBER); 662 | normal_sampler.filter(normal_sampling_indices); 663 | } 664 | { 665 | typedef pcl::PointXYZ PointType; 666 | pcl::UniformSampling uniform_sampler; 667 | pcl::PointCloud::Ptr tri_vertex(new pcl::PointCloud); 668 | { 669 | tri_vertex->resize(src_pcd->points.size()); 670 | for (int i=0; ipoints.size(); ++i) { 671 | tri_vertex->points[i].x = src_pcd->points[i].x; 672 | tri_vertex->points[i].y = src_pcd->points[i].y; 673 | tri_vertex->points[i].z = src_pcd->points[i].z; 674 | } 675 | } 676 | pcl::search::KdTree::Ptr tree(new pcl::search::KdTree()); 677 | uniform_sampler.setInputCloud(tri_vertex); 678 | uniform_sampler.setSearchMethod(tree); 679 | uniform_sampler.setRadiusSearch(max_dist); 680 | { 681 | pcl::PointCloud keypoints_src_idx; 682 | uniform_sampler.compute(keypoints_src_idx); 683 | uniform_sampling_indices.clear(); 684 | uniform_sampling_indices.resize(keypoints_src_idx.size()); 685 | std::copy(keypoints_src_idx.begin(), keypoints_src_idx.end(), uniform_sampling_indices.begin()); 686 | } 687 | } 688 | 689 | //merge the sampling result to output 690 | sample_index.clear(); 691 | sample_index.resize(uniform_sampling_indices.size() + normal_sampling_indices.size()); 692 | std::sort(uniform_sampling_indices.begin(), uniform_sampling_indices.end()); 693 | std::sort(normal_sampling_indices.begin(), normal_sampling_indices.end()); 694 | 695 | std::merge(uniform_sampling_indices.begin(), uniform_sampling_indices.end(), 696 | normal_sampling_indices.begin(), normal_sampling_indices.end(), 697 | sample_index.begin()); 698 | std::vector::iterator last_it = std::unique(sample_index.begin(), sample_index.end()); 699 | sample_index.erase(last_it, sample_index.end()); 700 | printf("****uniform sampling count : %d\n****normal sampling count : %d\n", uniform_sampling_indices.size(), 701 | normal_sampling_indices.size()); 702 | printf("****sampling count : %d\n", sample_index.size()); 703 | } 704 | --------------------------------------------------------------------------------