Merge pull request #1132 from pierotofy/odmfilterpoints

odm_filterpoints improvements

Former-commit-id: 7947df0787
pull/1161/head v0.1.0.1
Piero Toffanin 2020-07-11 10:45:51 -04:00 zatwierdzone przez GitHub
commit 01b42e3c2b
17 zmienionych plików z 299 dodań i 1629 usunięć

Wyświetl plik

@ -8,15 +8,14 @@ ExternalProject_Add(${_proj_name}
STAMP_DIR ${_SB_BINARY_DIR}/stamp
#--Download step--------------
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}
URL https://github.com/PDAL/PDAL/archive/1.9.1.zip
URL https://github.com/PDAL/PDAL/archive/2.1.0.zip
#--Update/Patch step----------
UPDATE_COMMAND ""
#--Configure step-------------
SOURCE_DIR ${SB_SOURCE_DIR}/${_proj_name}
CMAKE_ARGS
-BUILD_PGPOINTCLOUD_TESTS=OFF
-BUILD_PLUGIN_PCL=ON
-BUILD_PLUGIN_PGPOINTCLOUD=ON
-DBUILD_PGPOINTCLOUD_TESTS=OFF
-DBUILD_PLUGIN_PGPOINTCLOUD=OFF
-DBUILD_PLUGIN_CPD=OFF
-DBUILD_PLUGIN_GREYHOUND=OFF
-DBUILD_PLUGIN_HEXBIN=ON

Wyświetl plik

@ -1 +1 @@
1.0.0
1.0.1

Wyświetl plik

@ -7,7 +7,6 @@ endif()
add_subdirectory(odm_georef)
add_subdirectory(odm_orthophoto)
add_subdirectory(odm_cleanmesh)
add_subdirectory(odm_filterpoints)
if (ODM_BUILD_SLAM)
add_subdirectory(odm_slam)

Wyświetl plik

@ -1,21 +0,0 @@
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})

Wyświetl plik

