Removed 2.5D meshing program, VTK7

Former-commit-id: c784aa856f
pull/1161/head
Piero Toffanin 2018-07-02 10:29:52 -04:00
rodzic 9f8161f2f2
commit 4eaad83f66
13 zmienionych plików z 2 dodań i 1496 usunięć

Wyświetl plik

@ -60,7 +60,7 @@ RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Clean Superbuild
RUN rm -rf /code/SuperBuild/download /code/SuperBuild/src/vtk7 /code/SuperBuild/src/opencv /code/SuperBuild/src/pcl /code/SuperBuild/src/pdal /code/SuperBuild/src/opengv /code/SuperBuild/src/mvstexturing /code/SuperBuild/src/ceres /code/SuperBuild/build/vtk7 /code/SuperBuild/build/opencv
RUN rm -rf /code/SuperBuild/download /code/SuperBuild/src/opencv /code/SuperBuild/src/pcl /code/SuperBuild/src/pdal /code/SuperBuild/src/opengv /code/SuperBuild/src/mvstexturing /code/SuperBuild/src/ceres /code/SuperBuild/build/opencv
# Entry point
ENTRYPOINT ["python", "/code/run.py", "code"]

Wyświetl plik

@ -95,17 +95,6 @@ option(ODM_BUILD_Ceres "Force to build Ceres library" OFF)
SETUP_EXTERNAL_PROJECT(Ceres ${ODM_Ceres_Version} ${ODM_BUILD_Ceres})
# ---------------------------------------------------------------------------------------------
# VTK7
# We need to build VTK from sources because Debian packages
# are built with DVTK_SMP_IMPLEMENTATION_TYPE set to
# "Sequential" which means no multithread support.
set(ODM_VTK7_Version 7.1.1)
option(ODM_BUILD_VTK7 "Force to build VTK7 library" OFF)
SETUP_EXTERNAL_PROJECT(VTK7 ${ODM_VTK7_Version} ${ODM_BUILD_VTK7})
# ---------------------------------------------------------------------------------------------
# Hexer
#

Wyświetl plik

@ -1,29 +0,0 @@
set(_proj_name vtk7)
set(_SB_BINARY_DIR "${SB_BINARY_DIR}/${_proj_name}")
ExternalProject_Add(${_proj_name}
PREFIX ${_SB_BINARY_DIR}
TMP_DIR ${_SB_BINARY_DIR}/tmp
STAMP_DIR ${_SB_BINARY_DIR}/stamp
#--Download step--------------
DOWNLOAD_DIR ${SB_DOWNLOAD_DIR}/${_proj_name}
URL https://github.com/Kitware/VTK/archive/v7.1.1.zip
#--Update/Patch step----------
UPDATE_COMMAND ""
#--Configure step-------------
SOURCE_DIR ${SB_SOURCE_DIR}/${_proj_name}
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX:PATH=${SB_INSTALL_DIR}
-DVTK_SMP_IMPLEMENTATION_TYPE=TBB
-DCMAKE_BUILD_TYPE=Release
-DVTK_Group_Rendering=OFF
-DBUILD_TESTING=OFF
#--Build step-----------------
BINARY_DIR ${_SB_BINARY_DIR}
#--Install step---------------
INSTALL_DIR ${SB_INSTALL_DIR}
#--Output logging-------------
LOG_DOWNLOAD OFF
LOG_CONFIGURE OFF
LOG_BUILD OFF
)

Wyświetl plik

@ -61,7 +61,7 @@ RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# Clean Superbuild
RUN rm -rf /code/SuperBuild/download /code/SuperBuild/src/vtk7 /code/SuperBuild/src/opencv /code/SuperBuild/src/pcl /code/SuperBuild/src/pdal /code/SuperBuild/src/opengv /code/SuperBuild/src/mvstexturing /code/SuperBuild/src/ceres /code/SuperBuild/build/vtk7 /code/SuperBuild/build/opencv
RUN rm -rf /code/SuperBuild/download /code/SuperBuild/src/opencv /code/SuperBuild/src/pcl /code/SuperBuild/src/pdal /code/SuperBuild/src/opengv /code/SuperBuild/src/mvstexturing /code/SuperBuild/src/ceres /code/SuperBuild/build/opencv
# Entry point
ENTRYPOINT ["python", "/code/run.py", "code"]

Wyświetl plik

@ -8,7 +8,6 @@ add_subdirectory(odm_extract_utm)
add_subdirectory(odm_georef)
add_subdirectory(odm_meshing)
add_subdirectory(odm_orthophoto)
add_subdirectory(odm_25dmeshing)
add_subdirectory(odm_cleanmesh)
if (ODM_BUILD_SLAM)
add_subdirectory(odm_slam)

Wyświetl plik

@ -1,23 +0,0 @@
project(odm_25dmeshing)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR})
set (CMAKE_CXX_STANDARD 11)
#set(VTK_DIR "/data/packages/VTK-7.1.1-build")
set(VTK_SMP_IMPLEMENTATION_TYPE TBB)
find_package(VTK 7.1.1 REQUIRED)
include(${VTK_USE_FILE})
# Add compiler options.
#add_definitions(-Wall -Wextra -O0 -g3)
add_definitions(-Wall -Wextra)
# Add source directory
aux_source_directory("./src" SRC_LIST)
# Add exectuteable
add_executable(${PROJECT_NAME} ${SRC_LIST})
target_link_libraries(odm_25dmeshing ${VTK_LIBRARIES})

Wyświetl plik

@ -1,31 +0,0 @@
#include "Logger.hpp"
Logger::Logger(bool isPrintingInCout) : isPrintingInCout_(isPrintingInCout)
{
}
Logger::~Logger()
{
}
void Logger::printToFile(std::string filePath)
{
std::ofstream file(filePath.c_str(), std::ios::binary);
file << logStream_.str();
file.close();
}
bool Logger::isPrintingInCout() const
{
return isPrintingInCout_;
}
void Logger::setIsPrintingInCout(bool isPrintingInCout)
{
isPrintingInCout_ = isPrintingInCout;
}

Wyświetl plik

@ -1,67 +0,0 @@
#pragma once
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
/*!
* \brief The Logger class is used to store program messages in a log file.
* \details By using the << operator while printInCout is set, the class writes both to
* cout and to file, if the flag is not set, output is written to file only.
*/
class Logger
{
public:
/*!
* \brief Logger Contains functionality for printing and displaying log information.
* \param printInCout Flag toggling if operator << also writes to cout.
*/
Logger(bool isPrintingInCout = true);
/*!
* \brief Destructor.
*/
~Logger();
/*!
* \brief print Prints the contents of the log to file.
* \param filePath Path specifying where to write the log.
*/
void printToFile(std::string filePath);
/*!
* \brief isPrintingInCout Check if console printing flag is set.
* \return Console printing flag.
*/
bool isPrintingInCout() const;
/*!
* \brief setIsPrintingInCout Set console printing flag.
* \param isPrintingInCout Value, if true, messages added to the log are also printed in cout.
*/
void setIsPrintingInCout(bool isPrintingInCout);
/*!
* Operator for printing messages to log and in the standard output stream if desired.
*/
template<class T>
friend Logger& operator<< (Logger &log, T t)
{
// If console printing is enabled.
if (log.isPrintingInCout_)
{
std::cout << t;
std::cout.flush();
}
// Write to log.
log.logStream_ << t;
return log;
}
private:
bool isPrintingInCout_; /*!< If flag is set, log is printed in cout and written to the log. */
std::stringstream logStream_; /*!< Stream for storing the log. */
};

