Merge pull request #956 from pierotofy/simplifydtm

DEM simplification, filtering for all

Former-commit-id: 2a94b42889
pull/1161/head
Piero Toffanin 2019-03-07 11:57:53 -05:00 zatwierdzone przez GitHub
commit df311ffc4c
18 zmienionych plików z 1647 dodań i 171 usunięć

Wyświetl plik

@ -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 ()

Wyświetl plik

@ -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})

Wyświetl plik

@ -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

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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 );
}
}
};

Wyświetl plik

@ -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

Wyświetl plik

@ -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;
}
}

Wyświetl plik

@ -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");
}

Wyświetl plik

@ -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.')

Wyświetl plik

@ -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':

Wyświetl plik

@ -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 = [

Wyświetl plik

@ -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))

Wyświetl plik

@ -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)

Wyświetl plik

@ -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):

Wyświetl plik

@ -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)

Wyświetl plik

@ -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: