kopia lustrzana https://github.com/OpenDroneMap/ODM
Merge pull request #956 from pierotofy/simplifydtm
DEM simplification, filtering for allpull/962/head
commit
2a94b42889
|
@ -8,6 +8,8 @@ add_subdirectory(odm_extract_utm)
|
|||
add_subdirectory(odm_georef)
|
||||
add_subdirectory(odm_orthophoto)
|
||||
add_subdirectory(odm_cleanmesh)
|
||||
add_subdirectory(odm_filterpoints)
|
||||
|
||||
if (ODM_BUILD_SLAM)
|
||||
add_subdirectory(odm_slam)
|
||||
endif ()
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
project(odm_filterpoints)
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
# Add compiler options.
|
||||
add_definitions(-Wall -Wextra -Wconversion -pedantic -std=c++11)
|
||||
|
||||
# PDAL and jsoncpp
|
||||
find_package(PDAL REQUIRED CONFIG)
|
||||
include_directories(${PDAL_INCLUDE_DIRS})
|
||||
include_directories("${PROJECT_SOURCE_DIR}/../../SuperBuild/src/pdal/vendor/jsoncpp/dist")
|
||||
link_directories(${PDAL_LIBRARY_DIRS})
|
||||
add_definitions(${PDAL_DEFINITIONS})
|
||||
|
||||
# Add source directory
|
||||
aux_source_directory("./src" SRC_LIST)
|
||||
|
||||
# Add exectuteable
|
||||
add_executable(${PROJECT_NAME} ${SRC_LIST})
|
||||
|
||||
# Link
|
||||
target_link_libraries(${PROJECT_NAME} jsoncpp ${PDAL_LIBRARIES})
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifndef CMD_LINE_PARSER_INCLUDED
|
||||
#define CMD_LINE_PARSER_INCLUDED
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef WIN32
|
||||
int strcasecmp( const char* c1 , const char* c2 );
|
||||
#endif // WIN32
|
||||
|
||||
class cmdLineReadable
|
||||
{
|
||||
public:
|
||||
bool set;
|
||||
char *name;
|
||||
cmdLineReadable( const char *name );
|
||||
virtual ~cmdLineReadable( void );
|
||||
virtual int read( char** argv , int argc );
|
||||
virtual void writeValue( char* str ) const;
|
||||
};
|
||||
|
||||
template< class Type > void cmdLineWriteValue( Type t , char* str );
|
||||
template< class Type > void cmdLineCleanUp( Type* t );
|
||||
template< class Type > Type cmdLineInitialize( void );
|
||||
template< class Type > Type cmdLineCopy( Type t );
|
||||
template< class Type > Type cmdLineStringToType( const char* str );
|
||||
|
||||
template< class Type >
|
||||
class cmdLineParameter : public cmdLineReadable
|
||||
{
|
||||
public:
|
||||
Type value;
|
||||
cmdLineParameter( const char *name );
|
||||
cmdLineParameter( const char *name , Type v );
|
||||
~cmdLineParameter( void );
|
||||
int read( char** argv , int argc );
|
||||
void writeValue( char* str ) const;
|
||||
bool expectsArg( void ) const { return true; }
|
||||
};
|
||||
|
||||
template< class Type , int Dim >
|
||||
class cmdLineParameterArray : public cmdLineReadable
|
||||
{
|
||||
public:
|
||||
Type values[Dim];
|
||||
cmdLineParameterArray( const char *name, const Type* v=NULL );
|
||||
~cmdLineParameterArray( void );
|
||||
int read( char** argv , int argc );
|
||||
void writeValue( char* str ) const;
|
||||
bool expectsArg( void ) const { return true; }
|
||||
};
|
||||
|
||||
template< class Type >
|
||||
class cmdLineParameters : public cmdLineReadable
|
||||
{
|
||||
public:
|
||||
int count;
|
||||
Type *values;
|
||||
cmdLineParameters( const char* name );
|
||||
~cmdLineParameters( void );
|
||||
int read( char** argv , int argc );
|
||||
void writeValue( char* str ) const;
|
||||
bool expectsArg( void ) const { return true; }
|
||||
};
|
||||
|
||||
void cmdLineParse( int argc , char **argv, cmdLineReadable** params );
|
||||
char* FileExtension( char* fileName );
|
||||
char* LocalFileName( char* fileName );
|
||||
char* DirectoryName( char* fileName );
|
||||
char* GetFileExtension( const char* fileName );
|
||||
char* GetLocalFileName( const char* fileName );
|
||||
char** ReadWords( const char* fileName , int& cnt );
|
||||
|
||||
#include "CmdLineParser.inl"
|
||||
#endif // CMD_LINE_PARSER_INCLUDED
|
|
@ -0,0 +1,300 @@
|
|||
/* -*- 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.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <string.h>
|
||||
|
||||
#if defined( WIN32 ) || defined( _WIN64 )
|
||||
inline int strcasecmp( const char* c1 , const char* c2 ){ return _stricmp( c1 , c2 ); }
|
||||
#endif // WIN32 || _WIN64
|
||||
|
||||
template< > void cmdLineCleanUp< int >( int* t ){ *t = 0; }
|
||||
template< > void cmdLineCleanUp< float >( float* t ){ *t = 0; }
|
||||
template< > void cmdLineCleanUp< double >( double* t ){ *t = 0; }
|
||||
template< > void cmdLineCleanUp< char* >( char** t ){ if( *t ) free( *t ) ; *t = NULL; }
|
||||
template< > int cmdLineInitialize< int >( void ){ return 0; }
|
||||
template< > float cmdLineInitialize< float >( void ){ return 0.f; }
|
||||
template< > double cmdLineInitialize< double >( void ){ return 0.; }
|
||||
template< > char* cmdLineInitialize< char* >( void ){ return NULL; }
|
||||
template< > void cmdLineWriteValue< int >( int t , char* str ){ sprintf( str , "%d" , t ); }
|
||||
template< > void cmdLineWriteValue< float >( float t , char* str ){ sprintf( str , "%f" , t ); }
|
||||
template< > void cmdLineWriteValue< double >( double t , char* str ){ sprintf( str , "%f" , t ); }
|
||||
template< > void cmdLineWriteValue< char* >( char* t , char* str ){ if( t ) sprintf( str , "%s" , t ) ; else str[0]=0; }
|
||||
template< > int cmdLineCopy( int t ){ return t; }
|
||||
template< > float cmdLineCopy( float t ){ return t; }
|
||||
template< > double cmdLineCopy( double t ){ return t; }
|
||||
#if defined( WIN32 ) || defined( _WIN64 )
|
||||
template< > char* cmdLineCopy( char* t ){ return _strdup( t ); }
|
||||
#else // !WIN32 && !_WIN64
|
||||
template< > char* cmdLineCopy( char* t ){ return strdup( t ); }
|
||||
#endif // WIN32 || _WIN64
|
||||
template< > int cmdLineStringToType( const char* str ){ return atoi( str ); }
|
||||
template< > float cmdLineStringToType( const char* str ){ return float( atof( str ) ); }
|
||||
template< > double cmdLineStringToType( const char* str ){ return double( atof( str ) ); }
|
||||
#if defined( WIN32 ) || defined( _WIN64 )
|
||||
template< > char* cmdLineStringToType( const char* str ){ return _strdup( str ); }
|
||||
#else // !WIN32 && !_WIN64
|
||||
template< > char* cmdLineStringToType( const char* str ){ return strdup( str ); }
|
||||
#endif // WIN32 || _WIN64
|
||||
|
||||
|
||||
/////////////////////
|
||||
// cmdLineReadable //
|
||||
/////////////////////
|
||||
#if defined( WIN32 ) || defined( _WIN64 )
|
||||
inline cmdLineReadable::cmdLineReadable( const char *name ) : set(false) { this->name = _strdup( name ); }
|
||||
#else // !WIN32 && !_WIN64
|
||||
inline cmdLineReadable::cmdLineReadable( const char *name ) : set(false) { this->name = strdup( name ); }
|
||||
#endif // WIN32 || _WIN64
|
||||
|
||||
inline cmdLineReadable::~cmdLineReadable( void ){ if( name ) free( name ) ; name = NULL; }
|
||||
inline int cmdLineReadable::read( char** , int ){ set = true ; return 0; }
|
||||
inline void cmdLineReadable::writeValue( char* str ) const { str[0] = 0; }
|
||||
|
||||
//////////////////////
|
||||
// cmdLineParameter //
|
||||
//////////////////////
|
||||
template< class Type > cmdLineParameter< Type >::~cmdLineParameter( void ) { cmdLineCleanUp( &value ); }
|
||||
template< class Type > cmdLineParameter< Type >::cmdLineParameter( const char *name ) : cmdLineReadable( name ){ value = cmdLineInitialize< Type >(); }
|
||||
template< class Type > cmdLineParameter< Type >::cmdLineParameter( const char *name , Type v ) : cmdLineReadable( name ){ value = cmdLineCopy< Type >( v ); }
|
||||
template< class Type >
|
||||
int cmdLineParameter< Type >::read( char** argv , int argc )
|
||||
{
|
||||
if( argc>0 )
|
||||
{
|
||||
cmdLineCleanUp< Type >( &value ) , value = cmdLineStringToType< Type >( argv[0] );
|
||||
set = true;
|
||||
return 1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
template< class Type >
|
||||
void cmdLineParameter< Type >::writeValue( char* str ) const { cmdLineWriteValue< Type >( value , str ); }
|
||||
|
||||
|
||||
///////////////////////////
|
||||
// cmdLineParameterArray //
|
||||
///////////////////////////
|
||||
template< class Type , int Dim >
|
||||
cmdLineParameterArray< Type , Dim >::cmdLineParameterArray( const char *name , const Type* v ) : cmdLineReadable( name )
|
||||
{
|
||||
if( v ) for( int i=0 ; i<Dim ; i++ ) values[i] = cmdLineCopy< Type >( v[i] );
|
||||
else for( int i=0 ; i<Dim ; i++ ) values[i] = cmdLineInitialize< Type >();
|
||||
}
|
||||
template< class Type , int Dim >
|
||||
cmdLineParameterArray< Type , Dim >::~cmdLineParameterArray( void ){ for( int i=0 ; i<Dim ; i++ ) cmdLineCleanUp< Type >( values+i ); }
|
||||
template< class Type , int Dim >
|
||||
int cmdLineParameterArray< Type , Dim >::read( char** argv , int argc )
|
||||
{
|
||||
if( argc>=Dim )
|
||||
{
|
||||
for( int i=0 ; i<Dim ; i++ ) cmdLineCleanUp< Type >( values+i ) , values[i] = cmdLineStringToType< Type >( argv[i] );
|
||||
set = true;
|
||||
return Dim;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
template< class Type , int Dim >
|
||||
void cmdLineParameterArray< Type , Dim >::writeValue( char* str ) const
|
||||
{
|
||||
char* temp=str;
|
||||
for( int i=0 ; i<Dim ; i++ )
|
||||
{
|
||||
cmdLineWriteValue< Type >( values[i] , temp );
|
||||
temp = str+strlen( str );
|
||||
}
|
||||
}
|
||||
///////////////////////
|
||||
// cmdLineParameters //
|
||||
///////////////////////
|
||||
template< class Type >
|
||||
cmdLineParameters< Type >::cmdLineParameters( const char* name ) : cmdLineReadable( name ) , values(NULL) , count(0) { }
|
||||
template< class Type >
|
||||
cmdLineParameters< Type >::~cmdLineParameters( void )
|
||||
{
|
||||
if( values ) delete[] values;
|
||||
values = NULL;
|
||||
count = 0;
|
||||
}
|
||||
template< class Type >
|
||||
int cmdLineParameters< Type >::read( char** argv , int argc )
|
||||
{
|
||||
if( values ) delete[] values;
|
||||
values = NULL;
|
||||
|
||||
if( argc>0 )
|
||||
{
|
||||
count = atoi(argv[0]);
|
||||
if( count <= 0 || argc <= count ) return 1;
|
||||
values = new Type[count];
|
||||
if( !values ) return 0;
|
||||
for( int i=0 ; i<count ; i++ ) values[i] = cmdLineStringToType< Type >( argv[i+1] );
|
||||
set = true;
|
||||
return count+1;
|
||||
}
|
||||
else return 0;
|
||||
}
|
||||
template< class Type >
|
||||
void cmdLineParameters< Type >::writeValue( char* str ) const
|
||||
{
|
||||
char* temp=str;
|
||||
for( int i=0 ; i<count ; i++ )
|
||||
{
|
||||
cmdLineWriteValue< Type >( values[i] , temp );
|
||||
temp = str+strlen( str );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline char* FileExtension( char* fileName )
|
||||
{
|
||||
char* temp = fileName;
|
||||
for( unsigned int i=0 ; i<strlen(fileName) ; i++ ) if( fileName[i]=='.' ) temp = &fileName[i+1];
|
||||
return temp;
|
||||
}
|
||||
|
||||
inline char* GetFileExtension( const char* fileName )
|
||||
{
|
||||
char* fileNameCopy;
|
||||
char* ext=NULL;
|
||||
char* temp;
|
||||
|
||||
fileNameCopy=new char[strlen(fileName)+1];
|
||||
assert(fileNameCopy);
|
||||
strcpy(fileNameCopy,fileName);
|
||||
temp=strtok(fileNameCopy,".");
|
||||
while(temp!=NULL)
|
||||
{
|
||||
if(ext!=NULL){delete[] ext;}
|
||||
ext=new char[strlen(temp)+1];
|
||||
assert(ext);
|
||||
strcpy(ext,temp);
|
||||
temp=strtok(NULL,".");
|
||||
}
|
||||
delete[] fileNameCopy;
|
||||
return ext;
|
||||
}
|
||||
inline char* GetLocalFileName( const char* fileName )
|
||||
{
|
||||
char* fileNameCopy;
|
||||
char* name=NULL;
|
||||
char* temp;
|
||||
|
||||
fileNameCopy=new char[strlen(fileName)+1];
|
||||
assert(fileNameCopy);
|
||||
strcpy(fileNameCopy,fileName);
|
||||
temp=strtok(fileNameCopy,"\\");
|
||||
while(temp!=NULL){
|
||||
if(name!=NULL){delete[] name;}
|
||||
name=new char[strlen(temp)+1];
|
||||
assert(name);
|
||||
strcpy(name,temp);
|
||||
temp=strtok(NULL,"\\");
|
||||
}
|
||||
delete[] fileNameCopy;
|
||||
return name;
|
||||
}
|
||||
inline char* LocalFileName( char* fileName )
|
||||
{
|
||||
char* temp = fileName;
|
||||
for( int i=0 ; i<(int)strlen(fileName) ; i++ ) if( fileName[i] =='\\' ) temp = &fileName[i+1];
|
||||
return temp;
|
||||
}
|
||||
inline char* DirectoryName( char* fileName )
|
||||
{
|
||||
for( int i=int( strlen(fileName) )-1 ; i>=0 ; i-- )
|
||||
if( fileName[i] =='\\' )
|
||||
{
|
||||
fileName[i] = 0;
|
||||
return fileName;
|
||||
}
|
||||
fileName[0] = 0;
|
||||
return fileName;
|
||||
}
|
||||
|
||||
inline void cmdLineParse( int argc , char **argv , cmdLineReadable** params )
|
||||
{
|
||||
while( argc>0 )
|
||||
{
|
||||
if( argv[0][0]=='-' )
|
||||
{
|
||||
cmdLineReadable* readable=NULL;
|
||||
for( int i=0 ; params[i]!=NULL && readable==NULL ; i++ ) if( !strcasecmp( params[i]->name , argv[0]+1 ) ) readable = params[i];
|
||||
if( readable )
|
||||
{
|
||||
int j = readable->read( argv+1 , argc-1 );
|
||||
argv += j , argc -= j;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr , "[WARNING] Invalid option: %s\n" , argv[0] );
|
||||
for( int i=0 ; params[i]!=NULL ; i++ ) printf( "\t-%s\n" , params[i]->name );
|
||||
}
|
||||
}
|
||||
else fprintf( stderr , "[WARNING] Parameter name should be of the form -<name>: %s\n" , argv[0] );
|
||||
++argv , --argc;
|
||||
}
|
||||
}
|
||||
|
||||
inline char** ReadWords(const char* fileName,int& cnt)
|
||||
{
|
||||
char** names;
|
||||
char temp[500];
|
||||
FILE* fp;
|
||||
|
||||
fp=fopen(fileName,"r");
|
||||
if(!fp){return NULL;}
|
||||
cnt=0;
|
||||
while(fscanf(fp," %s ",temp)==1){cnt++;}
|
||||
fclose(fp);
|
||||
|
||||
names=new char*[cnt];
|
||||
if(!names){return NULL;}
|
||||
|
||||
fp=fopen(fileName,"r");
|
||||
if(!fp){
|
||||
delete[] names;
|
||||
cnt=0;
|
||||
return NULL;
|
||||
}
|
||||
cnt=0;
|
||||
while(fscanf(fp," %s ",temp)==1){
|
||||
names[cnt]=new char[strlen(temp)+1];
|
||||
if(!names){
|
||||
for(int j=0;j<cnt;j++){delete[] names[j];}
|
||||
delete[] names;
|
||||
cnt=0;
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
strcpy(names[cnt],temp);
|
||||
cnt++;
|
||||
}
|
||||
fclose(fp);
|
||||
return names;
|
||||
}
|
|
@ -0,0 +1,454 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2015, Peter J. Gadomski <pete.gadomski@gmail.com>
|
||||
*
|
||||
* 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 Hobu, Inc. or Flaxen Geo Consulting 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.
|
||||
****************************************************************************/
|
||||
|
||||
// Modified to not cast to double and to use certain type identifier ("float" vs "float32")
|
||||
|
||||
#include "FloatPlyReader.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <pdal/PDALUtils.hpp>
|
||||
#include <pdal/PointView.hpp>
|
||||
#include <pdal/util/IStream.hpp>
|
||||
|
||||
namespace pdal
|
||||
{
|
||||
|
||||
|
||||
FloatPlyReader::FloatPlyReader() : m_vertexElt(nullptr)
|
||||
{}
|
||||
|
||||
|
||||
std::string FloatPlyReader::readLine()
|
||||
{
|
||||
m_line.clear();
|
||||
if (m_lines.size())
|
||||
{
|
||||
m_line = m_lines.top();
|
||||
m_lines.pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
std::getline(*m_stream, m_line);
|
||||
} while (m_line.empty() && m_stream->good());
|
||||
}
|
||||
Utils::trimTrailing(m_line);
|
||||
m_linePos = Utils::extract(m_line, 0,
|
||||
[](char c){ return !std::isspace(c); });
|
||||
return std::string(m_line, 0, m_linePos);
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::pushLine()
|
||||
{
|
||||
m_lines.push(m_line);
|
||||
}
|
||||
|
||||
|
||||
std::string FloatPlyReader::nextWord()
|
||||
{
|
||||
std::string s;
|
||||
std::string::size_type cnt = Utils::extractSpaces(m_line, m_linePos);
|
||||
m_linePos += cnt;
|
||||
if (m_linePos == m_line.size())
|
||||
return s;
|
||||
|
||||
cnt = Utils::extract(m_line, m_linePos,
|
||||
[](char c){ return !std::isspace(c); });
|
||||
s = std::string(m_line, m_linePos, cnt);
|
||||
m_linePos += cnt;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::extractMagic()
|
||||
{
|
||||
std::string first = readLine();
|
||||
if (first != "ply")
|
||||
throwError("File isn't a PLY file. 'ply' not found.");
|
||||
if (m_linePos != m_line.size())
|
||||
throwError("Text found following 'ply' keyword.");
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::extractEnd()
|
||||
{
|
||||
std::string first = readLine();
|
||||
if (first != "end_header")
|
||||
throwError("'end_header' expected but found line beginning with '" +
|
||||
first + "' instead.");
|
||||
if (m_linePos != m_line.size())
|
||||
throwError("Text found following 'end_header' keyword.");
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::extractFormat()
|
||||
{
|
||||
std::string word = readLine();
|
||||
if (word != "format")
|
||||
throwError("Expected format line not found in PLY file.");
|
||||
|
||||
word = nextWord();
|
||||
if (word == "ascii")
|
||||
m_format = Format::Ascii;
|
||||
else if (word == "binary_big_endian")
|
||||
m_format = Format::BinaryBe;
|
||||
else if (word == "binary_little_endian")
|
||||
m_format = Format::BinaryLe;
|
||||
else
|
||||
throwError("Unrecognized PLY format: '" + word + "'.");
|
||||
|
||||
word = nextWord();
|
||||
if (word != "1.0")
|
||||
throwError("Unsupported PLY version: '" + word + "'.");
|
||||
}
|
||||
|
||||
|
||||
Dimension::Type FloatPlyReader::getType(const std::string& name)
|
||||
{
|
||||
static std::map<std::string, Dimension::Type> types =
|
||||
{
|
||||
{ "int8", Dimension::Type::Signed8 },
|
||||
{ "uint8", Dimension::Type::Unsigned8 },
|
||||
{ "int16", Dimension::Type::Signed16 },
|
||||
{ "uint16", Dimension::Type::Unsigned16 },
|
||||
{ "int32", Dimension::Type::Signed32 },
|
||||
{ "uint32", Dimension::Type::Unsigned32 },
|
||||
{ "float32", Dimension::Type::Float },
|
||||
{ "float64", Dimension::Type::Double },
|
||||
|
||||
{ "char", Dimension::Type::Signed8 },
|
||||
{ "uchar", Dimension::Type::Unsigned8 },
|
||||
{ "short", Dimension::Type::Signed16 },
|
||||
{ "ushort", Dimension::Type::Unsigned16 },
|
||||
{ "int", Dimension::Type::Signed32 },
|
||||
{ "uint", Dimension::Type::Unsigned32 },
|
||||
{ "float", Dimension::Type::Float },
|
||||
{ "double", Dimension::Type::Double }
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
return types.at(name);
|
||||
}
|
||||
catch (std::out_of_range&)
|
||||
{}
|
||||
return Dimension::Type::None;
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::extractProperty(Element& element)
|
||||
{
|
||||
std::string word = nextWord();
|
||||
Dimension::Type type = getType(word);
|
||||
|
||||
if (type != Dimension::Type::None)
|
||||
{
|
||||
std::string name = nextWord();
|
||||
if (name.empty())
|
||||
throwError("No name for property of element '" +
|
||||
element.m_name + "'.");
|
||||
element.m_properties.push_back(
|
||||
std::unique_ptr<Property>(new SimpleProperty(name, type)));
|
||||
}
|
||||
else if (word == "list")
|
||||
{
|
||||
if (element.m_name == "vertex")
|
||||
throwError("List properties are not supported for the 'vertex' "
|
||||
"element.");
|
||||
|
||||
word = nextWord();
|
||||
Dimension::Type countType = getType(word);
|
||||
if (countType == Dimension::Type::None)
|
||||
throwError("No valid count type for list property of element '" +
|
||||
element.m_name + "'.");
|
||||
word = nextWord();
|
||||
Dimension::Type listType = getType(word);
|
||||
if (listType == Dimension::Type::None)
|
||||
throwError("No valid list type for list property of element '" +
|
||||
element.m_name + "'.");
|
||||
std::string name = nextWord();
|
||||
if (name.empty())
|
||||
throwError("No name for property of element '" +
|
||||
element.m_name + "'.");
|
||||
element.m_properties.push_back(
|
||||
std::unique_ptr<Property>(new ListProperty(name, countType,
|
||||
listType)));
|
||||
}
|
||||
else
|
||||
throwError("Invalid property type '" + word + "'.");
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::extractProperties(Element& element)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
std::string word = readLine();
|
||||
if (word == "comment" || word == "obj_info")
|
||||
continue;
|
||||
else if (word == "property")
|
||||
extractProperty(element);
|
||||
else
|
||||
{
|
||||
pushLine();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool FloatPlyReader::extractElement()
|
||||
{
|
||||
std::string word = readLine();
|
||||
if (word == "comment" || word == "obj_info")
|
||||
return true;
|
||||
else if (word == "end_header")
|
||||
{
|
||||
pushLine();
|
||||
return false;
|
||||
}
|
||||
else if (word == "element")
|
||||
{
|
||||
std::string name = nextWord();
|
||||
if (name.empty())
|
||||
throwError("Missing element name.");
|
||||
long count = std::stol(nextWord());
|
||||
if (count < 0)
|
||||
throwError("Invalid count for element '" + name + "'.");
|
||||
m_elements.emplace_back(name, count);
|
||||
extractProperties(m_elements.back());
|
||||
return true;
|
||||
}
|
||||
throwError("Invalid keyword '" + word + "' when expecting an element.");
|
||||
return false; // quiet compiler
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::extractHeader()
|
||||
{
|
||||
m_elements.clear();
|
||||
extractMagic();
|
||||
extractFormat();
|
||||
while (extractElement())
|
||||
;
|
||||
extractEnd();
|
||||
m_dataPos = m_stream->tellg();
|
||||
|
||||
for (Element& elt : m_elements)
|
||||
if (elt.m_name == "vertex")
|
||||
m_vertexElt = &elt;
|
||||
if (!m_vertexElt)
|
||||
throwError("Can't read PLY file without a 'vertex' element.");
|
||||
}
|
||||
|
||||
|
||||
std::string FloatPlyReader::getName() const
|
||||
{
|
||||
return "FloatPlyReader";
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::initialize()
|
||||
{
|
||||
m_stream = Utils::openFile(m_filename, true);
|
||||
if (!m_stream)
|
||||
throwError("Couldn't open '" + m_filename + "'.");
|
||||
extractHeader();
|
||||
Utils::closeFile(m_stream);
|
||||
m_stream = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::addDimensions(PointLayoutPtr layout)
|
||||
{
|
||||
// Override XYZ
|
||||
// layout->registerDim(Dimension::Id::X);
|
||||
// layout->registerDim(Dimension::Id::Y);
|
||||
// layout->registerDim(Dimension::Id::Z);
|
||||
|
||||
for (auto& elt : m_elements)
|
||||
{
|
||||
if (elt.m_name == "vertex")
|
||||
{
|
||||
for (auto& prop : elt.m_properties)
|
||||
{
|
||||
auto vprop = static_cast<SimpleProperty *>(prop.get());
|
||||
layout->registerOrAssignDim(vprop->m_name, vprop->m_type);
|
||||
vprop->setDim(
|
||||
layout->registerOrAssignDim(vprop->m_name, vprop->m_type));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
throwError("No 'vertex' element in header.");
|
||||
}
|
||||
|
||||
|
||||
bool FloatPlyReader::readProperty(Property *prop, PointRef& point)
|
||||
{
|
||||
if (!m_stream->good())
|
||||
return false;
|
||||
prop->read(m_stream, m_format, point);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::SimpleProperty::read(std::istream *stream,
|
||||
FloatPlyReader::Format format, PointRef& point)
|
||||
{
|
||||
if (format == Format::Ascii)
|
||||
{
|
||||
double d;
|
||||
*stream >> d;
|
||||
point.setField(m_dim, d);
|
||||
}
|
||||
else if (format == Format::BinaryLe)
|
||||
{
|
||||
ILeStream in(stream);
|
||||
Everything e = Utils::extractDim(in, m_type);
|
||||
point.setField(m_dim, m_type, &e);
|
||||
}
|
||||
else if (format == Format::BinaryBe)
|
||||
{
|
||||
IBeStream in(stream);
|
||||
Everything e = Utils::extractDim(in, m_type);
|
||||
point.setField(m_dim, m_type, &e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Right now we don't support list properties for point data. We just
|
||||
// read the data and throw it away.
|
||||
void FloatPlyReader::ListProperty::read(std::istream *stream,
|
||||
FloatPlyReader::Format format, PointRef& point)
|
||||
{
|
||||
if (format == Format::Ascii)
|
||||
{
|
||||
size_t cnt;
|
||||
*stream >> cnt;
|
||||
|
||||
double d;
|
||||
while (cnt--)
|
||||
*stream >> d;
|
||||
}
|
||||
else if (format == Format::BinaryLe)
|
||||
{
|
||||
ILeStream istream(stream);
|
||||
Everything e = Utils::extractDim(istream, m_countType);
|
||||
size_t cnt = (size_t)Utils::toDouble(e, m_countType);
|
||||
cnt *= Dimension::size(m_listType);
|
||||
istream.seek(cnt, std::ios_base::cur);
|
||||
}
|
||||
else if (format == Format::BinaryBe)
|
||||
{
|
||||
IBeStream istream(stream);
|
||||
Everything e = Utils::extractDim(istream, m_countType);
|
||||
size_t cnt = (size_t)Utils::toDouble(e, m_countType);
|
||||
cnt *= Dimension::size(m_listType);
|
||||
istream.seek(cnt, std::ios_base::cur);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::readElement(Element& elt, PointRef& point)
|
||||
{
|
||||
for (auto& prop : elt.m_properties)
|
||||
if (!readProperty(prop.get(), point))
|
||||
throwError("Error reading data for point/element " +
|
||||
std::to_string(point.pointId()) + ".");
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::ready(PointTableRef table)
|
||||
{
|
||||
m_stream = Utils::openFile(m_filename, true);
|
||||
if (m_stream)
|
||||
m_stream->seekg(m_dataPos);
|
||||
for (Element& elt : m_elements)
|
||||
{
|
||||
if (&elt == m_vertexElt)
|
||||
break;
|
||||
|
||||
// We read an element into point 0. Since the element's properties
|
||||
// weren't registered as dimensions, we'll try to write the data
|
||||
// to a NULL dimension, which is a noop.
|
||||
// This essentially just gets us to the vertex element.
|
||||
// In binary mode, this is all silliness, since we should be able
|
||||
// to seek right where we want to go, but in text mode, you've got
|
||||
// to go through the data.
|
||||
PointRef point(table, 0);
|
||||
for (PointId idx = 0; idx < elt.m_count; ++idx)
|
||||
readElement(elt, point);
|
||||
}
|
||||
m_index = 0;
|
||||
}
|
||||
|
||||
|
||||
bool FloatPlyReader::processOne(PointRef& point)
|
||||
{
|
||||
if (m_index < m_vertexElt->m_count)
|
||||
{
|
||||
readElement(*m_vertexElt, point);
|
||||
m_index++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// We're just reading the vertex element here.
|
||||
point_count_t FloatPlyReader::read(PointViewPtr view, point_count_t num)
|
||||
{
|
||||
point_count_t cnt(0);
|
||||
|
||||
PointRef point(view->point(0));
|
||||
for (PointId idx = 0; idx < m_vertexElt->m_count && idx < num; ++idx)
|
||||
{
|
||||
point.setPointId(idx);
|
||||
processOne(point);
|
||||
cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
|
||||
void FloatPlyReader::done(PointTableRef table)
|
||||
{
|
||||
Utils::closeFile(m_stream);
|
||||
}
|
||||
|
||||
} // namespace pdal
|
|
@ -0,0 +1,156 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2015, Peter J. Gadomski <pete.gadomski@gmail.com>
|
||||
*
|
||||
* 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 Hobu, Inc. or Flaxen Geo Consulting 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.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <pdal/io/PlyReader.hpp>
|
||||
#include <pdal/PointTable.hpp>
|
||||
#include <pdal/PointView.hpp>
|
||||
#include <pdal/Options.hpp>
|
||||
#include <pdal/Filter.hpp>
|
||||
|
||||
#include <stack>
|
||||
|
||||
#include <pdal/Dimension.hpp>
|
||||
#include <pdal/Reader.hpp>
|
||||
#include <pdal/StageFactory.hpp>
|
||||
|
||||
namespace pdal
|
||||
{
|
||||
|
||||
class PDAL_DLL FloatPlyReader : public Reader
|
||||
{
|
||||
public:
|
||||
std::string getName() const;
|
||||
|
||||
typedef std::map<std::string, Dimension::Id> DimensionMap;
|
||||
|
||||
FloatPlyReader();
|
||||
|
||||
private:
|
||||
enum class Format
|
||||
{
|
||||
Ascii,
|
||||
BinaryLe,
|
||||
BinaryBe
|
||||
};
|
||||
|
||||
struct Property
|
||||
{
|
||||
Property(const std::string& name) : m_name(name)
|
||||
{}
|
||||
virtual ~Property()
|
||||
{}
|
||||
|
||||
std::string m_name;
|
||||
|
||||
virtual void setDim(Dimension::Id id)
|
||||
{}
|
||||
virtual void read(std::istream *stream, FloatPlyReader::Format format,
|
||||
PointRef& point) = 0;
|
||||
};
|
||||
|
||||
struct SimpleProperty : public Property
|
||||
{
|
||||
SimpleProperty(const std::string& name, Dimension::Type type) :
|
||||
Property(name), m_type(type), m_dim(Dimension::Id::Unknown)
|
||||
{}
|
||||
|
||||
Dimension::Type m_type;
|
||||
Dimension::Id m_dim;
|
||||
|
||||
virtual void read(std::istream *stream, FloatPlyReader::Format format,
|
||||
PointRef& point) override;
|
||||
virtual void setDim(Dimension::Id id) override
|
||||
{ m_dim = id; }
|
||||
};
|
||||
|
||||
struct ListProperty : public Property
|
||||
{
|
||||
ListProperty(const std::string& name, Dimension::Type countType,
|
||||
Dimension::Type listType) : Property(name), m_countType(countType),
|
||||
m_listType(listType)
|
||||
{}
|
||||
|
||||
Dimension::Type m_countType;
|
||||
Dimension::Type m_listType;
|
||||
|
||||
virtual void read(std::istream *stream, FloatPlyReader::Format format,
|
||||
PointRef& point) override;
|
||||
};
|
||||
|
||||
struct Element
|
||||
{
|
||||
Element(const std::string name, size_t count) :
|
||||
m_name(name), m_count(count)
|
||||
{}
|
||||
|
||||
std::string m_name;
|
||||
size_t m_count;
|
||||
std::vector<std::unique_ptr<Property>> m_properties;
|
||||
};
|
||||
|
||||
Format m_format;
|
||||
std::string m_line;
|
||||
std::string::size_type m_linePos;
|
||||
std::stack<std::string> m_lines;
|
||||
std::istream *m_stream;
|
||||
std::istream::streampos m_dataPos;
|
||||
std::vector<Element> m_elements;
|
||||
PointId m_index;
|
||||
Element *m_vertexElt;
|
||||
|
||||
virtual void initialize();
|
||||
virtual void addDimensions(PointLayoutPtr layout);
|
||||
virtual void ready(PointTableRef table);
|
||||
virtual point_count_t read(PointViewPtr view, point_count_t num);
|
||||
virtual void done(PointTableRef table);
|
||||
virtual bool processOne(PointRef& point);
|
||||
|
||||
std::string readLine();
|
||||
void pushLine();
|
||||
std::string nextWord();
|
||||
void extractMagic();
|
||||
void extractEnd();
|
||||
void extractFormat();
|
||||
Dimension::Type getType(const std::string& name);
|
||||
void extractProperty(Element& element);
|
||||
void extractProperties(Element& element);
|
||||
bool extractElement();
|
||||
void extractHeader();
|
||||
void readElement(Element& elt, PointRef& point);
|
||||
bool readProperty(Property *prop, PointRef& point);
|
||||
};
|
||||
|
||||
} // namespace pdal
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include "CmdLineParser.h"
|
||||
|
||||
struct Logger{
|
||||
bool verbose;
|
||||
const char* outputFile;
|
||||
|
||||
Logger(){
|
||||
this->verbose = false;
|
||||
this->outputFile = NULL;
|
||||
}
|
||||
|
||||
void operator() ( 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( verbose )
|
||||
{
|
||||
va_list args;
|
||||
va_start( args , format );
|
||||
vprintf( format , args );
|
||||
va_end( args );
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,276 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2015, Peter J. Gadomski <pete.gadomski@gmail.com>
|
||||
*
|
||||
* 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 Hobu, Inc. or Flaxen Geo Consulting 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.
|
||||
****************************************************************************/
|
||||
|
||||
// Modified to output certain property names in normalized format ("nx", "ny", ... instead of "normalx", "normaly", etc.)
|
||||
|
||||
#include "ModifiedPlyWriter.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
#include <pdal/util/OStream.hpp>
|
||||
#include <pdal/util/ProgramArgs.hpp>
|
||||
|
||||
namespace pdal
|
||||
{
|
||||
|
||||
std::string ModifiedPlyWriter::getName() const { return "ModifiedPlyWriter"; }
|
||||
|
||||
|
||||
ModifiedPlyWriter::ModifiedPlyWriter()
|
||||
{}
|
||||
|
||||
|
||||
void ModifiedPlyWriter::addArgs(ProgramArgs& args)
|
||||
{
|
||||
args.add("filename", "Output filename", m_filename).setPositional();
|
||||
args.add("storage_mode", "PLY Storage Mode", m_format, Format::Ascii);
|
||||
args.add("dims", "Dimension names", m_dimNames);
|
||||
args.add("faces", "Write faces", m_faces);
|
||||
m_precisionArg = &args.add("precision", "Output precision", m_precision, 3);
|
||||
}
|
||||
|
||||
|
||||
void ModifiedPlyWriter::prepared(PointTableRef table)
|
||||
{
|
||||
if (m_precisionArg->set() && m_format != Format::Ascii)
|
||||
throwError("Option 'precision' can only be set of the 'storage_mode' "
|
||||
"is ascii.");
|
||||
if (m_dimNames.size())
|
||||
{
|
||||
for (auto& name : m_dimNames)
|
||||
{
|
||||
auto id = table.layout()->findDim(name);
|
||||
if (id == Dimension::Id::Unknown)
|
||||
throwError("Unknown dimension '" + name + "' in provided "
|
||||
"dimension list.");
|
||||
m_dims.push_back(id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dims = table.layout()->dims();
|
||||
for (auto dim : m_dims)
|
||||
m_dimNames.push_back(Utils::tolower(table.layout()->dimName(dim)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string ModifiedPlyWriter::getType(Dimension::Type type) const
|
||||
{
|
||||
static std::map<Dimension::Type, std::string> types =
|
||||
{
|
||||
{ Dimension::Type::Signed8, "char" },
|
||||
{ Dimension::Type::Unsigned8, "uchar" },
|
||||
{ Dimension::Type::Signed16, "short" },
|
||||
{ Dimension::Type::Unsigned16, "ushort" },
|
||||
{ Dimension::Type::Signed32, "int" },
|
||||
{ Dimension::Type::Unsigned32, "uint" },
|
||||
{ Dimension::Type::Float, "float" },
|
||||
{ Dimension::Type::Double, "double" }
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
return types.at(type);
|
||||
}
|
||||
catch (std::out_of_range&)
|
||||
{
|
||||
throwError("Can't write dimension of type '" +
|
||||
Dimension::interpretationName(type) + "'.");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
void ModifiedPlyWriter::writeHeader(PointLayoutPtr layout) const
|
||||
{
|
||||
*m_stream << "ply" << std::endl;
|
||||
*m_stream << "format " << m_format << " 1.0" << std::endl;
|
||||
*m_stream << "comment Generated by odm_filterpoints" << std::endl;
|
||||
*m_stream << "element vertex " << pointCount() << std::endl;
|
||||
|
||||
auto ni = m_dimNames.begin();
|
||||
for (auto dim : m_dims)
|
||||
{
|
||||
std::string name = *ni++;
|
||||
std::string typeString = getType(layout->dimType(dim));
|
||||
|
||||
// Normalize certain property names
|
||||
if (name == "normalx" || name == "normal_x") name = "nx";
|
||||
if (name == "normaly" || name == "normal_y") name = "ny";
|
||||
if (name == "normalz" || name == "normal_z") name = "nz";
|
||||
|
||||
if (name == "diffuse_red") name = "red";
|
||||
if (name == "diffuse_green") name = "green";
|
||||
if (name == "diffuse_blue") name = "blue";
|
||||
|
||||
*m_stream << "property " << typeString << " " << name << std::endl;
|
||||
}
|
||||
if (m_faces)
|
||||
{
|
||||
*m_stream << "element face " << faceCount() << std::endl;
|
||||
*m_stream << "property list uchar uint vertex_indices" << std::endl;
|
||||
}
|
||||
*m_stream << "end_header" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
void ModifiedPlyWriter::ready(PointTableRef table)
|
||||
{
|
||||
if (pointCount() > (std::numeric_limits<uint32_t>::max)())
|
||||
throwError("Can't write PLY file. Only " +
|
||||
std::to_string((std::numeric_limits<uint32_t>::max)()) +
|
||||
" points supported.");
|
||||
|
||||
m_stream = Utils::createFile(m_filename, true);
|
||||
if (m_format == Format::Ascii && m_precisionArg->set())
|
||||
{
|
||||
*m_stream << std::fixed;
|
||||
m_stream->precision(m_precision);
|
||||
}
|
||||
writeHeader(table.layout());
|
||||
}
|
||||
|
||||
|
||||
void ModifiedPlyWriter::write(const PointViewPtr data)
|
||||
{
|
||||
m_views.push_back(data);
|
||||
}
|
||||
|
||||
|
||||
void ModifiedPlyWriter::writeValue(PointRef& point, Dimension::Id dim,
|
||||
Dimension::Type type)
|
||||
{
|
||||
if (m_format == Format::Ascii)
|
||||
{
|
||||
double d = point.getFieldAs<double>(dim);
|
||||
*m_stream << d;
|
||||
}
|
||||
else if (m_format == Format::BinaryLe)
|
||||
{
|
||||
OLeStream out(m_stream);
|
||||
Everything e;
|
||||
point.getField((char *)&e, dim, type);
|
||||
Utils::insertDim(out, type, e);
|
||||
}
|
||||
else if (m_format == Format::BinaryBe)
|
||||
{
|
||||
OBeStream out(m_stream);
|
||||
Everything e;
|
||||
point.getField((char *)&e, dim, type);
|
||||
Utils::insertDim(out, type, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ModifiedPlyWriter::writePoint(PointRef& point, PointLayoutPtr layout)
|
||||
{
|
||||
for (auto it = m_dims.begin(); it != m_dims.end();)
|
||||
{
|
||||
Dimension::Id dim = *it;
|
||||
writeValue(point, dim, layout->dimType(dim));
|
||||
++it;
|
||||
if (m_format == Format::Ascii && it != m_dims.end())
|
||||
*m_stream << " ";
|
||||
}
|
||||
if (m_format == Format::Ascii)
|
||||
*m_stream << std::endl;
|
||||
}
|
||||
|
||||
|
||||
void ModifiedPlyWriter::writeTriangle(const Triangle& t, size_t offset)
|
||||
{
|
||||
if (m_format == Format::Ascii)
|
||||
{
|
||||
*m_stream << "3 " << (t.m_a + offset) << " " <<
|
||||
(t.m_b + offset) << " " << (t.m_c + offset) << std::endl;
|
||||
}
|
||||
else if (m_format == Format::BinaryLe)
|
||||
{
|
||||
OLeStream out(m_stream);
|
||||
unsigned char count = 3;
|
||||
uint32_t a = (uint32_t)(t.m_a + offset);
|
||||
uint32_t b = (uint32_t)(t.m_b + offset);
|
||||
uint32_t c = (uint32_t)(t.m_c + offset);
|
||||
out << count << a << b << c;
|
||||
}
|
||||
else if (m_format == Format::BinaryBe)
|
||||
{
|
||||
OBeStream out(m_stream);
|
||||
unsigned char count = 3;
|
||||
uint32_t a = (uint32_t)(t.m_a + offset);
|
||||
uint32_t b = (uint32_t)(t.m_b + offset);
|
||||
uint32_t c = (uint32_t)(t.m_c + offset);
|
||||
out << count << a << b << c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Deferring write until this time allows both points and faces from multiple
|
||||
// point views to be written.
|
||||
void ModifiedPlyWriter::done(PointTableRef table)
|
||||
{
|
||||
for (auto& v : m_views)
|
||||
{
|
||||
PointRef point(*v, 0);
|
||||
for (PointId idx = 0; idx < v->size(); ++idx)
|
||||
{
|
||||
point.setPointId(idx);
|
||||
writePoint(point, table.layout());
|
||||
}
|
||||
}
|
||||
if (m_faces)
|
||||
{
|
||||
PointId offset = 0;
|
||||
for (auto& v : m_views)
|
||||
{
|
||||
TriangularMesh *mesh = v->mesh();
|
||||
if (mesh)
|
||||
{
|
||||
for (size_t id = 0; id < mesh->size(); ++id)
|
||||
{
|
||||
const Triangle& t = (*mesh)[id];
|
||||
writeTriangle(t, offset);
|
||||
}
|
||||
}
|
||||
offset += v->size();
|
||||
}
|
||||
}
|
||||
Utils::closeFile(m_stream);
|
||||
m_stream = nullptr;
|
||||
getMetadata().addList("filename", m_filename);
|
||||
}
|
||||
|
||||
} // namespace pdal
|
|
@ -0,0 +1,116 @@
|
|||
/******************************************************************************
|
||||
* Copyright (c) 2015, Peter J. Gadomski <pete.gadomski@gmail.com>
|
||||
*
|
||||
* 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 Hobu, Inc. or Flaxen Geo Consulting 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.
|
||||
****************************************************************************/
|
||||
|
||||
#include <pdal/PointView.hpp>
|
||||
#include <pdal/Writer.hpp>
|
||||
|
||||
namespace pdal
|
||||
{
|
||||
|
||||
class Triangle;
|
||||
|
||||
class PDAL_DLL ModifiedPlyWriter : public Writer
|
||||
{
|
||||
public:
|
||||
enum class Format
|
||||
{
|
||||
Ascii,
|
||||
BinaryLe,
|
||||
BinaryBe
|
||||
};
|
||||
|
||||
std::string getName() const;
|
||||
|
||||
ModifiedPlyWriter();
|
||||
|
||||
private:
|
||||
virtual void addArgs(ProgramArgs& args);
|
||||
virtual void prepared(PointTableRef table);
|
||||
virtual void ready(PointTableRef table);
|
||||
virtual void write(const PointViewPtr data);
|
||||
virtual void done(PointTableRef table);
|
||||
|
||||
std::string getType(Dimension::Type type) const;
|
||||
void writeHeader(PointLayoutPtr layout) const;
|
||||
void writeValue(PointRef& point, Dimension::Id dim, Dimension::Type type);
|
||||
void writePoint(PointRef& point, PointLayoutPtr layout);
|
||||
void writeTriangle(const Triangle& t, size_t offset);
|
||||
|
||||
std::ostream *m_stream;
|
||||
std::string m_filename;
|
||||
Format m_format;
|
||||
bool m_faces;
|
||||
StringList m_dimNames;
|
||||
Dimension::IdList m_dims;
|
||||
int m_precision;
|
||||
Arg *m_precisionArg;
|
||||
std::vector<PointViewPtr> m_views;
|
||||
};
|
||||
|
||||
inline std::istream& operator>>(std::istream& in, ModifiedPlyWriter::Format& f)
|
||||
{
|
||||
std::string s;
|
||||
std::getline(in, s);
|
||||
Utils::trim(s);
|
||||
Utils::tolower(s);
|
||||
if (s == "ascii" || s == "default")
|
||||
f = ModifiedPlyWriter::Format::Ascii;
|
||||
else if (s == "little endian" || s == "binary_little_endian")
|
||||
f = ModifiedPlyWriter::Format::BinaryLe;
|
||||
else if (s == "big endian" || s == "binary_big_endian")
|
||||
f = ModifiedPlyWriter::Format::BinaryBe;
|
||||
else
|
||||
in.setstate(std::ios_base::failbit);
|
||||
return in;
|
||||
}
|
||||
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, const ModifiedPlyWriter::Format& f)
|
||||
{
|
||||
switch (f)
|
||||
{
|
||||
case ModifiedPlyWriter::Format::Ascii:
|
||||
out << "ascii";
|
||||
break;
|
||||
case ModifiedPlyWriter::Format::BinaryLe:
|
||||
out << "binary_little_endian";
|
||||
break;
|
||||
case ModifiedPlyWriter::Format::BinaryBe:
|
||||
out << "binary_big_endian";
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
#include <iostream>
|
||||
#include <pdal/filters/OutlierFilter.hpp>
|
||||
#include <pdal/filters/RangeFilter.hpp>
|
||||
#include "CmdLineParser.h"
|
||||
#include "Logger.h"
|
||||
#include "FloatPlyReader.hpp"
|
||||
#include "ModifiedPlyWriter.hpp"
|
||||
|
||||
Logger logWriter;
|
||||
|
||||
cmdLineParameter< char* >
|
||||
InputFile( "inputFile" ) ,
|
||||
OutputFile( "outputFile" );
|
||||
cmdLineParameter< float >
|
||||
StandardDeviation( "sd" ) ,
|
||||
MeanK ( "meank" );
|
||||
cmdLineReadable
|
||||
Verbose( "verbose" );
|
||||
|
||||
cmdLineReadable* params[] = {
|
||||
&InputFile , &OutputFile , &StandardDeviation, &MeanK, &Verbose ,
|
||||
NULL
|
||||
};
|
||||
|
||||
void help(char *ex){
|
||||
std::cout << "Usage: " << ex << std::endl
|
||||
<< "\t -" << InputFile.name << " <input PLY point cloud>" << std::endl
|
||||
<< "\t -" << OutputFile.name << " <output PLY point cloud>" << std::endl
|
||||
<< "\t [-" << StandardDeviation.name << " <standard deviation threshold>]" << std::endl
|
||||
<< "\t [-" << MeanK.name << " <mean number of neighbors >]" << std::endl
|
||||
|
||||
<< "\t [-" << Verbose.name << "]" << std::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
void logArgs(cmdLineReadable* params[], Logger& logWriter){
|
||||
logWriter("Running with parameters:\n");
|
||||
char str[1024];
|
||||
for( int i=0 ; params[i] ; i++ ){
|
||||
if( params[i]->set ){
|
||||
params[i]->writeValue( str );
|
||||
if( strlen( str ) ) logWriter( "\t--%s %s\n" , params[i]->name , str );
|
||||
else logWriter( "\t--%s\n" , params[i]->name );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
cmdLineParse( argc-1 , &argv[1] , params );
|
||||
if( !InputFile.set || !OutputFile.set ) help(argv[0]);
|
||||
if( !StandardDeviation.set ) StandardDeviation.value = 2.0;
|
||||
if( !MeanK.set ) MeanK.value = 8;
|
||||
|
||||
logWriter.verbose = Verbose.set;
|
||||
logArgs(params, logWriter);
|
||||
|
||||
logWriter("Filtering point cloud...\n");
|
||||
|
||||
pdal::Options inPlyOpts;
|
||||
inPlyOpts.add("filename", InputFile.value);
|
||||
|
||||
pdal::PointTable table;
|
||||
pdal::FloatPlyReader plyReader;
|
||||
plyReader.setOptions(inPlyOpts);
|
||||
|
||||
pdal::Options outlierOpts;
|
||||
outlierOpts.add("method", "statistical");
|
||||
outlierOpts.add("mean_k", MeanK.value);
|
||||
outlierOpts.add("multiplier", StandardDeviation.value);
|
||||
|
||||
pdal::OutlierFilter outlierFilter;
|
||||
outlierFilter.setInput(plyReader);
|
||||
outlierFilter.setOptions(outlierOpts);
|
||||
|
||||
pdal::Options rangeOpts;
|
||||
rangeOpts.add("limits", "Classification![7:7]"); // Remove outliers
|
||||
|
||||
pdal::RangeFilter rangeFilter;
|
||||
rangeFilter.setInput(outlierFilter);
|
||||
rangeFilter.setOptions(rangeOpts);
|
||||
|
||||
pdal::Options outPlyOpts;
|
||||
outPlyOpts.add("storage_mode", "little endian");
|
||||
outPlyOpts.add("filename", OutputFile.value);
|
||||
|
||||
pdal::ModifiedPlyWriter plyWriter;
|
||||
plyWriter.setOptions(outPlyOpts);
|
||||
plyWriter.setInput(rangeFilter);
|
||||
plyWriter.prepare(table);
|
||||
plyWriter.execute(table);
|
||||
|
||||
logWriter("Done!\n");
|
||||
}
|
|
@ -297,13 +297,10 @@ def config():
|
|||
'Default: %(default)s'))
|
||||
|
||||
parser.add_argument('--pc-classify',
|
||||
metavar='<string>',
|
||||
default='none',
|
||||
choices=['none', 'smrf', 'pmf'],
|
||||
help='Classify the point cloud outputs using either '
|
||||
'a Simple Morphological Filter or a Progressive Morphological Filter. '
|
||||
'If --dtm is set this parameter defaults to smrf. '
|
||||
'You can control the behavior of both smrf and pmf by tweaking the --dem-* parameters. '
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Classify the point cloud outputs using a Simple Morphological Filter. '
|
||||
'You can control the behavior of this option by tweaking the --dem-* parameters. '
|
||||
'Default: '
|
||||
'%(default)s')
|
||||
|
||||
|
@ -317,6 +314,14 @@ def config():
|
|||
default=False,
|
||||
help='Export the georeferenced point cloud in LAS format. Default: %(default)s')
|
||||
|
||||
parser.add_argument('--pc-filter',
|
||||
metavar='<positive float>',
|
||||
type=float,
|
||||
default=2.5,
|
||||
help='Filters the point cloud by removing points that deviate more than N standard deviations from the local mean. Set to 0 to disable filtering.'
|
||||
'\nDefault: '
|
||||
'%(default)s')
|
||||
|
||||
parser.add_argument('--texturing-data-term',
|
||||
metavar='<string>',
|
||||
default='gmi',
|
||||
|
@ -423,31 +428,6 @@ def config():
|
|||
help='DSM/DTM resolution in cm / pixel.'
|
||||
'\nDefault: %(default)s')
|
||||
|
||||
parser.add_argument('--dem-maxsd',
|
||||
metavar='<positive float>',
|
||||
type=float,
|
||||
default=2.5,
|
||||
help='Points that deviate more than maxsd standard deviations from the local mean '
|
||||
'are discarded. \nDefault: '
|
||||
'%(default)s')
|
||||
|
||||
parser.add_argument('--dem-initial-distance',
|
||||
metavar='<positive float>',
|
||||
type=float,
|
||||
default=0.15,
|
||||
help='Used to classify ground vs non-ground points. Set this value to account for Z noise in meters. '
|
||||
'If you have an uncertainty of around 15 cm, set this value large enough to not exclude these points. '
|
||||
'Too small of a value will exclude valid ground points, while too large of a value will misclassify non-ground points for ground ones. '
|
||||
'\nDefault: '
|
||||
'%(default)s')
|
||||
|
||||
parser.add_argument('--dem-approximate',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='Use this tag use the approximate progressive '
|
||||
'morphological filter, which computes DEMs faster '
|
||||
'but is not as accurate.')
|
||||
|
||||
parser.add_argument('--dem-decimation',
|
||||
metavar='<positive integer>',
|
||||
default=1,
|
||||
|
@ -456,17 +436,6 @@ def config():
|
|||
'100 decimates ~99%% of the points. Useful for speeding up '
|
||||
'generation.\nDefault=%(default)s')
|
||||
|
||||
parser.add_argument('--dem-terrain-type',
|
||||
metavar='<string>',
|
||||
choices=['FlatNonForest', 'FlatForest', 'ComplexNonForest', 'ComplexForest'],
|
||||
default='ComplexForest',
|
||||
help='One of: %(choices)s. Specifies the type of terrain. This mainly helps reduce processing time. '
|
||||
'\nFlatNonForest: Relatively flat region with little to no vegetation'
|
||||
'\nFlatForest: Relatively flat region that is forested'
|
||||
'\nComplexNonForest: Varied terrain with little to no vegetation'
|
||||
'\nComplexForest: Varied terrain that is forested'
|
||||
'\nDefault=%(default)s')
|
||||
|
||||
parser.add_argument('--orthophoto-resolution',
|
||||
metavar='<float > 0.0>',
|
||||
default=5,
|
||||
|
@ -548,9 +517,9 @@ def config():
|
|||
log.ODM_INFO('Fast orthophoto is turned on, automatically setting --skip-3dmodel')
|
||||
args.skip_3dmodel = True
|
||||
|
||||
if args.dtm and args.pc_classify == 'none':
|
||||
if args.dtm and not args.pc_classify:
|
||||
log.ODM_INFO("DTM is turned on, automatically turning on point cloud classification")
|
||||
args.pc_classify = "smrf"
|
||||
args.pc_classify = True
|
||||
|
||||
if args.skip_3dmodel and args.use_3dmesh:
|
||||
log.ODM_WARNING('--skip-3dmodel is set, but so is --use-3dmesh. --use_3dmesh will be ignored.')
|
||||
|
|
|
@ -9,15 +9,11 @@ from functools import partial
|
|||
|
||||
from . import pdal
|
||||
|
||||
def classify(lasFile, smrf=False, slope=1, cellsize=3, maxWindowSize=10, maxDistance=1,
|
||||
approximate=False, initialDistance=0.7, verbose=False):
|
||||
def classify(lasFile, slope=0.15, cellsize=1, maxWindowSize=18, verbose=False):
|
||||
start = datetime.now()
|
||||
|
||||
try:
|
||||
if smrf:
|
||||
pdal.run_pdaltranslate_smrf(lasFile, lasFile, slope, cellsize, maxWindowSize, verbose)
|
||||
else:
|
||||
pdal.run_pdalground(lasFile, lasFile, slope, cellsize, maxWindowSize, maxDistance, approximate=approximate, initialDistance=initialDistance, verbose=verbose)
|
||||
pdal.run_pdaltranslate_smrf(lasFile, lasFile, slope, cellsize, maxWindowSize, verbose)
|
||||
except:
|
||||
raise Exception("Error creating classified file %s" % fout)
|
||||
|
||||
|
@ -60,7 +56,6 @@ def create_dems(filenames, demtype, radius=['0.56'], gapfill=False,
|
|||
|
||||
|
||||
def create_dem(filenames, demtype, radius, decimation=None,
|
||||
maxsd=None, maxz=None,
|
||||
products=['idw'], outdir='', suffix='', verbose=False, resolution=0.1):
|
||||
""" Create DEM from collection of LAS files """
|
||||
start = datetime.now()
|
||||
|
@ -75,10 +70,6 @@ def create_dem(filenames, demtype, radius, decimation=None,
|
|||
# JSON pipeline
|
||||
json = pdal.json_gdal_base(bname, products, radius, resolution)
|
||||
|
||||
# A DSM for meshing does not use additional filters
|
||||
if demtype != 'mesh_dsm':
|
||||
json = pdal.json_add_filters(json, maxsd, maxz)
|
||||
|
||||
if demtype == 'dsm':
|
||||
json = pdal.json_add_classification_filter(json, 2, equality='max')
|
||||
elif demtype == 'dtm':
|
||||
|
|
|
@ -102,75 +102,6 @@ def json_add_classification_filter(json, classification, equality="equals"):
|
|||
return json
|
||||
|
||||
|
||||
def json_add_maxsd_filter(json, meank=20, thresh=3.0):
|
||||
""" Add outlier Filter element and return """
|
||||
json['pipeline'].insert(0, {
|
||||
'type': 'filters.outlier',
|
||||
'method': 'statistical',
|
||||
'mean_k': meank,
|
||||
'multiplier': thresh
|
||||
})
|
||||
return json
|
||||
|
||||
|
||||
def json_add_maxz_filter(json, maxz):
|
||||
""" Add max elevation Filter element and return """
|
||||
json['pipeline'].insert(0, {
|
||||
'type': 'filters.range',
|
||||
'limits': 'Z[:{0}]'.format(maxz)
|
||||
})
|
||||
|
||||
return json
|
||||
|
||||
|
||||
def json_add_maxangle_filter(json, maxabsangle):
|
||||
""" Add scan angle Filter element and return """
|
||||
json['pipeline'].insert(0, {
|
||||
'type': 'filters.range',
|
||||
'limits': 'ScanAngleRank[{0}:{1}]'.format(str(-float(maxabsangle)), maxabsangle)
|
||||
})
|
||||
return json
|
||||
|
||||
|
||||
def json_add_scanedge_filter(json, value):
|
||||
""" Add EdgeOfFlightLine Filter element and return """
|
||||
json['pipeline'].insert(0, {
|
||||
'type': 'filters.range',
|
||||
'limits': 'EdgeOfFlightLine[{0}:{0}]'.format(value)
|
||||
})
|
||||
return json
|
||||
|
||||
|
||||
def json_add_returnnum_filter(json, value):
|
||||
""" Add ReturnNum Filter element and return """
|
||||
json['pipeline'].insert(0, {
|
||||
'type': 'filters.range',
|
||||
'limits': 'ReturnNum[{0}:{0}]'.format(value)
|
||||
})
|
||||
return json
|
||||
|
||||
|
||||
def json_add_filters(json, maxsd=None, maxz=None, maxangle=None, returnnum=None):
|
||||
if maxsd is not None:
|
||||
json = json_add_maxsd_filter(json, thresh=maxsd)
|
||||
if maxz is not None:
|
||||
json = json_add_maxz_filter(json, maxz)
|
||||
if maxangle is not None:
|
||||
json = json_add_maxangle_filter(json, maxangle)
|
||||
if returnnum is not None:
|
||||
json = json_add_returnnum_filter(json, returnnum)
|
||||
return json
|
||||
|
||||
|
||||
def json_add_crop_filter(json, wkt):
|
||||
""" Add cropping polygon as Filter Element and return """
|
||||
json['pipeline'].insert(0, {
|
||||
'type': 'filters.crop',
|
||||
'polygon': wkt
|
||||
})
|
||||
return json
|
||||
|
||||
|
||||
def is_ply_file(filename):
|
||||
_, ext = os.path.splitext(filename)
|
||||
return ext.lower() == '.ply'
|
||||
|
@ -233,33 +164,6 @@ def run_pipeline(json, verbose=False):
|
|||
os.remove(jsonfile)
|
||||
|
||||
|
||||
def run_pdalground(fin, fout, slope, cellsize, maxWindowSize, maxDistance, approximate=False, initialDistance=0.7, verbose=False):
|
||||
""" Run PDAL ground """
|
||||
cmd = [
|
||||
'pdal',
|
||||
'ground',
|
||||
'-i %s' % fin,
|
||||
'-o %s' % fout,
|
||||
'--slope %s' % slope,
|
||||
'--cell_size %s' % cellsize,
|
||||
'--initial_distance %s' % initialDistance
|
||||
]
|
||||
if maxWindowSize is not None:
|
||||
cmd.append('--max_window_size %s' %maxWindowSize)
|
||||
if maxDistance is not None:
|
||||
cmd.append('--max_distance %s' %maxDistance)
|
||||
|
||||
if approximate:
|
||||
cmd.append('--approximate')
|
||||
|
||||
if verbose:
|
||||
cmd.append('--developer-debug')
|
||||
print ' '.join(cmd)
|
||||
print ' '.join(cmd)
|
||||
out = system.run(' '.join(cmd))
|
||||
if verbose:
|
||||
print out
|
||||
|
||||
def run_pdaltranslate_smrf(fin, fout, slope, cellsize, maxWindowSize, verbose=False):
|
||||
""" Run PDAL translate """
|
||||
cmd = [
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import os, sys
|
||||
from opendm import system
|
||||
from opendm import log
|
||||
from opendm import context
|
||||
|
||||
def filter(pointCloudPath, standard_deviation=2.5, meank=16, verbose=False):
|
||||
"""
|
||||
Filters a point cloud in place (it will replace the input file with the filtered result).
|
||||
"""
|
||||
if standard_deviation <= 0 or meank <= 0:
|
||||
log.ODM_INFO("Skipping point cloud filtering")
|
||||
return
|
||||
|
||||
log.ODM_INFO("Filtering point cloud (statistical, meanK {}, standard deviation {})".format(meank, standard_deviation))
|
||||
|
||||
if not os.path.exists(pointCloudPath):
|
||||
log.ODM_ERROR("{} does not exist, cannot filter point cloud. The program will now exit.".format(pointCloudPath))
|
||||
sys.exit(1)
|
||||
|
||||
filter_program = os.path.join(context.odm_modules_path, 'odm_filterpoints')
|
||||
if not os.path.exists(filter_program):
|
||||
log.ODM_WARNING("{} program not found. Will skip filtering, but this installation should be fixed.")
|
||||
return
|
||||
|
||||
pc_path, pc_filename = os.path.split(pointCloudPath)
|
||||
# pc_path = path/to
|
||||
# pc_filename = pointcloud.ply
|
||||
|
||||
basename, ext = os.path.splitext(pc_filename)
|
||||
# basename = pointcloud
|
||||
# ext = .ply
|
||||
|
||||
tmpPointCloud = os.path.join(pc_path, "{}.tmp{}".format(basename, ext))
|
||||
|
||||
filterArgs = {
|
||||
'bin': filter_program,
|
||||
'inputFile': pointCloudPath,
|
||||
'outputFile': tmpPointCloud,
|
||||
'sd': standard_deviation,
|
||||
'meank': meank,
|
||||
'verbose': '--verbose' if verbose else '',
|
||||
}
|
||||
|
||||
system.run('{bin} -inputFile {inputFile} '
|
||||
'-outputFile {outputFile} '
|
||||
'-sd {sd} '
|
||||
'-meank {meank} {verbose} '.format(**filterArgs))
|
||||
|
||||
# Remove input file, swap temp file
|
||||
if os.path.exists(tmpPointCloud):
|
||||
os.remove(pointCloudPath)
|
||||
os.rename(tmpPointCloud, pointCloudPath)
|
||||
else:
|
||||
log.ODM_WARNING("{} not found, filtering has failed.".format(tmpPointCloud))
|
|
@ -39,45 +39,33 @@ class ODMDEMCell(ecto.Cell):
|
|||
(args.rerun_from is not None and
|
||||
'odm_dem' in args.rerun_from)
|
||||
|
||||
log.ODM_INFO('Classify: ' + str(args.pc_classify != "none"))
|
||||
log.ODM_INFO('Classify: ' + str(args.pc_classify))
|
||||
log.ODM_INFO('Create DSM: ' + str(args.dsm))
|
||||
log.ODM_INFO('Create DTM: ' + str(args.dtm))
|
||||
log.ODM_INFO('DEM input file {0} found: {1}'.format(tree.odm_georeferencing_model_laz, str(las_model_found)))
|
||||
|
||||
# Setup terrain parameters
|
||||
terrain_params_map = {
|
||||
'flatnonforest': (1, 3),
|
||||
'flatforest': (1, 2),
|
||||
'complexnonforest': (5, 2),
|
||||
'complexforest': (10, 2)
|
||||
}
|
||||
terrain_params = terrain_params_map[args.dem_terrain_type.lower()]
|
||||
slope, cellsize = terrain_params
|
||||
slope, cellsize = (0.15, 1)
|
||||
|
||||
# define paths and create working directories
|
||||
odm_dem_root = tree.path('odm_dem')
|
||||
if not io.dir_exists(odm_dem_root):
|
||||
system.mkdir_p(odm_dem_root)
|
||||
|
||||
if args.pc_classify != "none" and las_model_found:
|
||||
if args.pc_classify and las_model_found:
|
||||
pc_classify_marker = os.path.join(odm_dem_root, 'pc_classify_done.txt')
|
||||
|
||||
if not io.file_exists(pc_classify_marker) or rerun_cell:
|
||||
log.ODM_INFO("Classifying {} using {}".format(tree.odm_georeferencing_model_laz, args.pc_classify))
|
||||
log.ODM_INFO("Classifying {} using Simple Morphological Filter".format(tree.odm_georeferencing_model_laz))
|
||||
commands.classify(tree.odm_georeferencing_model_laz,
|
||||
args.pc_classify == "smrf",
|
||||
slope,
|
||||
cellsize,
|
||||
approximate=args.dem_approximate,
|
||||
initialDistance=args.dem_initial_distance,
|
||||
verbose=args.verbose
|
||||
)
|
||||
|
||||
with open(pc_classify_marker, 'w') as f:
|
||||
f.write('Classify: {}\n'.format(args.pc_classify))
|
||||
f.write('Classify: smrf\n')
|
||||
f.write('Slope: {}\n'.format(slope))
|
||||
f.write('Cellsize: {}\n'.format(cellsize))
|
||||
f.write('Approximate: {}\n'.format(args.dem_approximate))
|
||||
f.write('InitialDistance: {}\n'.format(args.dem_initial_distance))
|
||||
|
||||
# Do we need to process anything here?
|
||||
if (args.dsm or args.dtm) and las_model_found:
|
||||
|
@ -105,7 +93,6 @@ class ODMDEMCell(ecto.Cell):
|
|||
gapfill=True,
|
||||
outdir=odm_dem_root,
|
||||
resolution=resolution / 100.0,
|
||||
maxsd=args.dem_maxsd,
|
||||
decimation=args.dem_decimation,
|
||||
verbose=args.verbose,
|
||||
max_workers=get_max_concurrency_for_dem(args.max_concurrency,tree.odm_georeferencing_model_laz)
|
||||
|
|
|
@ -9,6 +9,7 @@ from opendm import types
|
|||
from opendm import system
|
||||
from opendm import context
|
||||
from opendm.cropper import Cropper
|
||||
from opendm import point_cloud
|
||||
|
||||
|
||||
class ODMGeoreferencingCell(ecto.Cell):
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import ecto
|
||||
import sys
|
||||
import os
|
||||
|
||||
from opendm import log
|
||||
from opendm import io
|
||||
from opendm import system
|
||||
from opendm import context
|
||||
from opendm import gsd
|
||||
from opendm import point_cloud
|
||||
|
||||
class ODMOpenSfMCell(ecto.Cell):
|
||||
def declare_params(self, params):
|
||||
|
@ -170,6 +172,9 @@ class ODMOpenSfMCell(ecto.Cell):
|
|||
if args.fast_orthophoto:
|
||||
system.run('PYTHONPATH=%s %s/bin/opensfm export_ply --no-cameras %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
|
||||
|
||||
# Filter
|
||||
point_cloud.filter(os.path.join(tree.opensfm, 'reconstruction.ply'), standard_deviation=args.pc_filter, verbose=args.verbose)
|
||||
elif args.use_opensfm_dense:
|
||||
# Undistort images at full scale in JPG
|
||||
# (TODO: we could compare the size of the PNGs if they are < than depthmap_resolution
|
||||
|
@ -179,6 +184,8 @@ class ODMOpenSfMCell(ecto.Cell):
|
|||
system.run('PYTHONPATH=%s %s/bin/opensfm compute_depthmaps %s' %
|
||||
(context.pyopencv_path, context.opensfm_path, tree.opensfm))
|
||||
|
||||
# Filter
|
||||
point_cloud.filter(tree.opensfm_model, standard_deviation=args.pc_filter, verbose=args.verbose)
|
||||
else:
|
||||
log.ODM_WARNING('Found a valid OpenSfM reconstruction file in: %s' %
|
||||
tree.opensfm_reconstruction)
|
||||
|
|
|
@ -4,6 +4,7 @@ from opendm import log
|
|||
from opendm import io
|
||||
from opendm import system
|
||||
from opendm import context
|
||||
from opendm import point_cloud
|
||||
|
||||
|
||||
class ODMSmvsCell(ecto.Cell):
|
||||
|
@ -91,6 +92,9 @@ class ODMSmvsCell(ecto.Cell):
|
|||
old_file = smvs_files[-1]
|
||||
if not (io.rename_file(old_file, tree.smvs_model)):
|
||||
log.ODM_WARNING("File %s does not exist, cannot be renamed. " % old_file)
|
||||
|
||||
# Filter
|
||||
point_cloud.filter(tree.smvs_model, standard_deviation=args.pc_filter, verbose=args.verbose)
|
||||
else:
|
||||
log.ODM_WARNING("Cannot find a valid point cloud (smvs-XX.ply) in %s. Check the console output for errors." % tree.smvs)
|
||||
else:
|
||||
|
|
Ładowanie…
Reference in New Issue