Wyświetl plik

@ -1,450 +0,0 @@
#include "Odm25dMeshing.hpp"
int Odm25dMeshing::run(int argc, char **argv) {
log << logFilePath << "\n";
// If no arguments were passed, print help and return early.
if (argc <= 1) {
printHelp();
return EXIT_SUCCESS;
}
try {
parseArguments(argc, argv);
loadPointCloud();
buildMesh();
} catch (const Odm25dMeshingException& e) {
log.setIsPrintingInCout(true);
log << e.what() << "\n";
log.printToFile(logFilePath);
log << "For more detailed information, see log file." << "\n";
return EXIT_FAILURE;
} catch (const std::exception& e) {
log.setIsPrintingInCout(true);
log << "Error in OdmMeshing:\n";
log << e.what() << "\n";
log.printToFile(logFilePath);
log << "For more detailed information, see log file." << "\n";
return EXIT_FAILURE;
}
log.printToFile(logFilePath);
return EXIT_SUCCESS;
}
void Odm25dMeshing::loadPointCloud() {
log << "Loading point cloud... ";
try{
std::ifstream ss(inputFile, std::ios::binary);
if (ss.fail()) throw Odm25dMeshingException("Failed to open " + inputFile);
PlyFile file;
file.parse_header(ss);
std::shared_ptr<PlyData> vertices = file.request_properties_from_element("vertex", { "x", "y", "z" });
file.read(ss);
const size_t numVerticesBytes = vertices->buffer.size_bytes();
struct float3 { float x, y, z; };
struct double3 { double x, y, z; };
if (vertices->t == tinyply::Type::FLOAT32) {
std::vector<float3> verts(vertices->count);
std::memcpy(verts.data(), vertices->buffer.get(), numVerticesBytes);
for (float3 &v : verts){
points->InsertNextPoint(v.x, v.y, v.z);
}
}else if (vertices->t == tinyply::Type::FLOAT64) {
std::vector<double3> verts(vertices->count);
std::memcpy(verts.data(), vertices->buffer.get(), numVerticesBytes);
for (double3 &v : verts){
points->InsertNextPoint(v.x, v.y, v.z);
}
}else{
throw Odm25dMeshingException("Invalid data type (only float32 and float64 are supported): " + std::to_string((int)vertices->t));
}
}
catch (const std::exception & e)
{
throw Odm25dMeshingException("Error while loading point cloud: " + std::string(e.what()));
}
log << "loaded " << points->GetNumberOfPoints() << " points\n";
}
void Odm25dMeshing::buildMesh(){
vtkThreadedImageAlgorithm::SetGlobalDefaultEnableSMP(true);
log << "Remove outliers... ";
vtkSmartPointer<vtkPolyData> polyPoints =
vtkSmartPointer<vtkPolyData>::New();
polyPoints->SetPoints(points);
vtkSmartPointer<vtkOctreePointLocator> locator = vtkSmartPointer<vtkOctreePointLocator>::New();
vtkSmartPointer<vtkRadiusOutlierRemoval> radiusRemoval =
vtkSmartPointer<vtkRadiusOutlierRemoval>::New();
radiusRemoval->SetInputData(polyPoints);
radiusRemoval->SetLocator(locator);
radiusRemoval->SetRadius(20); // 20 meters
radiusRemoval->SetNumberOfNeighbors(2);
vtkSmartPointer<vtkStatisticalOutlierRemoval> statsRemoval =
vtkSmartPointer<vtkStatisticalOutlierRemoval>::New();
statsRemoval->SetInputConnection(radiusRemoval->GetOutputPort());
statsRemoval->SetLocator(locator);
statsRemoval->SetSampleSize(neighbors);
statsRemoval->SetStandardDeviationFactor(1.5);
statsRemoval->GenerateOutliersOff();
statsRemoval->Update();
log << (radiusRemoval->GetNumberOfPointsRemoved() + statsRemoval->GetNumberOfPointsRemoved()) << " points removed\n";
vtkSmartPointer<vtkPoints> cleanedPoints = statsRemoval->GetOutput()->GetPoints();
statsRemoval = nullptr;
radiusRemoval = nullptr;
polyPoints = nullptr;
log << "Squash point cloud to plane... ";
vtkSmartPointer<vtkFloatArray> elevation = vtkSmartPointer<vtkFloatArray>::New();
elevation->SetName("elevation");
elevation->SetNumberOfComponents(1);
double p[2];
for (vtkIdType i = 0; i < cleanedPoints->GetNumberOfPoints(); i++){
cleanedPoints->GetPoint(i, p);
elevation->InsertNextValue(p[2]);
p[2] = 0.0;
cleanedPoints->SetPoint(i, p);
}
log << "OK\n";
vtkSmartPointer<vtkPolyData> polydataToProcess =
vtkSmartPointer<vtkPolyData>::New();
polydataToProcess->SetPoints(cleanedPoints);
polydataToProcess->GetPointData()->SetScalars(elevation);
const float NODATA = -9999;
double *bounds = polydataToProcess->GetBounds();
double centerX = polydataToProcess->GetCenter()[0];
double centerY = polydataToProcess->GetCenter()[1];
double centerZ = polydataToProcess->GetCenter()[2];
double extentX = bounds[1] - bounds[0];
double extentY = bounds[3] - bounds[2];
if (resolution == 0.0){
resolution = (double)maxVertexCount / (sqrt(extentX * extentY) * 75.0);
log << "Automatically set resolution to " << std::fixed << resolution << "\n";
}
int width = ceil(extentX * resolution);
int height = ceil(extentY * resolution);
log << "Plane extentX: " << extentX <<
", extentY: " << extentY << "\n";
double planeCenter[3];
planeCenter[0] = centerX;
planeCenter[1] = centerY;
planeCenter[2] = centerZ;
vtkSmartPointer<vtkPlaneSource> plane =
vtkSmartPointer<vtkPlaneSource>::New();
plane->SetResolution(width, height);
plane->SetOrigin(0.0, 0.0, 0.0);
plane->SetPoint1(extentX, 0.0, 0.0);
plane->SetPoint2(0.0, extentY, 0);
plane->SetCenter(planeCenter);
plane->SetNormal(0.0, 0.0, 1.0);
vtkSmartPointer<vtkShepardKernel> shepardKernel =
vtkSmartPointer<vtkShepardKernel>::New();
shepardKernel->SetPowerParameter(2.0);
shepardKernel->SetKernelFootprintToNClosest();
shepardKernel->SetNumberOfPoints(neighbors);
vtkSmartPointer<vtkImageData> image =
vtkSmartPointer<vtkImageData>::New();
image->SetDimensions(width, height, 1);
log << "DSM size is " << width << "x" << height << " (" << ceil(width * height * sizeof(float) * 1e-6) << " MB) \n";
image->AllocateScalars(VTK_FLOAT, 1);
log << "Point interpolation using shepard's kernel... ";
vtkSmartPointer<vtkPointInterpolator> interpolator =
vtkSmartPointer<vtkPointInterpolator>::New();
interpolator->SetInputConnection(plane->GetOutputPort());
interpolator->SetSourceData(polydataToProcess);
interpolator->SetKernel(shepardKernel);
interpolator->SetLocator(locator);
interpolator->SetNullValue(NODATA);
interpolator->Update();
vtkSmartPointer<vtkPolyData> interpolatedPoly =
interpolator->GetPolyDataOutput();
log << "OK\nTransfering interpolation results to DSM... ";
interpolator = nullptr;
polydataToProcess = nullptr;
elevation = nullptr;
cleanedPoints = nullptr;
plane = nullptr;
shepardKernel = nullptr;
locator = nullptr;
vtkSmartPointer<vtkFloatArray> interpolatedElevation =
vtkFloatArray::SafeDownCast(interpolatedPoly->GetPointData()->GetArray("elevation"));
for (int i = 0; i < width; i++){
for (int j = 0; j < height; j++){
float* pixel = static_cast<float*>(image->GetScalarPointer(i,j,0));
vtkIdType cellId = interpolatedPoly->GetCell(j * width + i)->GetPointId(0);
pixel[0] = interpolatedElevation->GetValue(cellId);
}
}
log << "OK\nMedian filter...";
vtkSmartPointer<vtkImageMedian3D> medianFilter =
vtkSmartPointer<vtkImageMedian3D>::New();
medianFilter->SetInputData(image);
medianFilter->SetKernelSize(
std::max(1.0, resolution),
std::max(1.0, resolution),
1);
medianFilter->Update();
log << "OK\n";
// double diffuseIterations = std::max(1.0, resolution / 2.0);
// vtkSmartPointer<vtkImageAnisotropicDiffusion2D> diffuse1 =
// vtkSmartPointer<vtkImageAnisotropicDiffusion2D>::New();
// diffuse1->SetInputConnection(medianFilter->GetOutputPort());
// diffuse1->FacesOn();
// diffuse1->EdgesOn();
// diffuse1->CornersOn();
// diffuse1->SetDiffusionFactor(1); // Full strength
// diffuse1->GradientMagnitudeThresholdOn();
// diffuse1->SetDiffusionThreshold(0.2); // Don't smooth jumps in elevation > than 0.20m
// diffuse1->SetNumberOfIterations(diffuseIterations);
// diffuse1->Update();
if (outputDsmFile != ""){
log << "Saving DSM to file... ";
vtkSmartPointer<vtkTIFFWriter> tiffWriter =
vtkSmartPointer<vtkTIFFWriter>::New();
tiffWriter->SetFileName(outputDsmFile.c_str());
tiffWriter->SetInputData(medianFilter->GetOutput());
tiffWriter->Write();
log << "OK\n";
}
log << "Triangulate... ";
vtkSmartPointer<vtkGreedyTerrainDecimation> terrain =
vtkSmartPointer<vtkGreedyTerrainDecimation>::New();
terrain->SetErrorMeasureToNumberOfTriangles();
terrain->SetNumberOfTriangles(maxVertexCount * 2); // Approximate
terrain->SetInputData(medianFilter->GetOutput());
terrain->BoundaryVertexDeletionOn();
log << "OK\nTransform... ";
vtkSmartPointer<vtkTransform> transform =
vtkSmartPointer<vtkTransform>::New();
transform->Translate(-extentX / 2.0 + centerX,
-extentY / 2.0 + centerY, 0);
transform->Scale(extentX / width, extentY / height, 1);
vtkSmartPointer<vtkTransformFilter> transformFilter =
vtkSmartPointer<vtkTransformFilter>::New();
transformFilter->SetInputConnection(terrain->GetOutputPort());
transformFilter->SetTransform(transform);
log << "OK\n";
log << "Saving mesh to file... ";
vtkSmartPointer<vtkPLYWriter> plyWriter =
vtkSmartPointer<vtkPLYWriter>::New();
plyWriter->SetFileName(outputFile.c_str());
plyWriter->SetInputConnection(transformFilter->GetOutputPort());
plyWriter->SetFileTypeToASCII();
plyWriter->Write();
log << "OK\n";
#ifdef SUPPORTDEBUGWINDOW
if (showDebugWindow){
vtkSmartPointer<vtkPolyDataMapper> mapper =
vtkSmartPointer<vtkPolyDataMapper>::New();
mapper->SetInputConnection(transformFilter->GetOutputPort());
mapper->SetScalarRange(150, 170);
// vtkSmartPointer<vtkDataSetMapper> mapper =
// vtkSmartPointer<vtkDataSetMapper>::New();
// mapper->SetInputData(image);
// mapper->SetScalarRange(150, 170);
vtkSmartPointer<vtkActor> actor =
vtkSmartPointer<vtkActor>::New();
actor->SetMapper(mapper);
actor->GetProperty()->SetPointSize(5);
vtkSmartPointer<vtkRenderer> renderer =
vtkSmartPointer<vtkRenderer>::New();
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow(renderWindow);
renderer->AddActor(actor);
renderer->SetBackground(0.1804,0.5451,0.3412); // Sea green
renderWindow->Render();
renderWindowInteractor->Start();
}
#endif
}
void Odm25dMeshing::parseArguments(int argc, char **argv) {
for (int argIndex = 1; argIndex < argc; ++argIndex) {
// The argument to be parsed.
std::string argument = std::string(argv[argIndex]);
if (argument == "-help") {
printHelp();
exit(0);
} else if (argument == "-verbose") {
log.setIsPrintingInCout(true);
} else if (argument == "-maxVertexCount" && argIndex < argc) {
++argIndex;
if (argIndex >= argc) throw Odm25dMeshingException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
std::stringstream ss(argv[argIndex]);
ss >> maxVertexCount;
if (ss.bad()) throw Odm25dMeshingException("Argument '" + argument + "' has a bad value (wrong type).");
maxVertexCount = std::max<unsigned int>(maxVertexCount, 0);
log << "Vertex count was manually set to: " << maxVertexCount << "\n";
} else if (argument == "-resolution" && argIndex < argc) {
++argIndex;
if (argIndex >= argc) throw Odm25dMeshingException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
std::stringstream ss(argv[argIndex]);
ss >> resolution;
if (ss.bad()) throw Odm25dMeshingException("Argument '" + argument + "' has a bad value (wrong type).");
resolution = std::min<double>(100000, std::max<double>(resolution, 0));
log << "Resolution was manually set to: " << resolution << "\n";
} else if (argument == "-neighbors" && argIndex < argc) {
++argIndex;
if (argIndex >= argc) throw Odm25dMeshingException("Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
std::stringstream ss(argv[argIndex]);
ss >> neighbors;
if (ss.bad()) throw Odm25dMeshingException("Argument '" + argument + "' has a bad value (wrong type).");
neighbors = std::min<unsigned int>(1000, std::max<unsigned int>(neighbors, 1));
log << "Neighbors was manually set to: " << neighbors << "\n";
} else if (argument == "-inputFile" && argIndex < argc) {
++argIndex;
if (argIndex >= argc) {
throw Odm25dMeshingException(
"Argument '" + argument
+ "' expects 1 more input following it, but no more inputs were provided.");
}
inputFile = std::string(argv[argIndex]);
std::ifstream testFile(inputFile.c_str(), std::ios::binary);
if (!testFile.is_open()) {
throw Odm25dMeshingException(
"Argument '" + argument + "' has a bad value. (file not accessible)");
}
testFile.close();
log << "Reading point cloud at: " << inputFile << "\n";
} else if (argument == "-outputFile" && argIndex < argc) {
++argIndex;
if (argIndex >= argc) {
throw Odm25dMeshingException(
"Argument '" + argument + "' expects 1 more input following it, but no more inputs were provided.");
}
outputFile = std::string(argv[argIndex]);
std::ofstream testFile(outputFile.c_str());
if (!testFile.is_open()) {
throw Odm25dMeshingException(
"Argument '" + argument + "' has a bad value.");
}
testFile.close();
log << "Writing output to: " << outputFile << "\n";
}else if (argument == "-outputDsmFile" && argIndex < argc) {
++argIndex;
if (argIndex >= argc) {
throw Odm25dMeshingException(
"Argument '" + argument
+ "' expects 1 more input following it, but no more inputs were provided.");
}
outputDsmFile = std::string(argv[argIndex]);
std::ofstream testFile(outputDsmFile.c_str());
if (!testFile.is_open()) {
throw Odm25dMeshingException(
"Argument '" + argument + "' has a bad value. (file not accessible)");
}
testFile.close();
log << "Saving DSM output to: " << outputDsmFile << "\n";
} else if (argument == "-showDebugWindow") {
showDebugWindow = true;
} else if (argument == "-logFile" && argIndex < argc) {
++argIndex;
if (argIndex >= argc) {
throw Odm25dMeshingException(
"Argument '" + argument
+ "' expects 1 more input following it, but no more inputs were provided.");
}
logFilePath = std::string(argv[argIndex]);
std::ofstream testFile(outputFile.c_str());
if (!testFile.is_open()) {
throw Odm25dMeshingException(
"Argument '" + argument + "' has a bad value.");
}
testFile.close();
log << "Writing log information to: " << logFilePath << "\n";
} else {
printHelp();
throw Odm25dMeshingException("Unrecognised argument '" + argument + "'");
}
}
}
void Odm25dMeshing::printHelp() {
bool printInCoutPop = log.isPrintingInCout();
log.setIsPrintingInCout(true);
log << "Usage: odm_25dmeshing -inputFile [plyFile] [optional-parameters]\n";
log << "Create a 2.5D mesh from a point cloud. "
<< "The program requires a path to an input PLY point cloud file, all other input parameters are optional.\n\n";
log << " -inputFile <path> to PLY point cloud\n"
<< " -outputFile <path> where the output PLY 2.5D mesh should be saved (default: " << outputFile << ")\n"
<< " -outputDsmFile <path> Optionally output the Digital Surface Model (DSM) computed for generating the mesh. (default: " << outputDsmFile << ")\n"
<< " -logFile <path> log file path (default: " << logFilePath << ")\n"
<< " -verbose whether to print verbose output (default: " << (printInCoutPop ? "true" : "false") << ")\n"
<< " -maxVertexCount <0 - N> Maximum number of vertices in the output mesh. The mesh might have fewer vertices, but will not exceed this limit. (default: " << maxVertexCount << ")\n"
<< " -neighbors <1 - 1000> Number of nearest neighbors to consider when doing shepard's interpolation and outlier removal. Higher values lead to smoother meshes but take longer to process. (default: " << neighbors << ")\n"
<< " -resolution <0 - N> Size of the interpolated digital surface model (DSM) used for deriving the 2.5D mesh, expressed in pixels per meter unit. When set to zero, the program automatically attempts to find a good value based on the point cloud extent and target vertex count. (default: " << resolution << ")\n"
<< "\n";
log.setIsPrintingInCout(printInCoutPop);
}

Wyświetl plik

@ -1,111 +0,0 @@
#pragma once
//#define SUPPORTDEBUGWINDOW 1
#ifdef SUPPORTDEBUGWINDOW
#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkDataSetMapper.h>
#endif
#include <vtkShepardKernel.h>
#include <vtkPointData.h>
#include <vtkImageData.h>
#include <vtkGreedyTerrainDecimation.h>
#include <vtkPLYWriter.h>
#include <vtkSmartPointer.h>
#include <vtkFloatArray.h>
#include <vtkOctreePointLocator.h>
#include <vtkPointInterpolator.h>
#include <vtkPlaneSource.h>
#include <vtkTransform.h>
#include <vtkTransformFilter.h>
#include <vtkImageAnisotropicDiffusion2D.h>
#include <vtkTIFFWriter.h>
#include <vtkStatisticalOutlierRemoval.h>
#include <vtkImageMedian3D.h>
#include <vtkRadiusOutlierRemoval.h>
// For compatibility with new VTK generic data arrays
#ifdef vtkGenericDataArray_h
#define InsertNextTupleValue InsertNextTypedTuple
#endif
#include <cstring>
#include "tinyply.h"
using namespace tinyply;
#include "Logger.hpp"
class Odm25dMeshing {
public:
Odm25dMeshing() :
log(false) {};
~Odm25dMeshing() {};
/*!
* \brief run Runs the meshing functionality using the provided input arguments.
* For a list of accepted arguments, please see the main page documentation or
* call the program with parameter "-help".
* \param argc Application argument count.
* \param argv Argument values.
* \return 0 If successful.
*/
int run(int argc, char **argv);
private:
/*!
* \brief parseArguments Parses command line arguments.
* \param argc Application argument count.
* \param argv Argument values.
*/
void parseArguments(int argc, char** argv);
/*!
* \brief printHelp Prints help, explaining usage. Can be shown by calling the program with argument: "-help".
*/
void printHelp();
void loadPointCloud();
void buildMesh();
Logger log;
std::string inputFile = "";
std::string outputFile = "odm_25dmesh.ply";
std::string logFilePath = "odm_25dmeshing_log.txt";
int maxVertexCount = 100000;
double resolution = 0;
unsigned int neighbors = 24;
std::string outputDsmFile = "";
bool showDebugWindow = false;
vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
};
class Odm25dMeshingException: public std::exception {
public:
Odm25dMeshingException() :
message("Error in Odm25dMeshing") {
}
Odm25dMeshingException(std::string msgInit) :
message("Error in Odm25dMeshing:\n" + msgInit) {
}
~Odm25dMeshingException() throw () {
}
virtual const char* what() const throw () {
return message.c_str();
}
private:
std::string message; /**< The error message **/
};

Wyświetl plik

@ -1,31 +0,0 @@
/*
OpenDroneMap - https://www.opendronemap.org
Copyright (C) 2017 OpenDroneMap Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Odm25dMeshing.hpp"
/*!
* \mainpage main OpenDroneMap 2.5D Meshing Module
*
* The OpenDroneMap 2.5D Meshing Module generates a 2.5D mesh using a constrained
* delaunay triangulation from any point cloud (points with corresponding normals).
*/
int main(int argc, char** argv)
{
Odm25dMeshing om;
return om.run(argc, argv);
}

Wyświetl plik

@ -1,621 +0,0 @@
// This software is in the public domain. Where that dedication is not
// recognized, you are granted a perpetual, irrevocable license to copy,
// distribute, and modify this file as you see fit.
// Authored in 2015 by Dimitri Diakopoulos (http://www.dimitridiakopoulos.com)
// https://github.com/ddiakopoulos/tinyply
// Version 2.0
#include "tinyply.h"
#include <algorithm>
#include <functional>
#include <type_traits>
#include <iostream>
#include <cstring>
using namespace tinyply;
using namespace std;
//////////////////
// Endian Utils //
//////////////////
template<typename T> T endian_swap(const T & v) { return v; }
template<> inline uint16_t endian_swap(const uint16_t & v) { return (v << 8) | (v >> 8); }
template<> inline uint32_t endian_swap(const uint32_t & v) { return (v << 24) | ((v << 8) & 0x00ff0000) | ((v >> 8) & 0x0000ff00) | (v >> 24); }
template<> inline uint64_t endian_swap(const uint64_t & v)
{
return (((v & 0x00000000000000ffLL) << 56) |
((v & 0x000000000000ff00LL) << 40) |
((v & 0x0000000000ff0000LL) << 24) |
((v & 0x00000000ff000000LL) << 8) |
((v & 0x000000ff00000000LL) >> 8) |
((v & 0x0000ff0000000000LL) >> 24) |
((v & 0x00ff000000000000LL) >> 40) |
((v & 0xff00000000000000LL) >> 56));
}
template<> inline int16_t endian_swap(const int16_t & v) { uint16_t r = endian_swap(*(uint16_t*)&v); return *(int16_t*)&r; }
template<> inline int32_t endian_swap(const int32_t & v) { uint32_t r = endian_swap(*(uint32_t*)&v); return *(int32_t*)&r; }
template<> inline int64_t endian_swap(const int64_t & v) { uint64_t r = endian_swap(*(uint64_t*)&v); return *(int64_t*)&r; }
inline float endian_swap_float(const uint32_t & v) { union { float f; uint32_t i; }; i = endian_swap(v); return f; }
inline double endian_swap_double(const uint64_t & v) { union { double d; uint64_t i; }; i = endian_swap(v); return d; }
/////////////////////////////
// Internal Implementation //
/////////////////////////////
inline Type property_type_from_string(const std::string & t)
{
if (t == "int8" || t == "char") return Type::INT8;
else if (t == "uint8" || t == "uchar") return Type::UINT8;
else if (t == "int16" || t == "short") return Type::INT16;
else if (t == "uint16" || t == "ushort") return Type::UINT16;
else if (t == "int32" || t == "int") return Type::INT32;
else if (t == "uint32" || t == "uint") return Type::UINT32;
else if (t == "float32" || t == "float") return Type::FLOAT32;
else if (t == "float64" || t == "double") return Type::FLOAT64;
return Type::INVALID;
}
struct PlyFile::PlyFileImpl
{
struct PlyCursor
{
size_t byteOffset;
size_t totalSizeBytes;
};
struct ParsingHelper
{
std::shared_ptr<PlyData> data;
std::shared_ptr<PlyCursor> cursor;
};
std::map<std::string, ParsingHelper> userData;
bool isBinary = false;
bool isBigEndian = false;
std::vector<PlyElement> elements;
std::vector<std::string> comments;
std::vector<std::string> objInfo;
void read(std::istream & is);
void write(std::ostream & os, bool isBinary);
std::shared_ptr<PlyData> request_properties_from_element(const std::string & elementKey, const std::initializer_list<std::string> propertyKeys);
void add_properties_to_element(const std::string & elementKey, const std::initializer_list<std::string> propertyKeys, const Type type, const size_t count, uint8_t * data, const Type listType, const size_t listCount);
size_t read_property_binary(const Type t, void * dest, size_t & destOffset, std::istream & is);
size_t read_property_ascii(const Type t, void * dest, size_t & destOffset, std::istream & is);
size_t skip_property_binary(const PlyProperty & property, std::istream & is);
size_t skip_property_ascii(const PlyProperty & property, std::istream & is);
bool parse_header(std::istream & is);
void parse_data(std::istream & is, bool firstPass);
void read_header_format(std::istream & is);
void read_header_element(std::istream & is);
void read_header_property(std::istream & is);
void read_header_text(std::string line, std::vector<std::string> & place, int erase = 0);
void write_header(std::ostream & os);
void write_ascii_internal(std::ostream & os);
void write_binary_internal(std::ostream & os);
void write_property_ascii(Type t, std::ostream & os, uint8_t * src, size_t & srcOffset);
void write_property_binary(Type t, std::ostream & os, uint8_t * src, size_t & srcOffset);
};
//////////////////
// PLY Property //
//////////////////
PlyProperty::PlyProperty(std::istream & is) : isList(false)
{
std::string type;
is >> type;
if (type == "list")
{
std::string countType;
is >> countType >> type;
listType = property_type_from_string(countType);
isList = true;
}
propertyType = property_type_from_string(type);
is >> name;
}
/////////////////
// PLY Element //
/////////////////
PlyElement::PlyElement(std::istream & is)
{
is >> name >> size;
}
///////////
// Utils //
///////////
std::string make_key(const std::string & a, const std::string & b)
{
return (a + "-" + b);
}
template<typename T> void ply_cast(void * dest, const char * src, bool be)
{
*(static_cast<T *>(dest)) = (be) ? endian_swap(*(reinterpret_cast<const T *>(src))) : *(reinterpret_cast<const T *>(src));
}
template<typename T> void ply_cast_float(void * dest, const char * src, bool be)
{
*(static_cast<T *>(dest)) = (be) ? endian_swap_float(*(reinterpret_cast<const uint32_t *>(src))) : *(reinterpret_cast<const T *>(src));
}
template<typename T> void ply_cast_double(void * dest, const char * src, bool be)
{
*(static_cast<T *>(dest)) = (be) ? endian_swap_double(*(reinterpret_cast<const uint64_t *>(src))) : *(reinterpret_cast<const T *>(src));
}
template<typename T> T ply_read_ascii(std::istream & is)
{
T data;
is >> data;
return data;
}
template<typename T> void ply_cast_ascii(void * dest, std::istream & is)
{
*(static_cast<T *>(dest)) = ply_read_ascii<T>(is);
}
size_t find_element(const std::string & key, const std::vector<PlyElement> & list)
{
for (size_t i = 0; i < list.size(); i++) if (list[i].name == key) return i;
return -1;
}
size_t find_property(const std::string & key, const std::vector<PlyProperty> & list)
{
for (size_t i = 0; i < list.size(); ++i) if (list[i].name == key) return i;
return -1;
}
//////////////
// PLY File //
//////////////
bool PlyFile::PlyFileImpl::parse_header(std::istream & is)
{
std::string line;
while (std::getline(is, line))
{
std::istringstream ls(line);
std::string token;
ls >> token;
if (token == "ply" || token == "PLY" || token == "") continue;
else if (token == "comment") read_header_text(line, comments, 8);
else if (token == "format") read_header_format(ls);
else if (token == "element") read_header_element(ls);
else if (token == "property") read_header_property(ls);
else if (token == "obj_info") read_header_text(line, objInfo, 9);
else if (token == "end_header") break;
else return false;
}
return true;
}
void PlyFile::PlyFileImpl::read_header_text(std::string line, std::vector<std::string>& place, int erase)
{
place.push_back((erase > 0) ? line.erase(0, erase) : line);
}
void PlyFile::PlyFileImpl::read_header_format(std::istream & is)
{
std::string s;
(is >> s);
if (s == "binary_little_endian") isBinary = true;
else if (s == "binary_big_endian") isBinary = isBigEndian = true;
}
void PlyFile::PlyFileImpl::read_header_element(std::istream & is)
{
elements.emplace_back(is);
}
void PlyFile::PlyFileImpl::read_header_property(std::istream & is)
{
elements.back().properties.emplace_back(is);
}
size_t PlyFile::PlyFileImpl::skip_property_binary(const PlyProperty & p, std::istream & is)
{
static std::vector<char> skip(PropertyTable[p.propertyType].stride);
if (p.isList)
{
size_t listSize = 0;
size_t dummyCount = 0;
read_property_binary(p.listType, &listSize, dummyCount, is);
for (size_t i = 0; i < listSize; ++i) is.read(skip.data(), PropertyTable[p.propertyType].stride);
return listSize * PropertyTable[p.propertyType].stride; // in bytes
}
else
{
is.read(skip.data(), PropertyTable[p.propertyType].stride);
return PropertyTable[p.propertyType].stride;
}
}
size_t PlyFile::PlyFileImpl::skip_property_ascii(const PlyProperty & p, std::istream & is)
{
std::string skip;
if (p.isList)
{
size_t listSize = 0;
size_t dummyCount = 0;
read_property_ascii(p.listType, &listSize, dummyCount, is);
for (size_t i = 0; i < listSize; ++i) is >> skip;
return listSize * PropertyTable[p.propertyType].stride; // in bytes
}
else
{
is >> skip;
return PropertyTable[p.propertyType].stride;
}
}
size_t PlyFile::PlyFileImpl::read_property_binary(const Type t, void * dest, size_t & destOffset, std::istream & is)
{
destOffset += PropertyTable[t].stride;
std::vector<char> src(PropertyTable[t].stride);
is.read(src.data(), PropertyTable[t].stride);
switch (t)
{
case Type::INT8: ply_cast<int8_t>(dest, src.data(), isBigEndian); break;
case Type::UINT8: ply_cast<uint8_t>(dest, src.data(), isBigEndian); break;
case Type::INT16: ply_cast<int16_t>(dest, src.data(), isBigEndian); break;
case Type::UINT16: ply_cast<uint16_t>(dest, src.data(), isBigEndian); break;
case Type::INT32: ply_cast<int32_t>(dest, src.data(), isBigEndian); break;
case Type::UINT32: ply_cast<uint32_t>(dest, src.data(), isBigEndian); break;
case Type::FLOAT32: ply_cast_float<float>(dest, src.data(), isBigEndian); break;
case Type::FLOAT64: ply_cast_double<double>(dest, src.data(), isBigEndian); break;
case Type::INVALID: throw std::invalid_argument("invalid ply property");
}
return PropertyTable[t].stride;
}
size_t PlyFile::PlyFileImpl::read_property_ascii(const Type t, void * dest, size_t & destOffset, std::istream & is)
{
destOffset += PropertyTable[t].stride;
switch (t)
{
case Type::INT8: *((int8_t *)dest) = ply_read_ascii<int32_t>(is); break;
case Type::UINT8: *((uint8_t *)dest) = ply_read_ascii<uint32_t>(is); break;
case Type::INT16: ply_cast_ascii<int16_t>(dest, is); break;
case Type::UINT16: ply_cast_ascii<uint16_t>(dest, is); break;
case Type::INT32: ply_cast_ascii<int32_t>(dest, is); break;
case Type::UINT32: ply_cast_ascii<uint32_t>(dest, is); break;
case Type::FLOAT32: ply_cast_ascii<float>(dest, is); break;
case Type::FLOAT64: ply_cast_ascii<double>(dest, is); break;
case Type::INVALID: throw std::invalid_argument("invalid ply property");
}
return PropertyTable[t].stride;
}
void PlyFile::PlyFileImpl::write_property_ascii(Type t, std::ostream & os, uint8_t * src, size_t & srcOffset)
{
switch (t)
{
case Type::INT8: os << static_cast<int32_t>(*reinterpret_cast<int8_t*>(src)); break;
case Type::UINT8: os << static_cast<uint32_t>(*reinterpret_cast<uint8_t*>(src)); break;
case Type::INT16: os << *reinterpret_cast<int16_t*>(src); break;
case Type::UINT16: os << *reinterpret_cast<uint16_t*>(src); break;
case Type::INT32: os << *reinterpret_cast<int32_t*>(src); break;
case Type::UINT32: os << *reinterpret_cast<uint32_t*>(src); break;
case Type::FLOAT32: os << *reinterpret_cast<float*>(src); break;
case Type::FLOAT64: os << *reinterpret_cast<double*>(src); break;
case Type::INVALID: throw std::invalid_argument("invalid ply property");
}
os << " ";
srcOffset += PropertyTable[t].stride;
}
void PlyFile::PlyFileImpl::write_property_binary(Type t, std::ostream & os, uint8_t * src, size_t & srcOffset)
{
os.write((char *)src, PropertyTable[t].stride);
srcOffset += PropertyTable[t].stride;
}
void PlyFile::PlyFileImpl::read(std::istream & is)
{
// Parse but only get the data size
parse_data(is, true);
std::vector<std::shared_ptr<PlyData>> buffers;
for (auto & entry : userData) buffers.push_back(entry.second.data);
// Since group-requested properties share the same cursor, we need to find unique cursors so we only allocate once
std::sort(buffers.begin(), buffers.end());
buffers.erase(std::unique(buffers.begin(), buffers.end()), buffers.end());
// Not great, but since we sorted by ptrs on PlyData, need to remap back onto its cursor in the userData table
for (auto & b : buffers)
{
for (auto & entry : userData)
{
if (entry.second.data == b && b->buffer.get() == nullptr)
{
b->buffer = Buffer(entry.second.cursor->totalSizeBytes);
}
}
}
// Populate the data
parse_data(is, false);
}
void PlyFile::PlyFileImpl::write(std::ostream & os, bool _isBinary)
{
if (_isBinary) write_binary_internal(os);
else write_ascii_internal(os);
}
void PlyFile::PlyFileImpl::write_binary_internal(std::ostream & os)
{
isBinary = true;
write_header(os);
for (auto & e : elements)
{
for (size_t i = 0; i < e.size; ++i)
{
for (auto & p : e.properties)
{
auto & helper = userData[make_key(e.name, p.name)];
if (p.isList)
{
uint8_t listSize[4] = {0, 0, 0, 0};
std::memcpy(listSize, &p.listCount, sizeof(uint32_t));
size_t dummyCount = 0;
write_property_binary(p.listType, os, listSize, dummyCount);
for (int j = 0; j < p.listCount; ++j)
{
write_property_binary(p.propertyType, os, (helper.data->buffer.get() + helper.cursor->byteOffset), helper.cursor->byteOffset);
}
}
else
{
write_property_binary(p.propertyType, os, (helper.data->buffer.get() + helper.cursor->byteOffset), helper.cursor->byteOffset);
}
}
}
}
}
void PlyFile::PlyFileImpl::write_ascii_internal(std::ostream & os)
{
write_header(os);
for (auto & e : elements)
{
for (size_t i = 0; i < e.size; ++i)
{
for (auto & p : e.properties)
{
auto & helper = userData[make_key(e.name, p.name)];
if (p.isList)
{
os << p.listCount << " ";
for (int j = 0; j < p.listCount; ++j)
{
write_property_ascii(p.propertyType, os, (helper.data->buffer.get() + helper.cursor->byteOffset), helper.cursor->byteOffset);
}
}
else
{
write_property_ascii(p.propertyType, os, (helper.data->buffer.get() + helper.cursor->byteOffset), helper.cursor->byteOffset);
}
}
os << "\n";
}
}
}
void PlyFile::PlyFileImpl::write_header(std::ostream & os)
{
const std::locale & fixLoc = std::locale("C");
os.imbue(fixLoc);
os << "ply\n";
if (isBinary) os << ((isBigEndian) ? "format binary_big_endian 1.0" : "format binary_little_endian 1.0") << "\n";
else os << "format ascii 1.0\n";
for (const auto & comment : comments) os << "comment " << comment << "\n";
for (auto & e : elements)
{
os << "element " << e.name << " " << e.size << "\n";
for (const auto & p : e.properties)
{
if (p.isList)
{
os << "property list " << PropertyTable[p.listType].str << " "
<< PropertyTable[p.propertyType].str << " " << p.name << "\n";
}
else
{
os << "property " << PropertyTable[p.propertyType].str << " " << p.name << "\n";
}
}
}
os << "end_header\n";
}
// Returns the size (in bytes)
std::shared_ptr<PlyData> PlyFile::PlyFileImpl::request_properties_from_element(const std::string & elementKey, const std::initializer_list<std::string> propertyKeys)
{
// All requested properties in the userDataTable share the same cursor (thrown into the same flat array)
ParsingHelper helper;
helper.data = std::make_shared<PlyData>();
helper.data->count = 0;
helper.data->t = Type::INVALID;
helper.cursor = std::make_shared<PlyCursor>();
helper.cursor->byteOffset = 0;
helper.cursor->totalSizeBytes = 0;
if (elements.size() == 0) throw std::runtime_error("parsed header had no elements defined. malformed file?");
if (!propertyKeys.size()) throw std::invalid_argument("`propertyKeys` argument is empty");
if (elementKey.size() == 0) throw std::invalid_argument("`elementKey` argument is empty");
const int elementIndex = find_element(elementKey, elements);
// Sanity check if the user requested element is in the pre-parsed header
if (elementIndex >= 0)
{
// We found the element
const PlyElement & element = elements[elementIndex];
helper.data->count = element.size;
// Find each of the keys
for (auto key : propertyKeys)
{
const int propertyIndex = find_property(key, element.properties);
if (propertyIndex >= 0)
{
// We found the property
const PlyProperty & property = element.properties[propertyIndex];
helper.data->t = property.propertyType; // hmm....
auto result = userData.insert(std::pair<std::string, ParsingHelper>(make_key(element.name, property.name), helper));
if (result.second == false) throw std::invalid_argument("element-property key has already been requested: " + make_key(element.name, property.name));
}
else throw std::invalid_argument("one of the property keys was not found in the header: " + key);
}
}
else throw std::invalid_argument("the element key was not found in the header: " + elementKey);
return helper.data;
}
void PlyFile::PlyFileImpl::add_properties_to_element(const std::string & elementKey, const std::initializer_list<std::string> propertyKeys, const Type type, const size_t count, uint8_t * data, const Type listType, const size_t listCount)
{
ParsingHelper helper;
helper.data = std::make_shared<PlyData>();
helper.data->count = count;
helper.data->t = type;
helper.data->buffer = Buffer(data);
helper.cursor = std::make_shared<PlyCursor>();
helper.cursor->byteOffset = 0;
helper.cursor->totalSizeBytes = 0;
auto create_property_on_element = [&](PlyElement & e)
{
for (auto key : propertyKeys)
{
PlyProperty newProp = (listType == Type::INVALID) ? PlyProperty(type, key) : PlyProperty(listType, type, key, listCount);
/* auto result = */userData.insert(std::pair<std::string, ParsingHelper>(make_key(elementKey, key), helper));
e.properties.push_back(newProp);
}
};
int idx = find_element(elementKey, elements);
if (idx >= 0)
{
PlyElement & e = elements[idx];
create_property_on_element(e);
}
else
{
PlyElement newElement = (listType == Type::INVALID) ? PlyElement(elementKey, count / propertyKeys.size()) : PlyElement(elementKey, count / listCount);
create_property_on_element(newElement);
elements.push_back(newElement);
}
}
void PlyFile::PlyFileImpl::parse_data(std::istream & is, bool firstPass)
{
std::function<size_t(const Type t, void * dest, size_t & destOffset, std::istream & is)> read;
std::function<size_t(const PlyProperty & p, std::istream & is)> skip;
const auto start = is.tellg();
if (isBinary)
{
read = [&](const Type t, void * dest, size_t & destOffset, std::istream & _is) { return read_property_binary(t, dest, destOffset, _is); };
skip = [&](const PlyProperty & p, std::istream & _is) { return skip_property_binary(p, _is); };
}
else
{
read = [&](const Type t, void * dest, size_t & destOffset, std::istream & _is) { return read_property_ascii(t, dest, destOffset, _is); };
skip = [&](const PlyProperty & p, std::istream & _is) { return skip_property_ascii(p, _is); };
}
for (auto & element : elements)
{
for (size_t count = 0; count < element.size; ++count)
{
for (auto & property : element.properties)
{
auto cursorIt = userData.find(make_key(element.name, property.name));
if (cursorIt != userData.end())
{
auto & helper = cursorIt->second;
if (!firstPass)
{
if (property.isList)
{
size_t listSize = 0;
size_t dummyCount = 0;
read(property.listType, &listSize, dummyCount, is);
for (size_t i = 0; i < listSize; ++i)
{
read(property.propertyType, (helper.data->buffer.get() + helper.cursor->byteOffset), helper.cursor->byteOffset, is);
}
}
else
{
read(property.propertyType, (helper.data->buffer.get() + helper.cursor->byteOffset), helper.cursor->byteOffset, is);
}
}
else
{
helper.cursor->totalSizeBytes += skip(property, is);
}
}
else
{
skip(property, is);
}
}
}
}
// Reset istream reader to the beginning
if (firstPass) is.seekg(start, is.beg);
}
///////////////////////////////////
// Pass-Through Public Interface //
///////////////////////////////////
PlyFile::PlyFile() { impl.reset(new PlyFileImpl()); };
PlyFile::~PlyFile() { };
bool PlyFile::parse_header(std::istream & is) { return impl->parse_header(is); }
void PlyFile::read(std::istream & is) { return impl->read(is); }
void PlyFile::write(std::ostream & os, bool isBinary) { return impl->write(os, isBinary); }
std::vector<PlyElement> PlyFile::get_elements() const { return impl->elements; }
std::vector<std::string> & PlyFile::get_comments() { return impl->comments; }
std::vector<std::string> PlyFile::get_info() const { return impl->objInfo; }
std::shared_ptr<PlyData> PlyFile::request_properties_from_element(const std::string & elementKey, const std::initializer_list<std::string> propertyKeys)
{
return impl->request_properties_from_element(elementKey, propertyKeys);
}
void PlyFile::add_properties_to_element(const std::string & elementKey, const std::initializer_list<std::string> propertyKeys, const Type type, const size_t count, uint8_t * data, const Type listType, const size_t listCount)
{
return impl->add_properties_to_element(elementKey, propertyKeys, type, count, data, listType, listCount);
}

Wyświetl plik

@ -1,119 +0,0 @@
// This software is in the public domain. Where that dedication is not
// recognized, you are granted a perpetual, irrevocable license to copy,
// distribute, and modify this file as you see fit.
// Authored in 2015 by Dimitri Diakopoulos (http://www.dimitridiakopoulos.com)
// https://github.com/ddiakopoulos/tinyply
// Version 2.0
#ifndef tinyply_h
#define tinyply_h
#include <vector>
#include <string>
#include <stdint.h>
#include <sstream>
#include <memory>
#include <map>
namespace tinyply
{
enum class Type : uint8_t
{
INVALID,
INT8,
UINT8,
INT16,
UINT16,
INT32,
UINT32,
FLOAT32,
FLOAT64
};
struct PropertyInfo
{
int stride;
std::string str;
};
static std::map<Type, PropertyInfo> PropertyTable
{
{ Type::INT8,{ 1, "char" } },
{ Type::UINT8,{ 1, "uchar" } },
{ Type::INT16,{ 2, "short" } },
{ Type::UINT16,{ 2, "ushort" } },
{ Type::INT32,{ 4, "int" } },
{ Type::UINT32,{ 4, "uint" } },
{ Type::FLOAT32,{ 4, "float" } },
{ Type::FLOAT64,{ 8, "double" } },
{ Type::INVALID,{ 0, "INVALID" } }
};
class Buffer
{
uint8_t * alias{ nullptr };
struct delete_array { void operator()(uint8_t * p) { delete[] p; } };
std::unique_ptr<uint8_t, decltype(Buffer::delete_array())> data;
size_t size;
public:
Buffer() {};
Buffer(const size_t size) : data(new uint8_t[size], delete_array()), size(size) { alias = data.get(); } // allocating
Buffer(uint8_t * ptr) { alias = ptr; } // non-allocating, fixme: set size?
uint8_t * get() { return alias; }
size_t size_bytes() const { return size; }
};
struct PlyData
{
Type t;
size_t count;
Buffer buffer;
};
struct PlyProperty
{
PlyProperty(std::istream & is);
PlyProperty(Type type, std::string & _name) : name(_name), propertyType(type) {}
PlyProperty(Type list_type, Type prop_type, std::string & _name, int list_count) : name(_name), propertyType(prop_type), isList(true), listType(list_type), listCount(list_count) {}
std::string name;
Type propertyType;
bool isList{ false };
Type listType{ Type::INVALID };
int listCount{ 0 };
};
struct PlyElement
{
PlyElement(std::istream & istream);
PlyElement(const std::string & _name, size_t count) : name(_name), size(count) {}
std::string name;
size_t size;
std::vector<PlyProperty> properties;
};
struct PlyFile
{
struct PlyFileImpl;
std::unique_ptr<PlyFileImpl> impl;
PlyFile();
~PlyFile();
bool parse_header(std::istream & is);
void read(std::istream & is);
void write(std::ostream & os, bool isBinary);
std::vector<PlyElement> get_elements() const;
std::vector<std::string> & get_comments();
std::vector<std::string> get_info() const;
std::shared_ptr<PlyData> request_properties_from_element(const std::string & elementKey, const std::initializer_list<std::string> propertyKeys);
void add_properties_to_element(const std::string & elementKey, const std::initializer_list<std::string> propertyKeys, const Type type, const size_t count, uint8_t * data, const Type listType, const size_t listCount);
};
} // namesapce tinyply
#endif // tinyply_h