ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
SurfaceTrimmer.cpp
Go to the documentation of this file.
1 /*
2 Copyright (c) 2013, Michael Kazhdan
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7 
8 Redistributions of source code must retain the above copyright notice, this list of
9 conditions and the following disclaimer. Redistributions in binary form must reproduce
10 the above copyright notice, this list of conditions and the following disclaimer
11 in the documentation and/or other materials provided with the distribution.
12 
13 Neither the name of the Johns Hopkins University nor the names of its contributors
14 may be used to endorse or promote products derived from this software without specific
15 prior written permission.
16 
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
18 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
20 SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
22 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
26 DAMAGE.
27 */
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <float.h>
32 #ifdef _OPENMP
33 #include <omp.h>
34 #endif // _OPENMP
35 #include <algorithm>
36 #include "CmdLineParser.h"
37 #include "Geometry.h"
38 #include "Ply.h"
39 #include "MAT.h"
40 #include "MyTime.h"
41 
42 cmdLineString In( "in" ) , Out( "out" );
43 cmdLineInt Smooth( "smooth" , 5 );
44 cmdLineFloat Trim( "trim" ) , IslandAreaRatio( "aRatio" , 0.001f );
45 cmdLineReadable PolygonMesh( "polygonMesh" );
46 
48 {
50 };
51 
52 void ShowUsage( char* ex )
53 {
54  printf( "Usage: %s\n" , ex );
55  printf( "\t --%s <input polygon mesh>\n" , In.name );
56  printf( "\t[--%s <ouput polygon mesh>]\n" , Out.name );
57  printf( "\t[--%s <smoothing iterations>=%d]\n" , Smooth.name , Smooth.value );
58  printf( "\t[--%s <trimming value>]\n" , Trim.name );
59  printf( "\t[--%s <relative area of islands>=%f]\n" , IslandAreaRatio.name , IslandAreaRatio.value );
60  printf( "\t[--%s]\n" , PolygonMesh.name );
61 }
62 
63 long long EdgeKey( int key1 , int key2 )
64 {
65  if( key1<key2 ) return ( ( (long long)key1 )<<32 ) | ( (long long)key2 );
66  else return ( ( (long long)key2 )<<32 ) | ( (long long)key1 );
67 }
68 
69 template< class Real , class Vertex >
70 Vertex InterpolateVertices( const Vertex& v1 , const Vertex& v2 , Real value )
71 {
72  typename Vertex::Wrapper _v1(v1) , _v2(v2);
73  if( _v1.value==_v2.value ) return Vertex( (_v1+_v2)/Real(2.) );
74 
75  Real dx = ( _v1.value-value ) / ( _v1.value-_v2.value );
76  return Vertex( _v1*(1.f-dx) + _v2*dx );
77 }
78 template< class Real , class Vertex >
79 void SmoothValues( std::vector< Vertex >& vertices , const std::vector< std::vector< int > >& polygons )
80 {
81  std::vector< int > count( vertices.size() );
82  std::vector< Real > sums( vertices.size() , 0 );
83  for( size_t i=0 ; i<polygons.size() ; i++ )
84  {
85  int sz = int(polygons[i].size());
86  for( int j=0 ; j<sz ; j++ )
87  {
88  int j1 = j , j2 = (j+1)%sz;
89  int v1 = polygons[i][j1] , v2 = polygons[i][j2];
90  count[v1]++ , count[v2]++;
91  sums[v1] += vertices[v2].value , sums[v2] += vertices[v1].value;
92  }
93  }
94  for( size_t i=0 ; i<vertices.size() ; i++ ) vertices[i].value = ( sums[i] + vertices[i].value ) / ( count[i] + 1 );
95 }
96 template< class Real , class Vertex >
98  (
99  const std::vector< int >& polygon ,
100  std::vector< Vertex >& vertices ,
101  std::vector< std::vector< int > >* ltPolygons , std::vector< std::vector< int > >* gtPolygons ,
102  std::vector< bool >* ltFlags , std::vector< bool >* gtFlags ,
103  hash_map< long long , int >& vertexTable ,
104  Real trimValue
105  )
106 {
107  int sz = int( polygon.size() );
108  std::vector< bool > gt( sz );
109  int gtCount = 0;
110  for( int j=0 ; j<sz ; j++ )
111  {
112  gt[j] = ( vertices[ polygon[j] ].value>trimValue );
113  if( gt[j] ) gtCount++;
114  }
115  if ( gtCount==sz ){ if( gtPolygons ) gtPolygons->push_back( polygon ) ; if( gtFlags ) gtFlags->push_back( false ); }
116  else if( gtCount==0 ){ if( ltPolygons ) ltPolygons->push_back( polygon ) ; if( ltFlags ) ltFlags->push_back( false ); }
117  else
118  {
119  int start;
120  for( start=0 ; start<sz ; start++ ) if( gt[start] && !gt[(start+sz-1)%sz] ) break;
121 
122  bool gtFlag = true;
123  std::vector< int > poly;
124 
125  // Add the initial vertex
126  {
127  int j1 = (start+int(sz)-1)%sz , j2 = start;
128  int v1 = polygon[j1] , v2 = polygon[j2];
129  int vIdx;
130  hash_map< long long , int >::iterator iter = vertexTable.find( EdgeKey( v1 , v2 ) );
131  if( iter==vertexTable.end() )
132  {
133  vertexTable[ EdgeKey( v1 , v2 ) ] = vIdx = int( vertices.size() );
134  vertices.push_back( InterpolateVertices( vertices[v1] , vertices[v2] , trimValue ) );
135  }
136  else vIdx = iter->second;
137  poly.push_back( vIdx );
138  }
139 
140  for( int _j=0 ; _j<=sz ; _j++ )
141  {
142  int j1 = (_j+start+sz-1)%sz , j2 = (_j+start)%sz;
143  int v1 = polygon[j1] , v2 = polygon[j2];
144  if( gt[j2]==gtFlag ) poly.push_back( v2 );
145  else
146  {
147  int vIdx;
148  hash_map< long long , int >::iterator iter = vertexTable.find( EdgeKey( v1 , v2 ) );
149  if( iter==vertexTable.end() )
150  {
151  vertexTable[ EdgeKey( v1 , v2 ) ] = vIdx = int( vertices.size() );
152  vertices.push_back( InterpolateVertices( vertices[v1] , vertices[v2] , trimValue ) );
153  }
154  else vIdx = iter->second;
155  poly.push_back( vIdx );
156  if( gtFlag ){ if( gtPolygons ) gtPolygons->push_back( poly ) ; if( ltFlags ) ltFlags->push_back( true ); }
157  else { if( ltPolygons ) ltPolygons->push_back( poly ) ; if( gtFlags ) gtFlags->push_back( true ); }
158  poly.clear() , poly.push_back( vIdx ) , poly.push_back( v2 );
159  gtFlag = !gtFlag;
160  }
161  }
162  }
163 }
164 template< class Real , class Vertex >
165 void Triangulate( const std::vector< Vertex >& vertices , const std::vector< std::vector< int > >& polygons , std::vector< std::vector< int > >& triangles )
166 {
167  triangles.clear();
168  for( size_t i=0 ; i<polygons.size() ; i++ )
169  if( polygons.size()>3 )
170  {
172  std::vector< Point3D< Real > > _vertices( polygons[i].size() );
173  std::vector< TriangleIndex > _triangles;
174  for( int j=0 ; j<int( polygons[i].size() ) ; j++ ) _vertices[j] = vertices[ polygons[i][j] ].point;
175  mat.GetTriangulation( _vertices , _triangles );
176 
177  // Add the triangles to the mesh
178  size_t idx = triangles.size();
179  triangles.resize( idx+_triangles.size() );
180  for( int j=0 ; j<int(_triangles.size()) ; j++ )
181  {
182  triangles[idx+j].resize(3);
183  for( int k=0 ; k<3 ; k++ ) triangles[idx+j][k] = polygons[i][ _triangles[j].idx[k] ];
184  }
185  }
186  else if( polygons[i].size()==3 ) triangles.push_back( polygons[i] );
187 }
188 template< class Real , class Vertex >
189 double PolygonArea( const std::vector< Vertex >& vertices , const std::vector< int >& polygon )
190 {
191  if( polygon.size()<3 ) return 0.;
192  else if( polygon.size()==3 ) return TriangleArea( vertices[polygon[0]].point , vertices[polygon[1]].point , vertices[polygon[2]].point );
193  else
194  {
195  Point3D< Real > center;
196  for( size_t i=0 ; i<polygon.size() ; i++ ) center += vertices[ polygon[i] ].point;
197  center /= Real( polygon.size() );
198  double area = 0;
199  for( size_t i=0 ; i<polygon.size() ; i++ ) area += TriangleArea( center , vertices[ polygon[i] ].point , vertices[ polygon[ (i+1)%polygon.size() ] ].point );
200  return area;
201  }
202 }
203 
204 template< class Vertex >
205 void RemoveHangingVertices( std::vector< Vertex >& vertices , std::vector< std::vector< int > >& polygons )
206 {
207  hash_map< int , int > vMap;
208  std::vector< bool > vertexFlags( vertices.size() , false );
209  for( size_t i=0 ; i<polygons.size() ; i++ ) for( size_t j=0 ; j<polygons[i].size() ; j++ ) vertexFlags[ polygons[i][j] ] = true;
210  int vCount = 0;
211  for( int i=0 ; i<int(vertices.size()) ; i++ ) if( vertexFlags[i] ) vMap[i] = vCount++;
212  for( size_t i=0 ; i<polygons.size() ; i++ ) for( size_t j=0 ; j<polygons[i].size() ; j++ ) polygons[i][j] = vMap[ polygons[i][j] ];
213 
214  std::vector< Vertex > _vertices( vCount );
215  for( int i=0 ; i<int(vertices.size()) ; i++ ) if( vertexFlags[i] ) _vertices[ vMap[i] ] = vertices[i];
216  vertices = _vertices;
217 }
218 void SetConnectedComponents( const std::vector< std::vector< int > >& polygons , std::vector< std::vector< int > >& components )
219 {
220  std::vector< int > polygonRoots( polygons.size() );
221  for( size_t i=0 ; i<polygons.size() ; i++ ) polygonRoots[i] = int(i);
222  hash_map< long long , int > edgeTable;
223  for( size_t i=0 ; i<polygons.size() ; i++ )
224  {
225  int sz = int( polygons[i].size() );
226  for( int j=0 ; j<sz ; j++ )
227  {
228  int j1 = j , j2 = (j+1)%sz;
229  int v1 = polygons[i][j1] , v2 = polygons[i][j2];
230  long long eKey = EdgeKey( v1 , v2 );
231  hash_map< long long , int >::iterator iter = edgeTable.find( eKey );
232  if( iter==edgeTable.end() ) edgeTable[ eKey ] = int(i);
233  else
234  {
235  int p = iter->second;
236  while( polygonRoots[p]!=p )
237  {
238  int temp = polygonRoots[p];
239  polygonRoots[p] = int(i);
240  p = temp;
241  }
242  polygonRoots[p] = int(i);
243  }
244  }
245  }
246  for( size_t i=0 ; i<polygonRoots.size() ; i++ )
247  {
248  int p = int(i);
249  while( polygonRoots[p]!=p ) p = polygonRoots[p];
250  int root = p;
251  p = int(i);
252  while( polygonRoots[p]!=p )
253  {
254  int temp = polygonRoots[p];
255  polygonRoots[p] = root;
256  p = temp;
257  }
258  }
259  int cCount = 0;
260  hash_map< int , int > vMap;
261  for( int i= 0 ; i<int(polygonRoots.size()) ; i++ ) if( polygonRoots[i]==i ) vMap[i] = cCount++;
262  components.resize( cCount );
263  for( int i=0 ; i<int(polygonRoots.size()) ; i++ ) components[ vMap[ polygonRoots[i] ] ].push_back(i);
264 }
265 template< class Real >
266 inline Point3D< Real > CrossProduct( Point3D< Real > p1 , Point3D< Real > p2 ){ return Point3D< Real >( p1[1]*p2[2]-p1[2]*p2[1] , p1[2]*p2[0]-p1[0]*p2[2] , p1[0]*p1[1]-p1[1]*p2[0] ); }
267 template< class Real >
269 {
270  Point3D< Real > n = CrossProduct( v2-v1 , v3-v1 );
271  return sqrt( n[0]*n[0] + n[1]*n[1] + n[2]*n[2] ) / 2.;
272 }
273 template< class Vertex >
274 int Execute( void )
275 {
276  float min , max;
277  int paramNum = sizeof(params)/sizeof(cmdLineReadable*);
278  std::vector< Vertex > vertices;
279  std::vector< std::vector< int > > polygons;
280 
281  int ft , commentNum = paramNum+2;
282  char** comments;
283  PlyReadPolygons( In.value , vertices , polygons , Vertex::ReadProperties , Vertex::ReadComponents , ft , &comments , &commentNum );
284  for( int i=0 ; i<Smooth.value ; i++ ) SmoothValues< float , Vertex >( vertices , polygons );
285  min = max = vertices[0].value;
286  for( size_t i=0 ; i<vertices.size() ; i++ ) min = std::min< float >( min , vertices[i].value ) , max = std::max< float >( max , vertices[i].value );
287  printf( "Value Range: [%f,%f]\n" , min , max );
288 
289 
290  hash_map< long long , int > vertexTable;
291  std::vector< std::vector< int > > ltPolygons , gtPolygons;
292  std::vector< bool > ltFlags , gtFlags;
293 
294  for( int i=0 ; i<paramNum+2 ; i++ ) comments[i+commentNum]=new char[1024];
295  sprintf( comments[commentNum++] , "Running Surface Trimmer (V5)" );
296  if( In.set ) sprintf(comments[commentNum++],"\t--%s %s" , In.name , In.value );
297  if( Out.set ) sprintf(comments[commentNum++],"\t--%s %s" , Out.name , Out.value );
298  if( Trim.set ) sprintf(comments[commentNum++],"\t--%s %f" , Trim.name , Trim.value );
299  if( Smooth.set ) sprintf(comments[commentNum++],"\t--%s %d" , Smooth.name , Smooth.value );
300  if( IslandAreaRatio.set ) sprintf(comments[commentNum++],"\t--%s %f" , IslandAreaRatio.name , IslandAreaRatio.value );
301  if( PolygonMesh.set ) sprintf(comments[commentNum++],"\t--%s" , PolygonMesh.name );
302 
303  double t=Time();
304  for( size_t i=0 ; i<polygons.size() ; i++ ) SplitPolygon( polygons[i] , vertices , &ltPolygons , &gtPolygons , &ltFlags , &gtFlags , vertexTable , Trim.value );
305  if( IslandAreaRatio.value>0 )
306  {
307  std::vector< std::vector< int > > _ltPolygons , _gtPolygons;
308  std::vector< std::vector< int > > ltComponents , gtComponents;
309  SetConnectedComponents( ltPolygons , ltComponents );
310  SetConnectedComponents( gtPolygons , gtComponents );
311  std::vector< double > ltAreas( ltComponents.size() , 0. ) , gtAreas( gtComponents.size() , 0. );
312  std::vector< bool > ltComponentFlags( ltComponents.size() , false ) , gtComponentFlags( gtComponents.size() , false );
313  double area = 0.;
314  for( size_t i=0 ; i<ltComponents.size() ; i++ )
315  {
316  for( size_t j=0 ; j<ltComponents[i].size() ; j++ )
317  {
318  ltAreas[i] += PolygonArea< float , Vertex >( vertices , ltPolygons[ ltComponents[i][j] ] );
319  ltComponentFlags[i] = ( ltComponentFlags[i] || ltFlags[ ltComponents[i][j] ] );
320  }
321  area += ltAreas[i];
322  }
323  for( size_t i=0 ; i<gtComponents.size() ; i++ )
324  {
325  for( size_t j=0 ; j<gtComponents[i].size() ; j++ )
326  {
327  gtAreas[i] += PolygonArea< float , Vertex >( vertices , gtPolygons[ gtComponents[i][j] ] );
328  gtComponentFlags[i] = ( gtComponentFlags[i] || gtFlags[ gtComponents[i][j] ] );
329  }
330  area += gtAreas[i];
331  }
332  for( size_t i=0 ; i<ltComponents.size() ; i++ )
333  {
334  if( ltAreas[i]<area*IslandAreaRatio.value && ltComponentFlags[i] ) for( size_t j=0 ; j<ltComponents[i].size() ; j++ ) _gtPolygons.push_back( ltPolygons[ ltComponents[i][j] ] );
335  else for( size_t j=0 ; j<ltComponents[i].size() ; j++ ) _ltPolygons.push_back( ltPolygons[ ltComponents[i][j] ] );
336  }
337  for( size_t i=0 ; i<gtComponents.size() ; i++ )
338  {
339  if( gtAreas[i]<area*IslandAreaRatio.value && gtComponentFlags[i] ) for( size_t j=0 ; j<gtComponents[i].size() ; j++ ) _ltPolygons.push_back( gtPolygons[ gtComponents[i][j] ] );
340  else for( size_t j=0 ; j<gtComponents[i].size() ; j++ ) _gtPolygons.push_back( gtPolygons[ gtComponents[i][j] ] );
341  }
342  ltPolygons = _ltPolygons , gtPolygons = _gtPolygons;
343  }
344  if( !PolygonMesh.set )
345  {
346  {
347  std::vector< std::vector< int > > polys = ltPolygons;
348  Triangulate< float , Vertex >( vertices , ltPolygons , polys ) , ltPolygons = polys;
349  }
350  {
351  std::vector< std::vector< int > > polys = gtPolygons;
352  Triangulate< float , Vertex >( vertices , gtPolygons , polys ) , gtPolygons = polys;
353  }
354  }
355 
356  RemoveHangingVertices( vertices , gtPolygons );
357  sprintf( comments[commentNum++] , "#Trimmed In: %9.1f (s)" , Time()-t );
358  if( Out.set ) PlyWritePolygons( Out.value , vertices , gtPolygons , Vertex::WriteProperties , Vertex::WriteComponents , ft , comments , commentNum );
359 
360  return EXIT_SUCCESS;
361 }
362 int SurfaceTrimmer( int argc , char* argv[] )
363 {
364  int paramNum = sizeof(params)/sizeof(cmdLineReadable*);
365  cmdLineParse( argc-1 , &argv[1] , paramNum , params , 0 );
366 
367  if( !In.set || !Trim.set )
368  {
369  ShowUsage( argv[0] );
370  return EXIT_FAILURE;
371  }
373  if( !PlyReadHeader( In.value , PlyColorAndValueVertex< float >::ReadProperties , PlyColorAndValueVertex< float >::ReadComponents , readFlags ) ) fprintf( stderr , "[ERROR] Failed to read ply header: %s\n" , In.value ) , exit( 0 );
374 
375  bool hasValue = readFlags[3];
376  bool hasColor = ( readFlags[4] || readFlags[7] ) && ( readFlags[5] || readFlags[8] ) && ( readFlags[6] || readFlags[9] );
377 
378  if( !hasValue ) fprintf( stderr , "[ERROR] Ply file does not contain values\n" ) , exit( 0 );
379  if( hasColor ) return Execute< PlyColorAndValueVertex< float > >();
380  else return Execute< PlyValueVertex< float > >();
381 }
void cmdLineParse(int argc, char **argv, int num, cmdLineReadable **readable, int dumpError)
int size
int count
double Time(void)
Definition: MyTime.h:38
int PlyReadPolygons(char *fileName, std::vector< Vertex > &vertices, std::vector< std::vector< int > > &polygons, PlyProperty *properties, int propertyNum, int &file_type, char ***comments=NULL, int *commentNum=NULL, bool *readFlags=NULL)
Definition: Ply.h:679
int PlyWritePolygons(char *fileName, CoredMeshData< Vertex > *mesh, int file_type, const Point3D< float > &translate, float scale, char **comments=NULL, int commentNum=0, XForm4x4< Real > xForm=XForm4x4< Real >::Identity())
Definition: Ply.h:790
bool PlyReadHeader(char *fileName, PlyProperty *properties, int propertyNum, bool *readFlags, int &file_type)
Definition: Ply.h:515
void SmoothValues(std::vector< Vertex > &vertices, const std::vector< std::vector< int > > &polygons)
cmdLineReadable * params[]
void SplitPolygon(const std::vector< int > &polygon, std::vector< Vertex > &vertices, std::vector< std::vector< int > > *ltPolygons, std::vector< std::vector< int > > *gtPolygons, std::vector< bool > *ltFlags, std::vector< bool > *gtFlags, std::unordered_map< long long, int > &vertexTable, Real trimValue)
cmdLineFloat IslandAreaRatio("aRatio", 0.001f)
Point3D< Real > CrossProduct(Point3D< Real > p1, Point3D< Real > p2)
void ShowUsage(char *ex)
cmdLineString Out("out")
void SetConnectedComponents(const std::vector< std::vector< int > > &polygons, std::vector< std::vector< int > > &components)
double PolygonArea(const std::vector< Vertex > &vertices, const std::vector< int > &polygon)
cmdLineString In("in")
void Triangulate(const std::vector< Vertex > &vertices, const std::vector< std::vector< int > > &polygons, std::vector< std::vector< int > > &triangles)
cmdLineFloat Trim("trim")
cmdLineInt Smooth("smooth", 5)
double TriangleArea(Point3D< Real > v1, Point3D< Real > v2, Point3D< Real > v3)
int SurfaceTrimmer(int argc, char *argv[])
void RemoveHangingVertices(std::vector< Vertex > &vertices, std::vector< std::vector< int > > &polygons)
int Execute(void)
Vertex InterpolateVertices(const Vertex &v1, const Vertex &v2, Real value)
cmdLineReadable PolygonMesh("polygonMesh")
long long EdgeKey(int key1, int key2)
boost::geometry::model::polygon< point_xy > polygon
Definition: TreeIso.cpp:37
int min(int a, int b)
Definition: cutil_math.h:53
int max(int a, int b)
Definition: cutil_math.h:48
Definition: lsd.c:149