mirror of
https://github.com/CloudCompare/PoissonRecon.git
synced 2026-08-30 00:50:28 +08:00
781 lines
30 KiB
C++
781 lines
30 KiB
C++
/*
|
|
Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
Redistributions of source code must retain the above copyright notice, this list of
|
|
conditions and the following disclaimer. Redistributions in binary form must reproduce
|
|
the above copyright notice, this list of conditions and the following disclaimer
|
|
in the documentation and/or other materials provided with the distribution.
|
|
|
|
Neither the name of the Johns Hopkins University nor the names of its contributors
|
|
may be used to endorse or promote products derived from this software without specific
|
|
prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
DAMAGE.
|
|
*/
|
|
|
|
#undef FAST_COMPILE
|
|
#undef ARRAY_DEBUG
|
|
#define BRUNO_LEVY_FIX
|
|
#define FOR_RELEASE
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#if defined( _WIN32 ) || defined( _WIN64 )
|
|
#include <Windows.h>
|
|
#include <Psapi.h>
|
|
#endif // _WIN32 || _WIN64
|
|
#include "MyTime.h"
|
|
#include "MarchingCubes.h"
|
|
#include "Octree.h"
|
|
#include "SparseMatrix.h"
|
|
#include "CmdLineParser.h"
|
|
#include "PPolynomial.h"
|
|
#include "Ply.h"
|
|
#include "MemoryUsage.h"
|
|
#ifdef _OPENMP
|
|
#include "omp.h"
|
|
#endif // _OPENMP
|
|
void DumpOutput( const char* format , ... );
|
|
void DumpOutput2( std::vector< char* >& comments , const char* format , ... );
|
|
#include "MultiGridOctreeData.h"
|
|
|
|
#define DEFAULT_FULL_DEPTH 5
|
|
|
|
#define XSTR(x) STR(x)
|
|
#define STR(x) #x
|
|
#if DEFAULT_FULL_DEPTH
|
|
#pragma message ( "[WARNING] Setting default full depth to " XSTR(DEFAULT_FULL_DEPTH) )
|
|
#endif // DEFAULT_FULL_DEPTH
|
|
|
|
#include <stdarg.h>
|
|
char* outputFile=NULL;
|
|
int echoStdout=0;
|
|
void DumpOutput( const char* format , ... )
|
|
{
|
|
if( outputFile )
|
|
{
|
|
FILE* fp = fopen( outputFile , "a" );
|
|
va_list args;
|
|
va_start( args , format );
|
|
vfprintf( fp , format , args );
|
|
fclose( fp );
|
|
va_end( args );
|
|
}
|
|
if( echoStdout )
|
|
{
|
|
va_list args;
|
|
va_start( args , format );
|
|
vprintf( format , args );
|
|
va_end( args );
|
|
}
|
|
}
|
|
void DumpOutput2( std::vector< char* >& comments , const char* format , ... )
|
|
{
|
|
if( outputFile )
|
|
{
|
|
FILE* fp = fopen( outputFile , "a" );
|
|
va_list args;
|
|
va_start( args , format );
|
|
vfprintf( fp , format , args );
|
|
fclose( fp );
|
|
va_end( args );
|
|
}
|
|
if( echoStdout )
|
|
{
|
|
va_list args;
|
|
va_start( args , format );
|
|
vprintf( format , args );
|
|
va_end( args );
|
|
}
|
|
comments.push_back( new char[1024] );
|
|
char* str = comments.back();
|
|
va_list args;
|
|
va_start( args , format );
|
|
vsprintf( str , format , args );
|
|
va_end( args );
|
|
if( str[strlen(str)-1]=='\n' ) str[strlen(str)-1] = 0;
|
|
}
|
|
|
|
|
|
cmdLineString
|
|
In( "in" ) ,
|
|
Out( "out" ) ,
|
|
VoxelGrid( "voxel" ) ,
|
|
XForm( "xForm" );
|
|
|
|
cmdLineReadable
|
|
#if defined( _WIN32 ) || defined( _WIN64 )
|
|
Performance( "performance" ) ,
|
|
#endif // _WIN32 || _WIN64
|
|
ShowResidual( "showResidual" ) ,
|
|
NoComments( "noComments" ) ,
|
|
PolygonMesh( "polygonMesh" ) ,
|
|
Confidence( "confidence" ) ,
|
|
NormalWeights( "nWeights" ) ,
|
|
NonManifold( "nonManifold" ) ,
|
|
ASCII( "ascii" ) ,
|
|
Density( "density" ) ,
|
|
LinearFit( "linearFit" ) ,
|
|
PrimalVoxel( "primalVoxel" ) ,
|
|
#ifndef FAST_COMPILE
|
|
Double( "double" ) ,
|
|
#endif // !FAST_COMPILE
|
|
Verbose( "verbose" );
|
|
|
|
cmdLineInt
|
|
#ifndef FAST_COMPILE
|
|
Degree( "degree" , 2 ) ,
|
|
#endif // !FAST_COMPILE
|
|
Depth( "depth" , 8 ) ,
|
|
CGDepth( "cgDepth" , 0 ) ,
|
|
KernelDepth( "kernelDepth" ) ,
|
|
AdaptiveExponent( "adaptiveExp" , 1 ) ,
|
|
Iters( "iters" , 8 ) ,
|
|
VoxelDepth( "voxelDepth" , -1 ) ,
|
|
FullDepth( "fullDepth" , DEFAULT_FULL_DEPTH ) ,
|
|
#ifndef FAST_COMPILE
|
|
BType( "bType" , BOUNDARY_NEUMANN+1 ) ,
|
|
#endif // !FAST_COMPILE
|
|
MaxSolveDepth( "maxSolveDepth" ) ,
|
|
Threads( "threads" , omp_get_num_procs() );
|
|
|
|
cmdLineFloat
|
|
Color( "color" , 16.f ) ,
|
|
SamplesPerNode( "samplesPerNode" , 1.5f ) ,
|
|
Scale( "scale" , 1.1f ) ,
|
|
CGSolverAccuracy( "cgAccuracy" , float(1e-3) ) ,
|
|
LowResIterMultiplier( "iterMultiplier" , 1.f ) ,
|
|
PointWeight( "pointWeight" , 4.f );
|
|
|
|
|
|
cmdLineReadable* params[] =
|
|
{
|
|
#ifndef FAST_COMPILE
|
|
&Degree , &Double , &BType ,
|
|
#endif // !FAST_COMPILE
|
|
&In , &Depth , &Out , &XForm ,
|
|
&Scale , &Verbose , &CGSolverAccuracy , &NoComments , &LowResIterMultiplier ,
|
|
&KernelDepth , &SamplesPerNode , &Confidence , &NormalWeights , &NonManifold , &PolygonMesh , &ASCII , &ShowResidual , &VoxelDepth ,
|
|
&PointWeight , &VoxelGrid , &Threads , &MaxSolveDepth ,
|
|
&AdaptiveExponent ,
|
|
&Density ,
|
|
&FullDepth ,
|
|
&CGDepth , &Iters ,
|
|
&Color ,
|
|
&LinearFit ,
|
|
&PrimalVoxel ,
|
|
#if defined( _WIN32 ) || defined( _WIN64 )
|
|
&Performance ,
|
|
#endif // _WIN32 || _WIN64
|
|
};
|
|
|
|
|
|
void ShowUsage(char* ex)
|
|
{
|
|
printf( "Usage: %s\n" , ex );
|
|
printf( "\t --%s <input points>\n" , In.name );
|
|
|
|
printf( "\t[--%s <ouput triangle mesh>]\n" , Out.name );
|
|
|
|
printf( "\t[--%s <ouput voxel grid>]\n" , VoxelGrid.name );
|
|
|
|
#ifndef FAST_COMPILE
|
|
printf( "\t[--%s <b-spline degree>=%d]\n" , Degree.name , Degree.value );
|
|
|
|
printf( "\t[--%s <boundary type>=%d]\n" , BType.name , BType.value );
|
|
for( int i=0 ; i<BOUNDARY_COUNT ; i++ ) printf( "\t\t%d] %s\n" , i+1 , BoundaryNames[i] );
|
|
#endif // FAST_COMPILE
|
|
|
|
printf( "\t[--%s <maximum reconstruction depth>=%d]\n" , Depth.name , Depth.value );
|
|
|
|
printf( "\t[--%s <scale factor>=%f]\n" , Scale.name , Scale.value );
|
|
|
|
printf( "\t[--%s <minimum number of samples per node>=%f]\n" , SamplesPerNode.name, SamplesPerNode.value );
|
|
|
|
printf( "\t[--%s <interpolation weight>=%.3e]\n" , PointWeight.name , PointWeight.value );
|
|
|
|
printf( "\t[--%s]\n" , Confidence.name );
|
|
|
|
printf( "\t[--%s]\n" , NormalWeights.name );
|
|
|
|
#ifndef FOR_RELEASE
|
|
printf( "\t[--%s <adaptive weighting exponent>=%d]\n", AdaptiveExponent.name , AdaptiveExponent.value );
|
|
#endif // !FOR_RELEASE
|
|
|
|
printf( "\t[--%s <iterations>=%d]\n" , Iters.name , Iters.value );
|
|
|
|
#ifndef FOR_RELEASE
|
|
printf( "\t[--%s <low-resolution iteration multiplier>=%f]\n" , LowResIterMultiplier.name , LowResIterMultiplier.value );
|
|
#endif // FOR_RELEASE
|
|
|
|
printf( "\t[--%s <conjugate-gradients depth>=%d]\n" , CGDepth.name , CGDepth.value );
|
|
|
|
#ifndef FOR_RELEASE
|
|
printf( "\t[--%s <conjugate-gradients solver accuracy>=%g]\n" , CGSolverAccuracy.name , CGSolverAccuracy.value );
|
|
#endif // !FOR_RELEASE
|
|
|
|
printf( "\t[--%s <full depth>=%d]\n" , FullDepth.name , FullDepth.value );
|
|
|
|
printf( "\t[--%s <depth at which to extract the voxel grid>=<%s>]\n" , VoxelDepth.name , Depth.name );
|
|
|
|
printf( "\t[--%s]\n" , PrimalVoxel.name );
|
|
|
|
printf( "\t[--%s <pull factor>]\n" , Color.name );
|
|
|
|
printf( "\t[--%s]\n" , Density.name );
|
|
|
|
printf( "\t[--%s]\n" , LinearFit.name );
|
|
|
|
printf( "\t[--%s]\n" , PolygonMesh.name);
|
|
|
|
#ifndef FOR_RELEASE
|
|
printf( "\t[--%s]\n" , NonManifold.name );
|
|
#endif // !FOR_RELEASE
|
|
|
|
#ifdef _OPENMP
|
|
printf( "\t[--%s <num threads>=%d]\n" , Threads.name , Threads.value );
|
|
#endif // _OPENMP
|
|
|
|
printf( "\t[--%s]\n" , Verbose.name );
|
|
|
|
#ifndef FOR_RELEASE
|
|
#if defined( _WIN32 ) || defined( _WIN64 )
|
|
printf( "\t[--%s]\n" , Performance.name );
|
|
#endif // _WIN32 || _WIN64
|
|
#endif // !FOR_RELEASE
|
|
|
|
#ifndef FOR_RELEASE
|
|
printf( "\t[--%s]\n" , ASCII.name );
|
|
|
|
printf( "\t[--%s]\n" , NoComments.name );
|
|
|
|
#ifndef FAST_COMPILE
|
|
printf( "\t[--%s]\n" , Double.name );
|
|
#endif // FAST_COMPILE
|
|
#endif // !FOR_RELEASE
|
|
}
|
|
|
|
template< class Real >
|
|
struct ColorInfo
|
|
{
|
|
static Point3D< Real > ReadASCII( FILE* fp )
|
|
{
|
|
Point3D< unsigned char > c;
|
|
if( fscanf( fp , " %c %c %c " , &c[0] , &c[1] , &c[2] )!=3 ) fprintf( stderr , "[ERROR] Failed to read color\n" ) , exit( 0 );
|
|
return Point3D< Real >( (Real)c[0] , (Real)c[1] , (Real)c[2] );
|
|
};
|
|
static bool ValidPlyProperties( const bool* props ){ return ( props[0] || props[3] ) && ( props[1] || props[4] ) && ( props[2] || props[5] ); }
|
|
const static PlyProperty PlyProperties[];
|
|
};
|
|
template<>
|
|
const PlyProperty ColorInfo< float >::PlyProperties[] =
|
|
{
|
|
{ "r" , PLY_UCHAR , PLY_FLOAT , int( offsetof( Point3D< float > , coords[0] ) ) , 0 , 0 , 0 , 0 } ,
|
|
{ "g" , PLY_UCHAR , PLY_FLOAT , int( offsetof( Point3D< float > , coords[1] ) ) , 0 , 0 , 0 , 0 } ,
|
|
{ "b" , PLY_UCHAR , PLY_FLOAT , int( offsetof( Point3D< float > , coords[2] ) ) , 0 , 0 , 0 , 0 } ,
|
|
{ "red" , PLY_UCHAR , PLY_FLOAT , int( offsetof( Point3D< float > , coords[0] ) ) , 0 , 0 , 0 , 0 } ,
|
|
{ "green" , PLY_UCHAR , PLY_FLOAT , int( offsetof( Point3D< float > , coords[1] ) ) , 0 , 0 , 0 , 0 } ,
|
|
{ "blue" , PLY_UCHAR , PLY_FLOAT , int( offsetof( Point3D< float > , coords[2] ) ) , 0 , 0 , 0 , 0 }
|
|
};
|
|
template<>
|
|
const PlyProperty ColorInfo< double >::PlyProperties[] =
|
|
{
|
|
{ "r" , PLY_UCHAR , PLY_DOUBLE , int( offsetof( Point3D< double > , coords[0] ) ) , 0 , 0 , 0 , 0 } ,
|
|
{ "g" , PLY_UCHAR , PLY_DOUBLE , int( offsetof( Point3D< double > , coords[1] ) ) , 0 , 0 , 0 , 0 } ,
|
|
{ "b" , PLY_UCHAR , PLY_DOUBLE , int( offsetof( Point3D< double > , coords[2] ) ) , 0 , 0 , 0 , 0 } ,
|
|
{ "red" , PLY_UCHAR , PLY_DOUBLE , int( offsetof( Point3D< double > , coords[0] ) ) , 0 , 0 , 0 , 0 } ,
|
|
{ "green" , PLY_UCHAR , PLY_DOUBLE , int( offsetof( Point3D< double > , coords[1] ) ) , 0 , 0 , 0 , 0 } ,
|
|
{ "blue" , PLY_UCHAR , PLY_DOUBLE , int( offsetof( Point3D< double > , coords[2] ) ) , 0 , 0 , 0 , 0 }
|
|
};
|
|
|
|
double Weight( double v , double start , double end )
|
|
{
|
|
v = ( v - start ) / ( end - start );
|
|
if ( v<0 ) return 1.;
|
|
else if( v>1 ) return 0.;
|
|
else
|
|
{
|
|
// P(x) = a x^3 + b x^2 + c x + d
|
|
// P (0) = 1 , P (1) = 0 , P'(0) = 0 , P'(1) = 0
|
|
// => d = 1 , a + b + c + d = 0 , c = 0 , 3a + 2b + c = 0
|
|
// => c = 0 , d = 1 , a + b = -1 , 3a + 2b = 0
|
|
// => a = 2 , b = -3 , c = 0 , d = 1
|
|
// => P(x) = 2 x^3 - 3 x^2 + 1
|
|
return 2. * v * v * v - 3. * v * v + 1.;
|
|
}
|
|
}
|
|
|
|
#if defined( _WIN32 ) || defined( _WIN64 )
|
|
double PeakMemoryUsageMB( void )
|
|
{
|
|
HANDLE h = GetCurrentProcess();
|
|
PROCESS_MEMORY_COUNTERS pmc;
|
|
return GetProcessMemoryInfo( h , &pmc , sizeof(pmc) ) ? ( (double)pmc.PeakWorkingSetSize )/(1<<20) : 0;
|
|
}
|
|
#endif // _WIN32 || _WIN64
|
|
|
|
|
|
template< class Real >
|
|
struct OctreeProfiler
|
|
{
|
|
Octree< Real >& tree;
|
|
double t;
|
|
|
|
OctreeProfiler( Octree< Real >& t ) : tree(t) { ; }
|
|
void start( void ){ t = Time() , tree.resetLocalMemoryUsage(); }
|
|
void print( const char* header ) const
|
|
{
|
|
tree.memoryUsage();
|
|
#if defined( _WIN32 ) || defined( _WIN64 )
|
|
if( header ) printf( "%s %9.1f (s), %9.1f (MB) / %9.1f (MB) / %9.1f (MB)\n" , header , Time()-t , tree.localMemoryUsage() , tree.maxMemoryUsage() , PeakMemoryUsageMB() );
|
|
else printf( "%9.1f (s), %9.1f (MB) / %9.1f (MB) / %9.1f (MB)\n" , Time()-t , tree.localMemoryUsage() , tree.maxMemoryUsage() , PeakMemoryUsageMB() );
|
|
#else // !_WIN32 && !_WIN64
|
|
if( header ) printf( "%s %9.1f (s), %9.1f (MB) / %9.1f (MB)\n" , header , Time()-t , tree.localMemoryUsage() , tree.maxMemoryUsage() );
|
|
else printf( "%9.1f (s), %9.1f (MB) / %9.1f (MB)\n" , Time()-t , tree.localMemoryUsage() , tree.maxMemoryUsage() );
|
|
#endif // _WIN32 || _WIN64
|
|
}
|
|
void dumpOutput( const char* header ) const
|
|
{
|
|
tree.memoryUsage();
|
|
#if defined( _WIN32 ) || defined( _WIN64 )
|
|
if( header ) DumpOutput( "%s %9.1f (s), %9.1f (MB) / %9.1f (MB) / %9.1f (MB)\n" , header , Time()-t , tree.localMemoryUsage() , tree.maxMemoryUsage() , PeakMemoryUsageMB() );
|
|
else DumpOutput( "%9.1f (s), %9.1f (MB) / %9.1f (MB) / %9.1f (MB)\n" , Time()-t , tree.localMemoryUsage() , tree.maxMemoryUsage() , PeakMemoryUsageMB() );
|
|
#else // !_WIN32 && !_WIN64
|
|
if( header ) DumpOutput( "%s %9.1f (s), %9.1f (MB) / %9.1f (MB) / %9.1f (MB)\n" , header , Time()-t , tree.localMemoryUsage() , tree.maxMemoryUsage() );
|
|
else DumpOutput( "%9.1f (s), %9.1f (MB) / %9.1f (MB) / %9.1f (MB)\n" , Time()-t , tree.localMemoryUsage() , tree.maxMemoryUsage() );
|
|
#endif // _WIN32 || _WIN64
|
|
}
|
|
void dumpOutput2( std::vector< char* >& comments , const char* header ) const
|
|
{
|
|
tree.memoryUsage();
|
|
#if defined( _WIN32 ) || defined( _WIN64 )
|
|
if( header ) DumpOutput2( comments , "%s %9.1f (s), %9.1f (MB) / %9.1f (MB) / %9.1f (MB)\n" , header , Time()-t , tree.localMemoryUsage() , tree.maxMemoryUsage() , PeakMemoryUsageMB() );
|
|
else DumpOutput2( comments , "%9.1f (s), %9.1f (MB) / %9.1f (MB) / %9.1f (MB)\n" , Time()-t , tree.localMemoryUsage() , tree.maxMemoryUsage() , PeakMemoryUsageMB() );
|
|
#else // !_WIN32 && !_WIN64
|
|
if( header ) DumpOutput2( comments , "%s %9.1f (s), %9.1f (MB) / %9.1f (MB)\n" , header , Time()-t , tree.localMemoryUsage() , tree.maxMemoryUsage() );
|
|
else DumpOutput2( comments , "%9.1f (s), %9.1f (MB) / %9.1f (MB)\n" , Time()-t , tree.localMemoryUsage() , tree.maxMemoryUsage() );
|
|
#endif // _WIN32 || _WIN64
|
|
}
|
|
};
|
|
|
|
template< class Real >
|
|
XForm4x4< Real > GetPointXForm( OrientedPointStream< Real >& stream , Real scaleFactor )
|
|
{
|
|
Point3D< Real > min , max;
|
|
stream.boundingBox( min , max );
|
|
Point3D< Real > center = ( max + min ) / 2;
|
|
Real scale = std::max< Real >( max[0]-min[0] , std::max< Real >( max[1]-min[1] , max[2]-min[2] ) );
|
|
scale *= scaleFactor;
|
|
for( int i=0 ; i<3 ; i++ ) center[i] -= scale/2;
|
|
XForm4x4< Real > tXForm = XForm4x4< Real >::Identity() , sXForm = XForm4x4< Real >::Identity();
|
|
for( int i=0 ; i<3 ; i++ ) sXForm(i,i) = (Real)(1./scale ) , tXForm(3,i) = -center[i];
|
|
return sXForm * tXForm;
|
|
}
|
|
|
|
template< class Real , int Degree , BoundaryType BType , class Vertex >
|
|
int _Execute( int argc , char* argv[] )
|
|
{
|
|
typedef typename Octree< Real >::template InterpolationInfo< false > InterpolationInfo;
|
|
typedef OrientedPointStream< Real > PointStream;
|
|
typedef OrientedPointStreamWithData< Real , Point3D< Real > > PointStreamWithData;
|
|
typedef TransformedOrientedPointStream< Real > XPointStream;
|
|
typedef TransformedOrientedPointStreamWithData< Real , Point3D< Real > > XPointStreamWithData;
|
|
Reset< Real >();
|
|
int paramNum = sizeof(params)/sizeof(cmdLineReadable*);
|
|
std::vector< char* > comments;
|
|
|
|
if( Verbose.set ) echoStdout=1;
|
|
|
|
XForm4x4< Real > xForm , iXForm;
|
|
if( XForm.set )
|
|
{
|
|
FILE* fp = fopen( XForm.value , "r" );
|
|
if( !fp )
|
|
{
|
|
fprintf( stderr , "[WARNING] Could not read x-form from: %s\n" , XForm.value );
|
|
xForm = XForm4x4< Real >::Identity();
|
|
}
|
|
else
|
|
{
|
|
for( int i=0 ; i<4 ; i++ ) for( int j=0 ; j<4 ; j++ )
|
|
{
|
|
float f;
|
|
if( fscanf( fp , " %f " , &f )!=1 ) fprintf( stderr , "[ERROR] Execute: Failed to read xform\n" ) , exit( 0 );
|
|
xForm(i,j) = (Real)f;
|
|
}
|
|
fclose( fp );
|
|
}
|
|
}
|
|
else xForm = XForm4x4< Real >::Identity();
|
|
|
|
DumpOutput2( comments , "Running Screened Poisson Reconstruction (Version 9.0)\n" );
|
|
char str[1024];
|
|
for( int i=0 ; i<paramNum ; i++ )
|
|
if( params[i]->set )
|
|
{
|
|
params[i]->writeValue( str );
|
|
if( strlen( str ) ) DumpOutput2( comments , "\t--%s %s\n" , params[i]->name , str );
|
|
else DumpOutput2( comments , "\t--%s\n" , params[i]->name );
|
|
}
|
|
|
|
double startTime = Time();
|
|
Real isoValue = 0;
|
|
|
|
Octree< Real > tree;
|
|
OctreeProfiler< Real > profiler( tree );
|
|
tree.threads = Threads.value;
|
|
if( !In.set )
|
|
{
|
|
ShowUsage( argv[0] );
|
|
return 0;
|
|
}
|
|
if( !MaxSolveDepth.set ) MaxSolveDepth.value = Depth.value;
|
|
|
|
OctNode< TreeNodeData >::SetAllocator( MEMORY_ALLOCATOR_BLOCK_SIZE );
|
|
|
|
int kernelDepth = KernelDepth.set ? KernelDepth.value : Depth.value-2;
|
|
if( kernelDepth>Depth.value )
|
|
{
|
|
fprintf( stderr,"[WARNING] %s can't be greater than %s: %d <= %d\n" , KernelDepth.name , Depth.name , KernelDepth.value , Depth.value );
|
|
kernelDepth = Depth.value;
|
|
}
|
|
|
|
int pointCount;
|
|
|
|
Real pointWeightSum;
|
|
std::vector< typename Octree< Real >::PointSample >* samples = new std::vector< typename Octree< Real >::PointSample >();
|
|
std::vector< ProjectiveData< Point3D< Real > , Real > >* sampleData = NULL;
|
|
SparseNodeData< Real , WEIGHT_DEGREE >* density;
|
|
SparseNodeData< Point3D< Real > , NORMAL_DEGREE >* normalInfo;
|
|
Real targetValue = (Real)0.5;
|
|
// Read in the samples (and color data)
|
|
{
|
|
profiler.start();
|
|
PointStream* pointStream;
|
|
char* ext = GetFileExtension( In.value );
|
|
if( Color.set && Color.value>0 )
|
|
{
|
|
sampleData = new std::vector< ProjectiveData< Point3D< Real > , Real > >();
|
|
if ( !strcasecmp( ext , "bnpts" ) ) pointStream = new BinaryOrientedPointStreamWithData< Real , Point3D< Real > , float , Point3D< unsigned char > >( In.value );
|
|
else if( !strcasecmp( ext , "ply" ) ) pointStream = new PLYOrientedPointStreamWithData< Real , Point3D< Real > >( In.value , ColorInfo< Real >::PlyProperties , 6 , ColorInfo< Real >::ValidPlyProperties );
|
|
else pointStream = new ASCIIOrientedPointStreamWithData< Real , Point3D< Real > >( In.value , ColorInfo< Real >::ReadASCII );
|
|
}
|
|
else
|
|
{
|
|
if ( !strcasecmp( ext , "bnpts" ) ) pointStream = new BinaryOrientedPointStream< Real , float >( In.value );
|
|
else if( !strcasecmp( ext , "ply" ) ) pointStream = new PLYOrientedPointStream< Real >( In.value );
|
|
else pointStream = new ASCIIOrientedPointStream< Real >( In.value );
|
|
}
|
|
delete[] ext;
|
|
XPointStream _pointStream( xForm , *pointStream );
|
|
xForm = GetPointXForm( _pointStream , (Real)Scale.value ) * xForm;
|
|
if( sampleData )
|
|
{
|
|
XPointStreamWithData _pointStream( xForm , ( PointStreamWithData& )*pointStream );
|
|
pointCount = tree.template init< Point3D< Real > >( _pointStream , Depth.value , Confidence.set , *samples , sampleData );
|
|
}
|
|
else
|
|
{
|
|
XPointStream _pointStream( xForm , *pointStream );
|
|
pointCount = tree.template init< Point3D< Real > >( _pointStream , Depth.value , Confidence.set , *samples , sampleData );
|
|
}
|
|
iXForm = xForm.inverse();
|
|
delete pointStream;
|
|
#pragma omp parallel for num_threads( Threads.value )
|
|
for( int i=0 ; i<(int)samples->size() ; i++ ) (*samples)[i].sample.data.n *= (Real)-1;
|
|
|
|
DumpOutput( "Input Points / Samples: %d / %d\n" , pointCount , samples->size() );
|
|
profiler.dumpOutput2( comments , "# Read input into tree:" );
|
|
}
|
|
DenseNodeData< Real , Degree > solution;
|
|
|
|
{
|
|
DenseNodeData< Real , Degree > constraints;
|
|
InterpolationInfo* iInfo = NULL;
|
|
int solveDepth = MaxSolveDepth.value;
|
|
|
|
tree.resetNodeIndices();
|
|
|
|
// Get the kernel density estimator [If discarding, compute anew. Otherwise, compute once.]
|
|
{
|
|
profiler.start();
|
|
density = new SparseNodeData< Real , WEIGHT_DEGREE >();
|
|
*density = tree.template setDensityEstimator< WEIGHT_DEGREE >( *samples , kernelDepth , SamplesPerNode.value );
|
|
profiler.dumpOutput2( comments , "# Got kernel density:" );
|
|
}
|
|
|
|
// Transform the Hermite samples into a vector field [If discarding, compute anew. Otherwise, compute once.]
|
|
{
|
|
profiler.start();
|
|
normalInfo = new SparseNodeData< Point3D< Real > , NORMAL_DEGREE >();
|
|
*normalInfo = tree.template setNormalField< NORMAL_DEGREE >( *samples , *density , pointWeightSum , BType==BOUNDARY_NEUMANN );
|
|
profiler.dumpOutput2( comments , "# Got normal field:" );
|
|
}
|
|
|
|
if( !Density.set ) delete density , density = NULL;
|
|
|
|
// Trim the tree and prepare for multigrid
|
|
{
|
|
profiler.start();
|
|
std::vector< int > indexMap;
|
|
|
|
constexpr int MAX_DEGREE = NORMAL_DEGREE > Degree ? NORMAL_DEGREE : Degree;
|
|
tree.template inalizeForBroodedMultigrid< MAX_DEGREE , Degree , BType >( FullDepth.value , typename Octree< Real >::template HasNormalDataFunctor< NORMAL_DEGREE >( *normalInfo ) , &indexMap );
|
|
|
|
if( normalInfo ) normalInfo->remapIndices( indexMap );
|
|
if( density ) density->remapIndices( indexMap );
|
|
profiler.dumpOutput2( comments , "# Finalized tree:" );
|
|
}
|
|
|
|
// Add the FEM constraints
|
|
{
|
|
profiler.start();
|
|
constraints = tree.template initDenseNodeData< Degree >( );
|
|
tree.template addFEMConstraints< Degree , BType , NORMAL_DEGREE , BType >( FEMVFConstraintFunctor< NORMAL_DEGREE , BType , Degree , BType >( 1. , 0. ) , *normalInfo , constraints , solveDepth );
|
|
profiler.dumpOutput2( comments , "# Set FEM constraints:" );
|
|
}
|
|
|
|
// Free up the normal info [If we don't need it for subseequent iterations.]
|
|
delete normalInfo , normalInfo = NULL;
|
|
|
|
// Add the interpolation constraints
|
|
if( PointWeight.value>0 )
|
|
{
|
|
profiler.start();
|
|
iInfo = new InterpolationInfo( tree , *samples , targetValue , AdaptiveExponent.value , (Real)PointWeight.value * pointWeightSum , (Real)0 );
|
|
tree.template addInterpolationConstraints< Degree , BType >( *iInfo , constraints , solveDepth );
|
|
profiler.dumpOutput2( comments , "#Set point constraints:" );
|
|
}
|
|
|
|
DumpOutput( "Leaf Nodes / Active Nodes / Ghost Nodes: %d / %d / %d\n" , (int)tree.leaves() , (int)tree.nodes() , (int)tree.ghostNodes() );
|
|
DumpOutput( "Memory Usage: %.3f MB\n" , float( MemoryInfo::Usage())/(1<<20) );
|
|
|
|
// Solve the linear system
|
|
{
|
|
profiler.start();
|
|
typename Octree< Real >::SolverInfo solverInfo;
|
|
solverInfo.cgDepth = CGDepth.value , solverInfo.iters = Iters.value , solverInfo.cgAccuracy = CGSolverAccuracy.value , solverInfo.verbose = Verbose.set , solverInfo.showResidual = ShowResidual.set , solverInfo.lowResIterMultiplier = std::max< double >( 1. , LowResIterMultiplier.value );
|
|
solution = tree.template solveSystem< Degree , BType >( FEMSystemFunctor< Degree , BType >( 0 , 1. , 0 ) , iInfo , constraints , solveDepth , solverInfo );
|
|
profiler.dumpOutput2( comments , "# Linear system solved:" );
|
|
if( iInfo ) delete iInfo , iInfo = NULL;
|
|
}
|
|
}
|
|
|
|
CoredFileMeshData< Vertex > mesh;
|
|
|
|
{
|
|
profiler.start();
|
|
double valueSum = 0 , weightSum = 0;
|
|
typename Octree< Real >::template MultiThreadedEvaluator< Degree , BType > evaluator( &tree , solution , Threads.value );
|
|
#pragma omp parallel for num_threads( Threads.value ) reduction( + : valueSum , weightSum )
|
|
for( int j=0 ; j<samples->size() ; j++ )
|
|
{
|
|
ProjectiveData< OrientedPoint3D< Real > , Real >& sample = (*samples)[j].sample;
|
|
Real w = sample.weight;
|
|
if( w>0 ) weightSum += w , valueSum += evaluator.value( sample.data.p / sample.weight , omp_get_thread_num() , (*samples)[j].node ) * w;
|
|
}
|
|
isoValue = (Real)( valueSum / weightSum );
|
|
if( !( Color.set && Color.value>0 ) && samples ) delete samples , samples = NULL;
|
|
profiler.dumpOutput( "Got average:" );
|
|
DumpOutput( "Iso-Value: %e\n" , isoValue );
|
|
}
|
|
|
|
if( VoxelGrid.set )
|
|
{
|
|
profiler.start();
|
|
FILE* fp = fopen( VoxelGrid.value , "wb" );
|
|
if( !fp ) fprintf( stderr , "Failed to open voxel file for writing: %s\n" , VoxelGrid.value );
|
|
else
|
|
{
|
|
int res = 0;
|
|
Pointer( Real ) values = tree.template voxelEvaluate< Real , Degree , BType >( solution , res , isoValue , VoxelDepth.value , PrimalVoxel.set );
|
|
fwrite( &res , sizeof(int) , 1 , fp );
|
|
if( sizeof(Real)==sizeof(float) ) fwrite( values , sizeof(float) , res*res*res , fp );
|
|
else
|
|
{
|
|
float *fValues = new float[res*res*res];
|
|
for( int i=0 ; i<res*res*res ; i++ ) fValues[i] = float( values[i] );
|
|
fwrite( fValues , sizeof(float) , res*res*res , fp );
|
|
delete[] fValues;
|
|
}
|
|
fclose( fp );
|
|
DeletePointer( values );
|
|
}
|
|
profiler.dumpOutput( "Got voxel grid:" );
|
|
}
|
|
|
|
if( Out.set )
|
|
{
|
|
profiler.start();
|
|
SparseNodeData< ProjectiveData< Point3D< Real > , Real > , DATA_DEGREE >* colorData = NULL;
|
|
if( sampleData )
|
|
{
|
|
colorData = new SparseNodeData< ProjectiveData< Point3D< Real > , Real > , DATA_DEGREE >();
|
|
*colorData = tree.template setDataField< DATA_DEGREE , false >( *samples , *sampleData , (SparseNodeData< Real , WEIGHT_DEGREE >*)NULL );
|
|
delete sampleData , sampleData = NULL;
|
|
for( const OctNode< TreeNodeData >* n = tree.tree().nextNode() ; n ; n=tree.tree().nextNode( n ) )
|
|
{
|
|
ProjectiveData< Point3D< Real > , Real >* clr = (*colorData)( n );
|
|
if( clr ) (*clr) *= (Real)pow( Color.value , tree.depth( n ) );
|
|
}
|
|
}
|
|
tree.template getMCIsoSurface< Degree , BType , WEIGHT_DEGREE , DATA_DEGREE >( density , colorData , solution , isoValue , mesh , !LinearFit.set , !NonManifold.set , PolygonMesh.set );
|
|
DumpOutput( "Vertices / Polygons: %d / %d\n" , mesh.outOfCorePointCount()+mesh.inCorePoints.size() , mesh.polygonCount() );
|
|
if( PolygonMesh.set ) profiler.dumpOutput2( comments , "# Got polygons:" );
|
|
else profiler.dumpOutput2( comments , "# Got triangles:" );
|
|
|
|
if( colorData ) delete colorData , colorData = NULL;
|
|
|
|
if( NoComments.set )
|
|
{
|
|
if( ASCII.set ) PlyWritePolygons( Out.value , &mesh , PLY_ASCII , NULL , 0 , iXForm );
|
|
else PlyWritePolygons( Out.value , &mesh , PLY_BINARY_NATIVE , NULL , 0 , iXForm );
|
|
}
|
|
else
|
|
{
|
|
if( ASCII.set ) PlyWritePolygons( Out.value , &mesh , PLY_ASCII , &comments[0] , (int)comments.size() , iXForm );
|
|
else PlyWritePolygons( Out.value , &mesh , PLY_BINARY_NATIVE , &comments[0] , (int)comments.size() , iXForm );
|
|
}
|
|
}
|
|
if( density ) delete density , density = NULL;
|
|
DumpOutput2( comments , "# Total Solve: %9.1f (s), %9.1f (MB)\n" , Time()-startTime , tree.maxMemoryUsage() );
|
|
|
|
return 1;
|
|
}
|
|
|
|
#if defined( _WIN32 ) || defined( _WIN64 )
|
|
inline double to_seconds( const FILETIME& ft )
|
|
{
|
|
const double low_to_sec=100e-9; // 100 nanoseconds
|
|
const double high_to_sec=low_to_sec*4294967296.0;
|
|
return ft.dwLowDateTime*low_to_sec+ft.dwHighDateTime*high_to_sec;
|
|
}
|
|
#endif // _WIN32 || _WIN64
|
|
|
|
#ifndef FAST_COMPILE
|
|
template< class Real , class Vertex >
|
|
int Execute( int argc , char* argv[] )
|
|
{
|
|
switch( BType.value )
|
|
{
|
|
case BOUNDARY_FREE+1:
|
|
{
|
|
switch( Degree.value )
|
|
{
|
|
case 1: return _Execute< Real , 1 , BOUNDARY_FREE , Vertex >( argc , argv );
|
|
case 2: return _Execute< Real , 2 , BOUNDARY_FREE , Vertex >( argc , argv );
|
|
case 3: return _Execute< Real , 3 , BOUNDARY_FREE , Vertex >( argc , argv );
|
|
case 4: return _Execute< Real , 4 , BOUNDARY_FREE , Vertex >( argc , argv );
|
|
default: fprintf( stderr , "[ERROR] Only B-Splines of degree 1 - 4 are supported" ) ; return EXIT_FAILURE;
|
|
}
|
|
}
|
|
case BOUNDARY_NEUMANN+1:
|
|
{
|
|
switch( Degree.value )
|
|
{
|
|
case 1: return _Execute< Real , 1 , BOUNDARY_NEUMANN , Vertex >( argc , argv );
|
|
case 2: return _Execute< Real , 2 , BOUNDARY_NEUMANN , Vertex >( argc , argv );
|
|
case 3: return _Execute< Real , 3 , BOUNDARY_NEUMANN , Vertex >( argc , argv );
|
|
case 4: return _Execute< Real , 4 , BOUNDARY_NEUMANN , Vertex >( argc , argv );
|
|
default: fprintf( stderr , "[ERROR] Only B-Splines of degree 1 - 4 are supported" ) ; return EXIT_FAILURE;
|
|
}
|
|
}
|
|
case BOUNDARY_DIRICHLET+1:
|
|
{
|
|
switch( Degree.value )
|
|
{
|
|
case 1: return _Execute< Real , 1 , BOUNDARY_DIRICHLET , Vertex >( argc , argv );
|
|
case 2: return _Execute< Real , 2 , BOUNDARY_DIRICHLET , Vertex >( argc , argv );
|
|
case 3: return _Execute< Real , 3 , BOUNDARY_DIRICHLET , Vertex >( argc , argv );
|
|
case 4: return _Execute< Real , 4 , BOUNDARY_DIRICHLET , Vertex >( argc , argv );
|
|
default: fprintf( stderr , "[ERROR] Only B-Splines of degree 1 - 4 are supported" ) ; return EXIT_FAILURE;
|
|
}
|
|
}
|
|
default: fprintf( stderr , "[ERROR] Not a valid boundary type: %d\n" , BType.value ) ; return EXIT_FAILURE;
|
|
}
|
|
}
|
|
#endif // !FAST_COMPILE
|
|
int main( int argc , char* argv[] )
|
|
{
|
|
#ifdef ARRAY_DEBUG
|
|
fprintf( stderr , "[WARNING] Running in array debugging mode\n" );
|
|
#endif // ARRAY_DEBUG
|
|
#if defined( WIN32 ) && defined( MAX_MEMORY_GB )
|
|
if( MAX_MEMORY_GB>0 )
|
|
{
|
|
SIZE_T peakMemory = 1;
|
|
peakMemory <<= 30;
|
|
peakMemory *= MAX_MEMORY_GB;
|
|
printf( "Limiting memory usage to %.2f GB\n" , float( peakMemory>>30 ) );
|
|
HANDLE h = CreateJobObject( NULL , NULL );
|
|
AssignProcessToJobObject( h , GetCurrentProcess() );
|
|
|
|
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
|
|
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_JOB_MEMORY;
|
|
jeli.JobMemoryLimit = peakMemory;
|
|
if( !SetInformationJobObject( h , JobObjectExtendedLimitInformation , &jeli , sizeof( jeli ) ) )
|
|
fprintf( stderr , "Failed to set memory limit\n" );
|
|
}
|
|
#endif // defined( WIN32 ) && defined( MAX_MEMORY_GB )
|
|
double t = Time();
|
|
|
|
cmdLineParse( argc-1 , &argv[1] , sizeof(params)/sizeof(cmdLineReadable*) , params , 1 );
|
|
#ifdef FAST_COMPILE
|
|
static const int Degree = 2;
|
|
static const BoundaryType BType = BOUNDARY_NEUMANN;
|
|
fprintf( stderr , "[WARNING] Compiling for degree-%d, boundary-%s, single-precision _only_\n" , Degree , BoundaryNames[ BType ] );
|
|
if( Density.set )
|
|
if( Color.set && Color.value>0 ) return _Execute< float , Degree , BType , PlyColorAndValueVertex< float > >( argc , argv );
|
|
else return _Execute< float , Degree , BType , PlyValueVertex< float > >( argc , argv );
|
|
else
|
|
if( Color.set && Color.value>0 ) return _Execute< float , Degree , BType , PlyColorVertex< float > >( argc , argv );
|
|
else return _Execute< float , Degree , BType , PlyVertex< float > >( argc , argv );
|
|
#else // !FAST_COMPILE
|
|
{
|
|
if( Density.set )
|
|
if( Color.set && Color.value>0 )
|
|
if( Double.set ) Execute< double , PlyColorAndValueVertex< float > >( argc , argv );
|
|
else Execute< float , PlyColorAndValueVertex< float > >( argc , argv );
|
|
else
|
|
if( Double.set ) Execute< double , PlyValueVertex< float > >( argc , argv );
|
|
else Execute< float , PlyValueVertex< float > >( argc , argv );
|
|
else
|
|
if( Color.set && Color.value>0 )
|
|
if( Double.set ) Execute< double , PlyColorVertex< float > >( argc , argv );
|
|
else Execute< float , PlyColorVertex< float > >( argc , argv );
|
|
else
|
|
if( Double.set ) Execute< double , PlyVertex< float > >( argc , argv );
|
|
else Execute< float , PlyVertex< float > >( argc , argv );
|
|
}
|
|
#endif // FAST_COMPILE
|
|
#if defined( _WIN32 ) || defined( _WIN64 )
|
|
if( Performance.set )
|
|
{
|
|
HANDLE cur_thread=GetCurrentThread();
|
|
FILETIME tcreat, texit, tkernel, tuser;
|
|
if( GetThreadTimes( cur_thread , &tcreat , &texit , &tkernel , &tuser ) )
|
|
printf( "Time (Wall/User/Kernel): %.2f / %.2f / %.2f\n" , Time()-t , to_seconds( tuser ) , to_seconds( tkernel ) );
|
|
else printf( "Time: %.2f\n" , Time()-t );
|
|
HANDLE h = GetCurrentProcess();
|
|
PROCESS_MEMORY_COUNTERS pmc;
|
|
if( GetProcessMemoryInfo( h , &pmc , sizeof(pmc) ) ) printf( "Peak Memory (MB): %d\n" , (int)( pmc.PeakWorkingSetSize>>20 ) );
|
|
}
|
|
#endif // _WIN32 || _WIN64
|
|
return EXIT_SUCCESS;
|
|
}
|