Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] feature/python_module : Adding libpointmatcher's Python bindings (#222) #397

Merged
merged 32 commits into from
Nov 2, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3e6227b
Adding libpointmatcher's Python bindings
aguenette Aug 6, 2020
b80b5ac
Merge branch 'master' into feature/python_module
aguenette Aug 12, 2020
c519c15
Improved CMakeLists.txt
aguenette Aug 14, 2020
9f4d0c9
Adding the compilation tutorial to the documentation.
aguenette Aug 14, 2020
6b909ea
Fixed error in CMakeLists.txt.
simonpierredeschenes Aug 15, 2020
8170e5d
Added the IO module
aguenette Aug 18, 2020
35926a8
Fixed icp_advance_api.py and icp_customized.py examples
aguenette Aug 18, 2020
ee50bb4
Change C++ examples base type from float to double
aguenette Aug 18, 2020
6ccdc71
Change the way that vectors are bonded
aguenette Aug 18, 2020
5c3a25c
Fixed overloaded methods ambiguities based on constness
aguenette Aug 18, 2020
1c0bcaa
Improved the CMakeLists.txt
aguenette Aug 21, 2020
5c011a9
Forgot to name the members of PLYElement.
aguenette Aug 21, 2020
2969fa6
Improved compilation's tutorial
aguenette Aug 21, 2020
a03e81a
Added 3 new examples
aguenette Aug 21, 2020
d550d13
Removed a pure virtual method that didn't have to be mapped
aguenette Aug 21, 2020
61517b3
Fixed PointMatcher get() method
aguenette Aug 21, 2020
073f2a4
Replaced double type declaration to ScalarType type declaration
aguenette Aug 25, 2020
ed2a295
Moved header's #include directive to source
aguenette Aug 25, 2020
c724bd2
Renamed #include directive
aguenette Aug 25, 2020
4ba0b15
Removed unnecessary namespace aliases
aguenette Aug 25, 2020
dbf15ed
Moved header's aliases declaration to source
aguenette Aug 25, 2020
da515b9
Named members and parameters that were forgotten
aguenette Aug 25, 2020
c900da2
Renamed files to conform to the Python naming convention
aguenette Aug 25, 2020
d463b11
Made std::vector and std::map opaque
aguenette Aug 25, 2020
9936878
Improved the Python examples
aguenette Aug 26, 2020
dcde397
Added the usage tutorial
aguenette Aug 26, 2020
3fa79e3
Minor corrections in Python documentation.
simonpierredeschenes Aug 30, 2020
d662059
Merge branch 'master' of github.com:AlexandreG87/libpointmatcher into…
aguenette Oct 20, 2020
d937274
Merge branch 'master' into feature/python_module
pomerlef Oct 27, 2020
e9dc721
Revert change C++ examples base type from float to double.
aguenette Oct 29, 2020
956aadb
Reorganization of the namespace structure
aguenette Nov 1, 2020
0c1c68f
Merge branch 'master' into feature/python_module
aguenette Nov 2, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions pointmatcher/IO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1780,9 +1780,9 @@ template
class PointMatcherIO<double>::PLYElement;

template
class PointMatcherIO<float>::PLYProperty;
struct PointMatcherIO<float>::PLYProperty;
aguenette marked this conversation as resolved.
Show resolved Hide resolved
template
class PointMatcherIO<double>::PLYProperty;
struct PointMatcherIO<double>::PLYProperty;