@ -1,106 +0,0 @@
/*
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

Wyświetl plik

@ -1,300 +0,0 @@
/* -*- 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;
}

Wyświetl plik

@ -1,454 +0,0 @@
/******************************************************************************
* 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

Wyświetl plik

@ -1,156 +0,0 @@
/******************************************************************************
* 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

Wyświetl plik

@ -1,33 +0,0 @@
#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 );
}
}
};

Wyświetl plik

@ -1,276 +0,0 @@
/******************************************************************************
* 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

Wyświetl plik

@ -1,116 +0,0 @@
/******************************************************************************
* 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;
}
}

Wyświetl plik

@ -1,126 +0,0 @@
#include <iostream>
#include <algorithm>
#include <pdal/filters/OutlierFilter.hpp>
#include <pdal/filters/RangeFilter.hpp>
#include <pdal/filters/SampleFilter.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" ) ,
Confidence ( "confidence" ) ,
Sample ( "sample" );
cmdLineReadable
Verbose( "verbose" );
cmdLineReadable* params[] = {
&InputFile , &OutputFile , &StandardDeviation, &MeanK, &Confidence, &Sample, &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 [-" << Confidence.name << " <lower bound filter for confidence property>]" << 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;
pdal::Stage *currentStage = &plyReader;
plyReader.setOptions(inPlyOpts);
pdal::RangeFilter confidenceFilter;
if (Confidence.set){
logWriter("Filtering confidence\n");
pdal::Options confidenceFilterOpts;
float confidenceValue = std::min(1.0f, std::max(Confidence.value, 0.0f));
std::ostringstream confidenceLimit;
confidenceLimit << "confidence[" << confidenceValue << ":1]";
confidenceFilterOpts.add("limits", confidenceLimit.str());
confidenceFilter.setInput(*currentStage);
currentStage = &confidenceFilter;
confidenceFilter.setOptions(confidenceFilterOpts);
}
pdal::SampleFilter sampleFilter;
if (Sample.set && Sample.value > 0.0f){
logWriter("Radius sampling\n");
pdal::Options sampleFilterOpts;
sampleFilterOpts.add("radius", Sample.value);
sampleFilter.setOptions(sampleFilterOpts);
sampleFilter.setInput(*currentStage);
currentStage = &sampleFilter;
}
pdal::Options outlierOpts;
outlierOpts.add("method", "statistical");
outlierOpts.add("mean_k", MeanK.value);
outlierOpts.add("multiplier", StandardDeviation.value);
pdal::OutlierFilter outlierFilter;
outlierFilter.setInput(*currentStage);
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");
}

Wyświetl plik

@ -1,5 +1,12 @@
from psutil import virtual_memory
import os
try:
import Queue as queue
except:
import queue
import threading
import time
from opendm import log
def get_max_memory(minimum = 5, use_at_most = 0.5):
"""
@ -16,3 +23,75 @@ def get_max_memory_mb(minimum = 100, use_at_most = 0.5):
:return value of memory to use in megabytes.
"""
return max(minimum, (virtual_memory().available / 1024 / 1024) * use_at_most)
def parallel_map(func, items, max_workers=1):
"""
Our own implementation for parallel processing
which handles gracefully CTRL+C and reverts to
single thread processing in case of errors
:param items list of objects
:param func function to execute on each object
"""
global error
error = None
def process_one(q):
func(q)
def worker():
global error
while True:
(num, q) = pq.get()
if q is None or error is not None:
pq.task_done()
break
try:
process_one(q)
except Exception as e:
error = e
finally:
pq.task_done()
if max_workers > 1:
use_single_thread = False
pq = queue.PriorityQueue()
threads = []
for i in range(max_workers):
t = threading.Thread(target=worker)
t.start()
threads.append(t)
for t in items:
pq.put((i, t.copy()))
def stop_workers():
for i in range(len(threads)):
pq.put((-1, None))
for t in threads:
t.join()
# block until all tasks are done
try:
while pq.unfinished_tasks > 0:
time.sleep(0.5)
except KeyboardInterrupt:
print("CTRL+C terminating...")
stop_workers()
sys.exit(1)
stop_workers()
if error is not None:
# Try to reprocess using a single thread
# in case this was a memory error
log.ODM_WARNING("Failed to run process in parallel, retrying with a single thread...")
use_single_thread = True
else:
use_single_thread = True
if use_single_thread:
# Boring, single thread processing
for q in items:
process_one(q)

Wyświetl plik

@ -80,6 +80,9 @@ def create_dem(input_point_cloud, dem_type, output_type='max', radiuses=['0.56']
verbose=False, decimation=None, keep_unfilled_copy=False,
apply_smoothing=True):
""" Create DEM from multiple radii, and optionally gapfill """
# TODO: refactor to use concurrency.parallel_map
global error
error = None
@ -166,9 +169,7 @@ def create_dem(input_point_cloud, dem_type, output_type='max', radiuses=['0.56']
d = pdal.json_gdal_base(q['filename'], output_type, q['radius'], resolution, q['bounds'])
if dem_type == 'dsm':
d = pdal.json_add_classification_filter(d, 2, equality='max')
elif dem_type == 'dtm':
if dem_type == 'dtm':
d = pdal.json_add_classification_filter(d, 2)
if decimation is not None:

Wyświetl plik

@ -1,13 +1,70 @@
import os, sys, shutil, tempfile, json
import os, sys, shutil, tempfile, json, math
from opendm import system
from opendm import log
from opendm import context
from opendm.system import run
from opendm import entwine
from opendm import io
from opendm.concurrency import parallel_map
from pipes import quote
def filter(input_point_cloud, output_point_cloud, standard_deviation=2.5, meank=16, confidence=None, sample_radius=0, verbose=False):
def ply_info(input_ply):
if not os.path.exists(input_ply):
return False
# Read PLY header, check if point cloud has normals
has_normals = False
vertex_count = 0
with open(input_ply, 'r') as f:
line = f.readline().strip().lower()
i = 0
while line != "end_header":
line = f.readline().strip().lower()
props = line.split(" ")
if len(props) == 3:
if props[0] == "property" and props[2] in ["nx", "normalx", "normal_x"]:
has_normals = True
elif props[0] == "element" and props[1] == "vertex":
vertex_count = int(props[2])
i += 1
if i > 100:
raise IOError("Cannot find end_header field. Invalid PLY?")
return {
'has_normals': has_normals,
'vertex_count': vertex_count,
}
def split(input_point_cloud, outdir, filename_template, capacity, dims=None):
log.ODM_INFO("Splitting point cloud filtering in chunks of {} vertices".format(capacity))
if not os.path.exists(input_point_cloud):
log.ODM_ERROR("{} does not exist, cannot split point cloud. The program will now exit.".format(input_point_cloud))
sys.exit(1)
if not os.path.exists(outdir):
system.mkdir_p(outdir)
if len(os.listdir(outdir)) != 0:
log.ODM_ERROR("%s already contains some files. The program will now exit.".format(outdir))
sys.exit(1)
cmd = 'pdal split -i "%s" -o "%s" --capacity %s ' % (input_point_cloud, os.path.join(outdir, filename_template), capacity)
if filename_template.endswith(".ply"):
cmd += ("--writers.ply.sized_types=false "
"--writers.ply.storage_mode='little endian' ")
if dims is not None:
cmd += '--writers.ply.dims="%s"' % dims
system.run(cmd)
return [os.path.join(outdir, f) for f in os.listdir(outdir)]
def filter(input_point_cloud, output_point_cloud, standard_deviation=2.5, meank=16, sample_radius=0, verbose=False, max_concurrency=1):
"""
Filters a point cloud
"""
@ -21,43 +78,95 @@ def filter(input_point_cloud, output_point_cloud, standard_deviation=2.5, meank=
shutil.copy(input_point_cloud, output_point_cloud)
return
if standard_deviation > 0 and meank > 0:
log.ODM_INFO("Filtering point cloud (statistical, meanK {}, standard deviation {})".format(meank, standard_deviation))
if confidence:
log.ODM_INFO("Keeping only points with > %s confidence" % confidence)
filters = []
if sample_radius > 0:
log.ODM_INFO("Sampling points around a %sm radius" % sample_radius)
filters.append('sample')
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.")
shutil.copy(input_point_cloud, output_point_cloud)
return
if standard_deviation > 0 and meank > 0:
log.ODM_INFO("Filtering {} (statistical, meanK {}, standard deviation {})".format(input_point_cloud, meank, standard_deviation))
filters.append('outlier')
filterArgs = {
'bin': filter_program,
'inputFile': input_point_cloud,
'outputFile': output_point_cloud,
'sd': standard_deviation,
'meank': meank,
'verbose': '-verbose' if verbose else '',
'confidence': '-confidence %s' % confidence if confidence else '',
'sample': max(0, sample_radius)
}
if len(filters) > 0:
filters.append('range')
system.run('{bin} -inputFile {inputFile} '
'-outputFile {outputFile} '
'-sd {sd} '
'-meank {meank} '
'-sample {sample} '
'{confidence} {verbose} '.format(**filterArgs))
info = ply_info(input_point_cloud)
dims = "x=float,y=float,z=float,"
if info['has_normals']:
dims += "nx=float,ny=float,nz=float,"
dims += "red=uchar,blue=uchar,green=uchar"
if info['vertex_count'] == 0:
log.ODM_ERROR("Cannot read vertex count for {}".format(input_point_cloud))
sys.exit(1)
# Do we need to split this?
VERTEX_THRESHOLD = 250000
should_split = max_concurrency > 1 and info['vertex_count'] > VERTEX_THRESHOLD*2
if should_split:
partsdir = os.path.join(os.path.dirname(output_point_cloud), "parts")
if os.path.exists(partsdir):
log.ODM_WARNING("Removing existing directory %s" % partsdir)
shutil.rmtree(partsdir)
point_cloud_submodels = split(input_point_cloud, partsdir, "part.ply", capacity=VERTEX_THRESHOLD, dims=dims)
def run_filter(pcs):
# Recurse
filter(pcs['path'], io.related_file_path(pcs['path'], postfix="_filtered"),
standard_deviation=standard_deviation,
meank=meank,
sample_radius=sample_radius,
verbose=verbose,
max_concurrency=1)
# Filter
parallel_map(run_filter, [{'path': p} for p in point_cloud_submodels], max_concurrency)
# Merge
log.ODM_INFO("Merging %s point cloud chunks to %s" % (len(point_cloud_submodels), output_point_cloud))
filtered_pcs = [io.related_file_path(pcs, postfix="_filtered") for pcs in point_cloud_submodels]
#merge_ply(filtered_pcs, output_point_cloud, dims)
fast_merge_ply(filtered_pcs, output_point_cloud)
if os.path.exists(partsdir):
shutil.rmtree(partsdir)
else:
# Process point cloud (or a point cloud submodel) in a single step
filterArgs = {
'inputFile': input_point_cloud,
'outputFile': output_point_cloud,
'stages': " ".join(filters),
'dims': dims
}
cmd = ("pdal translate -i \"{inputFile}\" "
"-o \"{outputFile}\" "
"{stages} "
"--writers.ply.sized_types=false "
"--writers.ply.storage_mode='little endian' "
"--writers.ply.dims=\"{dims}\" "
"").format(**filterArgs)
if 'sample' in filters:
cmd += "--filters.sample.radius={} ".format(sample_radius)
if 'outlier' in filters:
cmd += ("--filters.outlier.method='statistical' "
"--filters.outlier.mean_k={} "
"--filters.outlier.multiplier={} ").format(meank, standard_deviation)
if 'range' in filters:
# Remove outliers
cmd += "--filters.range.limits='Classification![7:7]' "
system.run(cmd)
# Remove input file, swap temp file
if not os.path.exists(output_point_cloud):
log.ODM_WARNING("{} not found, filtering has failed.".format(output_point_cloud))
def get_extent(input_point_cloud):
fd, json_file = tempfile.mkstemp(suffix='.json')
os.close(fd)
@ -114,7 +223,7 @@ def merge(input_point_cloud_files, output_file, rerun=False):
log.ODM_WARNING("No input point cloud files to process")
return
if rerun and io.file_exists(output_file):
if io.file_exists(output_file):
log.ODM_WARNING("Removing previous point cloud: %s" % output_file)
os.remove(output_file)
@ -126,6 +235,76 @@ def merge(input_point_cloud_files, output_file, rerun=False):
system.run('lasmerge -i {all_inputs} -o "{output}"'.format(**kwargs))
def fast_merge_ply(input_point_cloud_files, output_file):
# Assumes that all input files share the same header/content format
# As the merge is a naive byte stream copy
num_files = len(input_point_cloud_files)
if num_files == 0:
log.ODM_WARNING("No input point cloud files to process")
return
if io.file_exists(output_file):
log.ODM_WARNING("Removing previous point cloud: %s" % output_file)
os.remove(output_file)
vertex_count = sum([ply_info(pcf)['vertex_count'] for pcf in input_point_cloud_files])
master_file = input_point_cloud_files[0]
with open(output_file, "wb") as out:
with open(master_file, "r") as fhead:
# Copy header
line = fhead.readline()
out.write(line)
i = 0
while line.strip().lower() != "end_header":
line = fhead.readline()
# Intercept element vertex field
if line.lower().startswith("element vertex "):
out.write("element vertex %s\n" % vertex_count)
else:
out.write(line)
i += 1
if i > 100:
raise IOError("Cannot find end_header field. Invalid PLY?")
for ipc in input_point_cloud_files:
i = 0
with open(ipc, "rb") as fin:
# Skip header
line = fin.readline()
while line.strip().lower() != "end_header":
line = fin.readline()
i += 1
if i > 100:
raise IOError("Cannot find end_header field. Invalid PLY?")
# Write fields
out.write(fin.read())
return output_file
def merge_ply(input_point_cloud_files, output_file, dims=None):
num_files = len(input_point_cloud_files)
if num_files == 0:
log.ODM_WARNING("No input point cloud files to process")
return
cmd = [
'pdal',
'merge',
'--writers.ply.sized_types=false',
'--writers.ply.storage_mode="little endian"',
('--writers.ply.dims="%s"' % dims) if dims is not None else '',
' '.join(map(quote, input_point_cloud_files + [output_file])),
]
system.run(' '.join(cmd))
def post_point_cloud_steps(args, tree):
# XYZ point cloud output
if args.pc_csv:

1
run.py
Wyświetl plik

@ -48,6 +48,7 @@ if __name__ == '__main__':
quote(os.path.join(args.project_path, "odm_georeferencing_25d")),
quote(os.path.join(args.project_path, "odm_meshing")),
quote(os.path.join(args.project_path, "odm_orthophoto")),
quote(os.path.join(args.project_path, "odm_dem")),
quote(os.path.join(args.project_path, "odm_texturing")),
quote(os.path.join(args.project_path, "opensfm")),
quote(os.path.join(args.project_path, "odm_filterpoints")),

Wyświetl plik

@ -25,9 +25,9 @@ class ODMFilterPoints(types.ODM_Stage):
point_cloud.filter(inputPointCloud, tree.filtered_point_cloud,
standard_deviation=args.pc_filter,
confidence=None,
sample_radius=args.pc_sample,
verbose=args.verbose)
verbose=args.verbose,
max_concurrency=args.max_concurrency)
else:
log.ODM_WARNING('Found a valid point cloud file in: %s' %