1 /****************************************************************************
2 * VCGLib o o *
3 * Visual and Computer Graphics Library o o *
4 * _ O _ *
5 * Copyright(C) 2004-2016 \/)\/ *
6 * Visual Computing Lab /\/| *
7 * ISTI - Italian National Research Council | *
8 * \ *
9 * All rights reserved. *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) *
20 * for more details. *
21 * *
22 ****************************************************************************/
23
24 // standard libraries
25 #include <time.h>
26 #include <vcg/math/histogram.h>
27 #include <vcg/complex/complex.h>
28 #include <wrap/io_trimesh/import.h>
29 #include <wrap/io_trimesh/export.h>
30 #include <vcg/simplex/face/component_ep.h>
31 #include <vcg/complex/algorithms/update/component_ep.h>
32 #include <vcg/complex/algorithms/update/bounding.h>
33 #include "sampling.h"
34
35 using namespace std;
36 // project definitions.
37 // error messages
38
39 #define MSG_ERR_MESH_LOAD "error loading the input meshes.\n"
40 #define MSG_ERR_INVALID_OPTION "unable to parse option '%s'\n"
41 #define MSG_ERR_FILE_OPEN "unable to open the output file.'n"
42 #define MSG_ERR_UNKNOWN_FORMAT "unknown file format '%s'.\n"
43
44 // global constants
45 #define NO_SAMPLES_PER_FACE 10
46 #define N_SAMPLES_EDGE_TO_FACE_RATIO 0.1
47 #define BBOX_FACTOR 0.1
48 #define INFLATE_PERCENTAGE 0.02
49 #define MIN_SIZE 125 /* 125 = 5^3 */
50 #define N_HIST_BINS 256
51 #define PRINT_EVERY_N_ELEMENTS 1000
52
53
54 class CFace;
55 class CVertex;
56 struct UsedTypes:public vcg::UsedTypes< vcg::Use<CFace>::AsFaceType, vcg::Use<CVertex>::AsVertexType>{};
57 class CVertex : public vcg::Vertex<UsedTypes,vcg::vertex::Coord3d,vcg::vertex::Qualityf,vcg::vertex::Normal3d,vcg::vertex::Color4b,vcg::vertex::BitFlags> {};
58 class CFace : public vcg::Face< UsedTypes,vcg::face::VertexRef, vcg::face::Normal3d, vcg::face::EdgePlane,vcg::face::Color4b,vcg::face::Mark,vcg::face::BitFlags> {};
59 class CMesh : public vcg::tri::TriMesh< std::vector<CVertex>, std::vector<CFace> > {};
60
61
62 // -----------------------------------------------------------------------------------------------
63
64 using namespace vcg;
65
66
67 ////////////////// Command line Flags and parameters
68 bool NumberOfSamples = false;
69 bool SamplesPerAreaUnit = false;
70 bool CleaningFlag=false;
71 // -----------------------------------------------------------------------------------------------
72
Usage()73 void Usage()
74 {
75 printf("\nUsage: "\
76 "metro file1 file2 [opt]\n"\
77 "Where opt can be:\n"\
78 " -v disable vertex sampling\n"\
79 " -e disable edge sampling\n"\
80 " -f disable face sampling\n"\
81 " -u ignore unreferred vertices\n"\
82 " -sx set the face sampling mode\n"\
83 " where x can be:\n"\
84 " -s0 montecarlo sampling\n"\
85 " -s1 subdivision sampling\n"\
86 " -s2 similar triangles sampling (Default)\n"\
87 " -n# set the required number of samples (overrides -A)\n"\
88 " -a# set the required number of samples per area unit (overrides -N)\n"\
89 " -c save a mesh with error as per-vertex colour and quality\n"\
90 " -C # # Set the min/max values used for color mapping\n"\
91 " -L Remove duplicated and unreferenced vertices before processing\n"\
92 " -h write files with histograms of error distribution\n"\
93 " -G Use a static Uniform Grid as Search Structure (default)\n"\
94 " -O Use an octree as a Search Structure\n"\
95 " -A Use an AxisAligned Bounding Box Tree as Search Structure\n"\
96 " -H Use an Hashed Uniform Grid as Search Structure\n"\
97 "\n"
98 "Default options are to sample vertexes, edge and faces by taking \n"
99 "a number of samples that is approx. 10x the face number.\n"
100 );
101 exit(-1);
102 }
103
104
105 // simple aux function that compute the name for the file containing the stored computations
SaveFileName(const std::string & filename)106 std::string SaveFileName(const std::string &filename)
107 {
108 int pos=filename.find_last_of('.',filename.length());
109 std::string fileout=filename.substr(0,pos)+"_metro.ply";
110 return fileout;
111 }
112
113
114 // Open Mesh
OpenMesh(const char * filename,CMesh & m)115 void OpenMesh(const char *filename, CMesh &m)
116 {
117 int err = tri::io::Importer<CMesh>::Open(m,filename);
118 if(err) {
119 printf("Error in reading %s: '%s'\n",filename,tri::io::Importer<CMesh>::ErrorMsg(err));
120 if(tri::io::Importer<CMesh>::ErrorCritical(err)) exit(-1);
121 }
122 printf("read mesh `%s'\n", filename);
123 if(CleaningFlag){
124 int dup = tri::Clean<CMesh>::RemoveDuplicateVertex(m);
125 int unref = tri::Clean<CMesh>::RemoveUnreferencedVertex(m);
126 printf("Removed %i duplicate and %i unreferenced vertices from mesh %s\n",dup,unref,filename);
127 }
128 }
129
130
main(int argc,char ** argv)131 int main(int argc, char**argv)
132 {
133 CMesh S1, S2;
134 float ColorMin=0, ColorMax=0;
135 double dist1_max, dist2_max;
136 unsigned long n_samples_target, elapsed_time;
137 double n_samples_per_area_unit;
138 int flags;
139
140 // print program info
141 printf("-------------------------------\n"
142 " Metro V.4.07 \n"
143 " http://vcg.isti.cnr.it\n"
144 " release date: " __DATE__ "\n"
145 "-------------------------------\n\n");
146
147 if(argc <= 2) Usage();
148 // default parameters
149 flags = SamplingFlags::VERTEX_SAMPLING |
150 SamplingFlags::EDGE_SAMPLING |
151 SamplingFlags::FACE_SAMPLING |
152 SamplingFlags::SIMILAR_SAMPLING;
153
154 // parse command line.
155 for(int i=3; i < argc;)
156 {
157 if(argv[i][0]=='-')
158 switch(argv[i][1])
159 {
160 case 'h' : flags |= SamplingFlags::HIST; break;
161 case 'v' : flags &= ~SamplingFlags::VERTEX_SAMPLING; break;
162 case 'e' : flags &= ~SamplingFlags::EDGE_SAMPLING; break;
163 case 'f' : flags &= ~SamplingFlags::FACE_SAMPLING; break;
164 case 'u' : flags |= SamplingFlags::INCLUDE_UNREFERENCED_VERTICES; break;
165 case 's' :
166 switch(argv[i][2])
167 {
168 case '0': flags = (flags | SamplingFlags::MONTECARLO_SAMPLING ) & (~ SamplingFlags::NO_SAMPLING );break;
169 case '1': flags = (flags | SamplingFlags::SUBDIVISION_SAMPLING ) & (~ SamplingFlags::NO_SAMPLING );break;
170 case '2': flags = (flags | SamplingFlags::SIMILAR_SAMPLING ) & (~ SamplingFlags::NO_SAMPLING );break;
171 default : printf(MSG_ERR_INVALID_OPTION, argv[i]);
172 exit(0);
173 }
174 break;
175 case 'n': NumberOfSamples = true; n_samples_target = (unsigned long) atoi(&(argv[i][2])); break;
176 case 'a': SamplesPerAreaUnit = true; n_samples_per_area_unit = (unsigned long) atoi(&(argv[i][2])); break;
177 case 'c': flags |= SamplingFlags::SAVE_ERROR; break;
178 case 'L': CleaningFlag=true; break;
179 case 'C': ColorMin=float(atof(argv[i+1])); ColorMax=float(atof(argv[i+2])); i+=2; break;
180 case 'A': flags |= SamplingFlags::USE_AABB_TREE; printf("Using AABB Tree as search structure\n"); break;
181 case 'G': flags |= SamplingFlags::USE_STATIC_GRID; printf("Using static uniform grid as search structure\n"); break;
182 case 'H': flags |= SamplingFlags::USE_HASH_GRID; printf("Using hashed uniform grid as search structure\n"); break;
183 case 'O': flags |= SamplingFlags::USE_OCTREE; printf("Using octree as search structure\n"); break;
184 default : printf(MSG_ERR_INVALID_OPTION, argv[i]);
185 exit(0);
186 }
187 i++;
188 }
189
190 if(!(flags & SamplingFlags::USE_HASH_GRID) && !(flags & SamplingFlags::USE_AABB_TREE) && !(flags & SamplingFlags::USE_OCTREE))
191 flags |= SamplingFlags::USE_STATIC_GRID;
192
193 // load input meshes.
194 OpenMesh(argv[1],S1);
195 OpenMesh(argv[2],S2);
196
197 string S1NewName=SaveFileName(argv[1]);
198 string S2NewName=SaveFileName(argv[2]);
199
200 if(!NumberOfSamples && !SamplesPerAreaUnit)
201 {
202 NumberOfSamples = true;
203 n_samples_target = 10 * max(S1.fn,S2.fn);// take 10 samples per face
204 }
205
206 // compute face information
207 tri::UpdateComponentEP<CMesh>::Set(S1);
208 tri::UpdateComponentEP<CMesh>::Set(S2);
209
210 // set bounding boxes for S1 and S2
211 tri::UpdateBounding<CMesh>::Box(S1);
212 tri::UpdateBounding<CMesh>::Box(S2);
213
214 // set Bounding Box.
215 Box3<CMesh::ScalarType> bbox, tmp_bbox_M1=S1.bbox, tmp_bbox_M2=S2.bbox;
216 bbox.Add(S1.bbox);
217 bbox.Add(S2.bbox);
218 bbox.Offset(bbox.Diag()*0.02);
219 S1.bbox = bbox;
220 S2.bbox = bbox;
221
222 // initialize time info.
223 int t0=clock();
224
225 Sampling<CMesh> ForwardSampling(S1,S2);
226 Sampling<CMesh> BackwardSampling(S2,S1);
227
228 // print mesh info.
229 printf("Mesh info:\n");
230 printf(" M1: '%s'\n\tvertices %7i\n\tfaces %7i\n\tarea %12.4f\n", argv[1], S1.vn, S1.fn, ForwardSampling.GetArea());
231 printf("\tbbox (%7.4f %7.4f %7.4f)-(%7.4f %7.4f %7.4f)\n", tmp_bbox_M1.min[0], tmp_bbox_M1.min[1], tmp_bbox_M1.min[2], tmp_bbox_M1.max[0], tmp_bbox_M1.max[1], tmp_bbox_M1.max[2]);
232 printf("\tbbox diagonal %f\n", (float)tmp_bbox_M1.Diag());
233 printf(" M2: '%s'\n\tvertices %7i\n\tfaces %7i\n\tarea %12.4f\n", argv[2], S2.vn, S2.fn, BackwardSampling.GetArea());
234 printf("\tbbox (%7.4f %7.4f %7.4f)-(%7.4f %7.4f %7.4f)\n", tmp_bbox_M2.min[0], tmp_bbox_M2.min[1], tmp_bbox_M2.min[2], tmp_bbox_M2.max[0], tmp_bbox_M2.max[1], tmp_bbox_M2.max[2]);
235 printf("\tbbox diagonal %f\n", (float)tmp_bbox_M2.Diag());
236
237 // Forward distance.
238 printf("\nForward distance (M1 -> M2):\n");
239 ForwardSampling.SetFlags(flags);
240 if(NumberOfSamples)
241 {
242 ForwardSampling.SetSamplesTarget(n_samples_target);
243 n_samples_per_area_unit = ForwardSampling.GetNSamplesPerAreaUnit();
244 }
245 else
246 {
247 ForwardSampling.SetSamplesPerAreaUnit(n_samples_per_area_unit);
248 n_samples_target = ForwardSampling.GetNSamplesTarget();
249 }
250 printf("target # samples : %lu\ntarget # samples/area : %f\n", n_samples_target, n_samples_per_area_unit);
251 ForwardSampling.Hausdorff();
252 dist1_max = ForwardSampling.GetDistMax();
253 printf("\ndistances:\n max : %f (%f wrt bounding box diagonal)\n", (float)dist1_max, (float)dist1_max/bbox.Diag());
254 printf(" mean : %f\n", ForwardSampling.GetDistMean());
255 printf(" RMS : %f\n", ForwardSampling.GetDistRMS());
256 printf("# vertex samples %9lu\n", ForwardSampling.GetNVertexSamples());
257 printf("# edge samples %9lu\n", ForwardSampling.GetNEdgeSamples());
258 printf("# area samples %9lu\n", ForwardSampling.GetNAreaSamples());
259 printf("# total samples %9lu\n", ForwardSampling.GetNSamples());
260 printf("# samples per area unit: %f\n\n", ForwardSampling.GetNSamplesPerAreaUnit());
261
262 // Backward distance.
263 printf("\nBackward distance (M2 -> M1):\n");
264 BackwardSampling.SetFlags(flags);
265 if(NumberOfSamples)
266 {
267 BackwardSampling.SetSamplesTarget(n_samples_target);
268 n_samples_per_area_unit = BackwardSampling.GetNSamplesPerAreaUnit();
269 }
270 else
271 {
272 BackwardSampling.SetSamplesPerAreaUnit(n_samples_per_area_unit);
273 n_samples_target = BackwardSampling.GetNSamplesTarget();
274 }
275 printf("target # samples : %lu\ntarget # samples/area : %f\n", n_samples_target, n_samples_per_area_unit);
276 BackwardSampling.Hausdorff();
277 dist2_max = BackwardSampling.GetDistMax();
278 printf("\ndistances:\n max : %f (%f wrt bounding box diagonal)\n", (float)dist2_max, (float)dist2_max/bbox.Diag());
279 printf(" mean : %f\n", BackwardSampling.GetDistMean());
280 printf(" RMS : %f\n", BackwardSampling.GetDistRMS());
281 printf("# vertex samples %9lu\n", BackwardSampling.GetNVertexSamples());
282 printf("# edge samples %9lu\n", BackwardSampling.GetNEdgeSamples());
283 printf("# area samples %9lu\n", BackwardSampling.GetNAreaSamples());
284 printf("# total samples %9lu\n", BackwardSampling.GetNSamples());
285 printf("# samples per area unit: %f\n\n", BackwardSampling.GetNSamplesPerAreaUnit());
286
287 // compute time info.
288 elapsed_time = clock() - t0;
289 int n_total_sample=ForwardSampling.GetNSamples()+BackwardSampling.GetNSamples();
290 double mesh_dist_max = max(dist1_max , dist2_max);
291
292 printf("\nHausdorff distance: %f (%f wrt bounding box diagonal)\n",(float)mesh_dist_max,(float)mesh_dist_max/bbox.Diag());
293 printf(" Computation time : %d ms\n",(int)(1000.0*elapsed_time/CLOCKS_PER_SEC));
294 printf(" # samples/second : %f\n\n", (float)n_total_sample/((float)elapsed_time/CLOCKS_PER_SEC));
295
296 // save error files.
297 if(flags & SamplingFlags::SAVE_ERROR)
298 {
299 int saveMask = vcg::tri::io::Mask::IOM_VERTCOLOR | vcg::tri::io::Mask::IOM_VERTQUALITY /* | vcg::ply::PLYMask::PM_VERTQUALITY*/ ;
300 //p.mask|=vcg::ply::PLYMask::PM_VERTCOLOR|vcg::ply::PLYMask::PM_VERTQUALITY;
301 if(ColorMax!=0 || ColorMin != 0){
302 vcg::tri::UpdateColor<CMesh>::PerVertexQualityRamp(S1,ColorMin,ColorMax);
303 vcg::tri::UpdateColor<CMesh>::PerVertexQualityRamp(S2,ColorMin,ColorMax);
304 }
305 tri::io::ExporterPLY<CMesh>::Save( S1,S1NewName.c_str(),saveMask);
306 tri::io::ExporterPLY<CMesh>::Save( S2,S2NewName.c_str(),saveMask);
307 }
308
309 // save error files.
310 if(flags & SamplingFlags::HIST)
311 {
312 ForwardSampling.GetHist().FileWrite("forward_result.csv");
313 BackwardSampling.GetHist().FileWrite("backward_result.csv");
314 }
315 return 0;
316 }
317
318 // -----------------------------------------------------------------------------------------------
319