template <typename T>
Expand Down
1 change: 1 addition & 0 deletions python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ set(PYBIND11_SOURCES
pointmatcher/impls/transformationcheckers_impl.cpp
pointmatcher/impl.cpp
pointmatcher/inspector.cpp
pointmatcher/io.cpp
pointmatcher/matcher.cpp
pointmatcher/matches.cpp
pointmatcher/outlierfilter.cpp
Expand Down
3 changes: 3 additions & 0 deletions python/modules/pointmatcher_module.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
#include "pointmatcher_module.h"

#include "pointmatcher/pointmatcher.h"
#include "pointmatcher/impl.h"
#include "pointmatcher/io.h"

namespace pointmatcher
{
void pybindPointMatcherModule(py::module& p_module)
{
py::module pointmatcherModule = p_module.def_submodule("pointmatcher");
pybindPointMatcher(pointmatcherModule);
pybindIO(pointmatcherModule);
pybindImpl(pointmatcherModule);
}
}
217 changes: 217 additions & 0 deletions python/pointmatcher/io.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
#include "io.h"

#include "pointmatcher/IO.h"

PYBIND11_MAKE_OPAQUE(PointMatcherIO<ScalarType>::FileInfoVector)

namespace pointmatcher
{
void pybindIO(py::module& p_module)
{
using PMIO = PointMatcherIO<ScalarType>;
aguenette marked this conversation as resolved.
Show resolved Hide resolved
py::class_<PMIO> pyPointMatcherIO(p_module, "PointMatcherIO", "IO Functions and classes that are dependant on scalar type are defined in this templatized class");

pyPointMatcherIO
.def(py::init<>())
.def_static("getColLabel", &PMIO::getColLabel, py::arg("label"), py::arg("row"),"convert a descriptor label to an appropriate sub-label");

using PMPropTypes = PMIO::PMPropTypes;
py::enum_<PMPropTypes>(pyPointMatcherIO, "PMPropTypes", "Type of information in a DataPoints. Each type is stored in its own dense matrix.")
.value("FEATURE", PMPropTypes::FEATURE)
.value("DESCRIPTOR", PMPropTypes::DESCRIPTOR)
.value("TIME", PMPropTypes::TIME)
.value("UNSUPPORTED", PMPropTypes::UNSUPPORTED);

using SupportedLabel = PMIO::SupportedLabel;
py::class_<SupportedLabel>(pyPointMatcherIO, "SupportedLabel", "Structure containing all information required to map external information to PointMatcher internal representation")
.def_readwrite("internalName", &SupportedLabel::internalName, "name used in PointMatcher")
.def_readwrite("externalName", &SupportedLabel::externalName, "name used in external format")
.def_readwrite("type", &SupportedLabel::type, "type of information in PointMatcher")

.def(py::init<const std::string&, const std::string&, const PMPropTypes&>(), py::arg("internalName"), py::arg("externalName"), py::arg("type"));

using SupportedLabels = PMIO::SupportedLabels;
py::bind_vector<SupportedLabels>(pyPointMatcherIO, "SupportedLabels", "Vector of supported labels in PointMatcher and their external names");

using GenericInputHeader = PMIO::GenericInputHeader;
py::class_<GenericInputHeader>(pyPointMatcherIO, "GenericInputHeader", "Helper structure designed to parse file headers")
.def_readwrite("name", &GenericInputHeader::name)
.def_readwrite("matrixRowId", &GenericInputHeader::matrixRowId)
.def_readwrite("matrixType", &GenericInputHeader::matrixType)
.def(py::init<>())
.def(py::init<const std::string&>(), py::arg("name"));

aguenette marked this conversation as resolved.
Show resolved Hide resolved
pyPointMatcherIO.def_static("getSupportedExternalLabels", &PMIO::getSupportedExternalLabels, R"pbdoc(
Vector containing the mapping of all external names to PointMatcher representation.
The order is important (i.e., nx before ny). This can also be used to remap 1D descriptor name to a better one.
)pbdoc");

using LabelGenerator = PMIO::LabelGenerator;
py::class_<LabelGenerator>(pyPointMatcherIO, "LabelGenerator")
.def(py::init<>())
.def("add", (void (LabelGenerator::*)(const std::string)) &LabelGenerator::add, py::arg("internalName"), "add a name to the vector of labels. If already there, will increament the dimension.")
.def("add", (void (LabelGenerator::*)(const std::string, const unsigned int)) &LabelGenerator::add, py::arg("internalName"), py::arg("dim"), "add a name to the vector of labels with its dimension.")
.def("getLabels", &LabelGenerator::getLabels, "Return the vector of labels used to build a DataPoints");

pyPointMatcherIO
.def_static("loadCSV", (DataPoints (*)(const std::string&)) &PMIO::loadCSV, py::arg("fileName"))
.def_static("saveCSV", (void (*)(const DataPoints&, const std::string&)) &PMIO::saveCSV, py::arg("data"), py::arg("fileName"));

using SupportedVTKDataTypes = PMIO::SupportedVTKDataTypes;
py::enum_<SupportedVTKDataTypes>(pyPointMatcherIO, "SupportedVTKDataTypes", "Enumeration of legacy VTK data types that can be parsed")
.value("POLYDATA", SupportedVTKDataTypes::POLYDATA)
.value("UNSTRUCTURED_GRID", SupportedVTKDataTypes::UNSTRUCTURED_GRID);

using SplitTime = PMIO::SplitTime;
py::class_<SplitTime>(pyPointMatcherIO, "SplitTime", "Storage for time loaded separatly")
.def_readwrite("isHigh32Found", &SplitTime::isHigh32Found, "was the high 32bits found in the file")
.def_readwrite("isLow32Found", &SplitTime::isLow32Found, "was the low 32bits found in the file")
.def_readwrite("high32", &SplitTime::high32, "Matrix containing file data representing the high 32 bits")
.def_readwrite("low32", &SplitTime::low32, "Matrix containing file data representing the low 32 bits")
.def(py::init<>());

pyPointMatcherIO
.def_static("loadVTK", (DataPoints (*)(const std::string&)) &PMIO::loadVTK, py::arg("fileName"))
.def_static("saveVTK", (void (*)(const DataPoints&, const std::string&, bool)) &PMIO::saveVTK, py::arg("data"), py::arg("fileName"), py::arg("binary") = false)

.def_static("loadPLY", (DataPoints (*)(const std::string&)) &PMIO::loadPLY, py::arg("fileName"))
.def_static("savePLY", (void (*)(const DataPoints&, const std::string&)) &PMIO::savePLY, py::arg("data"), py::arg("fileName"), "save datapoints to PLY point cloud format")

.def_static("loadPCD", (DataPoints (*)(const std::string&)) &PMIO::loadPCD, py::arg("fileName"))
.def_static("savePCD", (void (*)(const DataPoints&, const std::string&)) &PMIO::savePCD, py::arg("data"), py::arg("fileName"), "save datapoints to PCD point cloud format");

using FileInfo = PMIO::FileInfo;
using Vector3 = FileInfo::Vector3;
py::class_<FileInfo>(pyPointMatcherIO, "FileInfo", "Information to exploit a reading from a file using this library. Fields might be left blank if unused.")
.def_readwrite("readingFileName", &FileInfo::readingFileName, "file name of the reading point cloud")
.def_readwrite("referenceFileName", &FileInfo::referenceFileName, "file name of the reference point cloud")
.def_readwrite("configFileName", &FileInfo::configFileName, "file name of the yaml configuration")
.def_readwrite("initialTransformation", &FileInfo::initialTransformation, "matrix of initial estimate transform")
.def_readwrite("groundTruthTransformation", &FileInfo::groundTruthTransformation, "matrix of the ground-truth transform")
.def_readwrite("gravity", &FileInfo::gravity, "gravity vector")

.def(py::init<const std::string&, const std::string&, const std::string&, const TransformationParameters&, const TransformationParameters&, const Vector&>(),
py::arg("readingPath") = "",
py::arg(" referencePath") = "",
py::arg("configFileName") = "",
py::arg("initialTransformation") = TransformationParameters(),
py::arg("groundTruthTransformation") = TransformationParameters(),
py::arg("gravity") = Vector3::Zero(),
"Constructor, leave fields blank if unused");

using FileInfoVector = PMIO::FileInfoVector;
py::bind_vector<FileInfoVector>(pyPointMatcherIO, "FileInfoVector", "A vector of file info, to be used in batch processing")
.def(py::init<>(), "Empty constructor")
.def(py::init<const std::string&, std::string, std::string>(), py::arg("fileName"), py::arg("dataPath") = "", py::arg("configPath") = "",
R"pbdoc(
Load a vector of FileInfo from a CSV file.

@param fileName name of the CSV file
@param dataPath path relative to which the point cloud CSV or VTK will be resolved
@param configPath path relative to which the yaml configuration files will be resolved

The first line of the CSV file must contain a header. The supported tags are:
- reading: file name of the reading point cloud
- reference: file name of the reference point cloud
- config: file name of the YAML configuration of the ICP chain
- iTxy: initial transformation, coordinate x,y
- gTxy: ground-truth transformation, coordinate x,y
Note that the header must at least contain "reading".
)pbdoc");

using CsvDescriptor = PMIO::CsvDescriptor;
py::class_<CsvDescriptor>(pyPointMatcherIO, "CsvDescriptor", "A structure to hold information about descriptors contained in a CSV file")
.def(py::init<>())
.def_readwrite("name", &CsvDescriptor::name, "name of descriptor")
.def_readwrite("start_col", &CsvDescriptor::start_col, "column number at which descriptor starts")
.def_readwrite("span", &CsvDescriptor::span, "number of columns spanned by descriptor");

pyPointMatcherIO.def_static("plyPropTypeValid", &PMIO::plyPropTypeValid, "Check that property defined by type is a valid PLY type note: type must be lowercase");

using PLYProperty = PMIO::PLYProperty;
py::class_<PLYProperty>(pyPointMatcherIO, "PLYProperty", "Interface for PLY property")
.def_readwrite("name", &PLYProperty::name, "name of PLY property")
.def_readwrite("type", &PLYProperty::type, "type of PLY property")
.def_readwrite("idx_type", &PLYProperty::idx_type, "for list properties, type of number of elements")
.def_readwrite("pos", &PLYProperty::pos, "index of the property in element")
.def_readwrite("is_list", &PLYProperty::is_list, "member is true of property is a list")
.def_readwrite("pmType", &PLYProperty::pmType, "type of information in PointMatcher")
.def_readwrite("pmRowID", &PLYProperty::pmRowID, "row id used in a DataPoints")

.def(py::init<>(), "Default constructor. If used member values must be filled later.")
.def(py::init<const std::string&, const std::string&, const unsigned>(), py::arg("type"), py::arg("name"), py::arg("pos"), "regular property")
.def(py::init<const std::string&, const std::string&, const std::string&, const unsigned>(), py::arg("idx_type"), py::arg("type"), py::arg("name"), py::arg("pos"), "list property")

.def("__eq__", &PLYProperty::operator==, "compare with other property");

using PLYDescPropMap = PMIO::PLYDescPropMap;
py::bind_map<PLYDescPropMap>(pyPointMatcherIO, "PLYDescPropMap", "Map from a descriptor name to a list PLY property\nex: \"normals\" -> nx, ny ,nz");

using PLYProperties = PMIO::PLYProperties;
py::bind_vector<PLYProperties>(pyPointMatcherIO, "PLYProperties", "Vector of properties specific to PLY files");

using PLYElement = PMIO::PLYElement;
py::class_<PLYElement>(pyPointMatcherIO, "PLYElement", "Interface for all PLY elements.")
.def_readwrite("", &PLYElement::name)
.def_readwrite("", &PLYElement::num)
.def_readwrite("", &PLYElement::total_props)
.def_readwrite("", &PLYElement::offset)
.def_readwrite("", &PLYElement::properties)
.def_readwrite("", &PLYElement::nbFeatures)
.def_readwrite("", &PLYElement::nbDescriptors)

.def(py::init<const std::string&, const unsigned, const unsigned>(), py::arg("name"), py::arg("num"), py::arg("offset"),
R"pbdoc(
PLY Element constructor

@param name name of the ply element (case-sensitive)
@param num number of times the element appears in the file
@param offset if there are several elements, the line offset at which this element begins. Note that, as of writing, only one (vertex) element is supported.

This object holds information about a PLY element contained in the file.
It is filled out when reading the header and used when parsing the data.
)pbdoc")
.def("__eq__", &PLYElement::operator==, "comparison operator for elements");

using PLYVertex = PMIO::PLYVertex;
py::class_<PLYVertex, PLYElement>(pyPointMatcherIO, "PLYVertex", "Implementation of PLY vertex element")
.def(py::init<const unsigned, const unsigned>(), py::arg("num"), py::arg("offset"),
R"pbdoc(
Constructor

@param num number of times the element appears in the file
@param offset if there are several elements, the line offset at which this element begins. Note that, as of writing, only one (vertex) element is supported.

Implementation of PLY element interface for the vertex element
)pbdoc");

// FIXME : Generate undefined symbol error for "elementSupported" method when importing the module
// using PLYElementF = PMIO::PLYElementF;
// py::class_<PLYElementF>(pyPointMatcherIO, "PLYElementF", "Factory for PLY elements")
// .def("elementSupported", &PLYElementF::elementSupported, py::arg("elem_name"), "returns true if element named elem_name is supported by this parser")
// .def_static("createElement", &PLYElementF::createElement, py::arg("elem_name"), py::arg("elem_num"), py::arg("offset"), "factory function, build element defined by name with elem_num elements");

using PCDproperty = PMIO::PCDproperty;
py::class_<PCDproperty>(pyPointMatcherIO, "PCDproperty", "Information for a PCD property")
.def_readwrite("field", &PCDproperty::field, "Name of the property")
.def_readwrite("size", &PCDproperty::size, "Size of the property in bytes")
.def_readwrite("type", &PCDproperty::type, "Type: I: signed, U: unsigned, F: float")
.def_readwrite("count", &PCDproperty::count, "number of dimension")
.def_readwrite("pmType", &PCDproperty::pmType, "type of information in PointMatcher")
.def_readwrite("pmRowID", &PCDproperty::pmRowID, "row id used in a DataPoints")

.def(py::init<>());

using PCDheader = PMIO::PCDheader;
py::class_<PCDheader>(pyPointMatcherIO, "PCDheader", "All information contained in the header of a PCD file")
.def_readwrite("version", &PCDheader::version, "Version of the PCD file used")
.def_readwrite("properties", &PCDheader::properties, "vector of properties")
.def_readwrite("width", &PCDheader::width, "width of sensor matrix")
.def_readwrite("height", &PCDheader::height, "height of sensor matrix")
.def_readwrite("viewPoint", &PCDheader::viewPoint, "not used")
.def_readwrite("nbPoints", &PCDheader::nbPoints, "number of points, same as width*height")
.def_readwrite("dataType", &PCDheader::dataType, "ascii or binary")

.def(py::init<>());
}
}
11 changes: 11 additions & 0 deletions python/pointmatcher/io.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifndef PYTHON_POINTMATCHER_IO_H
#define PYTHON_POINTMATCHER_IO_H

#include "pypointmatcher_helper.h"

namespace pointmatcher
{
void pybindIO(py::module& p_module);
}

#endif //PYTHON_POINTMATCHER_IO_H