├── 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 |
--------------------------------------------------------------------------------