Skip to content

Commit

Permalink
Adding in memory support for xyz files (#5866)
Browse files Browse the repository at this point in the history
For Eigen datatypes
  • Loading branch information
samypr100 authored Dec 22, 2023
1 parent 663d698 commit d7a2cf6
Show file tree
Hide file tree
Showing 11 changed files with 361 additions and 19 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
- Expose Near Clip + Far Clip parameters to setup_camera in OffscreenRenderer (#6520)
- Add Doppler ICP in tensor registration pipeline (PR #5237)
- Rename master branch to main.

- Support in memory loading of XYZ files

## 0.13

Expand Down
96 changes: 94 additions & 2 deletions cpp/open3d/io/PointCloudIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ static const std::unordered_map<
{"pts", ReadPointCloudFromPTS},
};

static const std::unordered_map<
std::string,
std::function<bool(const unsigned char *,
const size_t,
geometry::PointCloud &,
const ReadPointCloudOption &)>>
in_memory_to_pointcloud_read_function{
{"mem::xyz", ReadPointCloudInMemoryFromXYZ},
};

static const std::unordered_map<
std::string,
std::function<bool(const std::string &,
Expand All @@ -46,6 +56,16 @@ static const std::unordered_map<
{"pts", WritePointCloudToPTS},
};

static const std::unordered_map<
std::string,
std::function<bool(unsigned char *&,
size_t &,
const geometry::PointCloud &,
const WritePointCloudOption &)>>
in_memory_to_pointcloud_write_function{
{"mem::xyz", WritePointCloudInMemoryToXYZ},
};

std::shared_ptr<geometry::PointCloud> CreatePointCloudFromFile(
const std::string &filename,
const std::string &format,
Expand All @@ -55,6 +75,17 @@ std::shared_ptr<geometry::PointCloud> CreatePointCloudFromFile(
return pointcloud;
}

std::shared_ptr<geometry::PointCloud> CreatePointCloudFromMemory(
const unsigned char *buffer,
const size_t length,
const std::string &format,
bool print_progress) {
auto pointcloud = std::make_shared<geometry::PointCloud>();
ReadPointCloud(buffer, length, *pointcloud,
{format, true, true, print_progress});
return pointcloud;
}

bool ReadPointCloud(const std::string &filename,
geometry::PointCloud &pointcloud,
const ReadPointCloudOption &params) {
Expand Down Expand Up @@ -104,6 +135,38 @@ bool ReadPointCloud(const std::string &filename,
p.update_progress = progress_updater;
return ReadPointCloud(filename, pointcloud, p);
}
bool ReadPointCloud(const unsigned char *buffer,
const size_t length,
geometry::PointCloud &pointcloud,
const ReadPointCloudOption &params) {
std::string format = params.format;
if (format == "auto") {
utility::LogWarning(
"Read geometry::PointCloud failed: unknown format for "
"(format: {}).",
params.format);
return false;
}

utility::LogDebug("Format {}", params.format);

auto map_itr = in_memory_to_pointcloud_read_function.find(format);
if (map_itr == in_memory_to_pointcloud_read_function.end()) {
utility::LogWarning(
"Read geometry::PointCloud failed: unknown format for "
"(format: {}).",
params.format);
return false;
}
bool success = map_itr->second(buffer, length, pointcloud, params);
utility::LogDebug("Read geometry::PointCloud: {} vertices.",
pointcloud.points_.size());
if (params.remove_nan_points || params.remove_infinite_points) {
pointcloud.RemoveNonFinitePoints(params.remove_nan_points,
params.remove_infinite_points);
}
return success;
}

bool WritePointCloud(const std::string &filename,
const geometry::PointCloud &pointcloud,
Expand All @@ -126,14 +189,17 @@ bool WritePointCloud(const std::string &filename,
}
bool WritePointCloud(const std::string &filename,
const geometry::PointCloud &pointcloud,
const std::string &file_format /* = "auto"*/,
bool write_ascii /* = false*/,
bool compressed /* = false*/,
bool print_progress) {
WritePointCloudOption p;
p.write_ascii = WritePointCloudOption::IsAscii(write_ascii);
p.compressed = WritePointCloudOption::Compressed(compressed);
std::string format =
utility::filesystem::GetFileExtensionInLowerCase(filename);
std::string format = file_format;
if (format == "auto") {
format = utility::filesystem::GetFileExtensionInLowerCase(filename);
}
utility::ConsoleProgressUpdater progress_updater(
std::string("Writing ") + utility::ToUpper(format) +
" file: " + filename,
Expand All @@ -142,5 +208,31 @@ bool WritePointCloud(const std::string &filename,
return WritePointCloud(filename, pointcloud, p);
}

bool WritePointCloud(unsigned char *&buffer,
size_t &length,
const geometry::PointCloud &pointcloud,
const WritePointCloudOption &params) {
std::string format = params.format;
if (format == "auto") {
utility::LogWarning(
"Write geometry::PointCloud failed: unknown format for "
"(format: {}).",
params.format);
return false;
}
auto map_itr = in_memory_to_pointcloud_write_function.find(format);
if (map_itr == in_memory_to_pointcloud_write_function.end()) {
utility::LogWarning(
"Write geometry::PointCloud failed: unknown format {}.",
format);
return false;
}

bool success = map_itr->second(buffer, length, pointcloud, params);
utility::LogDebug("Write geometry::PointCloud: {} vertices.",
pointcloud.points_.size());
return success;
}

} // namespace io
} // namespace open3d
59 changes: 58 additions & 1 deletion cpp/open3d/io/PointCloudIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ std::shared_ptr<geometry::PointCloud> CreatePointCloudFromFile(
const std::string &format = "auto",
bool print_progress = false);

/// Factory function to create a pointcloud from memory
/// Return an empty pointcloud if fail to read from buffer.
std::shared_ptr<geometry::PointCloud> CreatePointCloudFromMemory(
const unsigned char *buffer,
const size_t length,
const std::string &format,
bool print_progress = false);

/// \struct ReadPointCloudOption
/// \brief Optional parameters to ReadPointCloud
struct ReadPointCloudOption {
Expand All @@ -43,6 +51,7 @@ struct ReadPointCloudOption {
};
/// Specifies what format the contents of the file are (and what loader to
/// use), default "auto" means to go off of file extension.
/// Note: "auto" is incompatible when reading directly from memory.
std::string format;
/// Whether to remove all points that have nan
bool remove_nan_points;
Expand All @@ -66,6 +75,15 @@ bool ReadPointCloud(const std::string &filename,
geometry::PointCloud &pointcloud,
const ReadPointCloudOption &params = {});

/// The general entrance for reading a PointCloud from memory
/// The function calls read functions based on the format.
/// See \p ReadPointCloudOption for additional options you can pass.
/// \return return true if the read function is successful, false otherwise.
bool ReadPointCloud(const unsigned char *buffer,
const size_t length,
geometry::PointCloud &pointcloud,
const ReadPointCloudOption &params = {});

/// \struct WritePointCloudOption
/// \brief Optional parameters to WritePointCloud
struct WritePointCloudOption {
Expand All @@ -74,11 +92,13 @@ struct WritePointCloudOption {
WritePointCloudOption(
// Attention: when you update the defaults, update the docstrings in
// pybind/io/class_io.cpp
std::string format = "auto",
IsAscii write_ascii = IsAscii::Binary,
Compressed compressed = Compressed::Uncompressed,
bool print_progress = false,
std::function<bool(double)> update_progress = {})
: write_ascii(write_ascii),
: format(format),
write_ascii(write_ascii),
compressed(compressed),
print_progress(print_progress),
update_progress(update_progress){};
Expand All @@ -91,10 +111,25 @@ struct WritePointCloudOption {
compressed(Compressed(compressed)),
print_progress(print_progress),
update_progress(update_progress){};
// for compatibility
WritePointCloudOption(std::string format,
bool write_ascii,
bool compressed = false,
bool print_progress = false,
std::function<bool(double)> update_progress = {})
: format(format),
write_ascii(IsAscii(write_ascii)),
compressed(Compressed(compressed)),
print_progress(print_progress),
update_progress(update_progress){};
WritePointCloudOption(std::function<bool(double)> up)
: WritePointCloudOption() {
update_progress = up;
};
/// Specifies what format the contents of the file are (and what writer to
/// use), default "auto" means to go off of file extension.
/// Note: "auto" is incompatible when reading directly from memory.
std::string format;
/// Whether to save in Ascii or Binary. Some savers are capable of doing
/// either, other ignore this.
IsAscii write_ascii;
Expand All @@ -120,14 +155,36 @@ bool WritePointCloud(const std::string &filename,
const geometry::PointCloud &pointcloud,
const WritePointCloudOption &params = {});

/// The general entrance for writing a PointCloud to memory
/// The function calls write functions based on the format.
/// WARNING: buffer gets initialized by WritePointCloud, you need to
/// delete it when finished when ret is true
/// See \p WritePointCloudOption for additional options you can pass.
/// \return return true if the write function is
/// successful, false otherwise.
bool WritePointCloud(unsigned char *&buffer,
size_t &length,
const geometry::PointCloud &pointcloud,
const WritePointCloudOption &params = {});

bool ReadPointCloudFromXYZ(const std::string &filename,
geometry::PointCloud &pointcloud,
const ReadPointCloudOption &params);

bool ReadPointCloudInMemoryFromXYZ(const unsigned char *buffer,
const size_t length,
geometry::PointCloud &pointcloud,
const ReadPointCloudOption &params);

bool WritePointCloudToXYZ(const std::string &filename,
const geometry::PointCloud &pointcloud,
const WritePointCloudOption &params);

bool WritePointCloudInMemoryToXYZ(unsigned char *&buffer,
size_t &length,
const geometry::PointCloud &pointcloud,
const WritePointCloudOption &params);

bool ReadPointCloudFromXYZN(const std::string &filename,
geometry::PointCloud &pointcloud,
const ReadPointCloudOption &params);
Expand Down
68 changes: 68 additions & 0 deletions cpp/open3d/io/file_format/FileXYZ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
// ----------------------------------------------------------------------------

#include <cstdio>
#include <sstream>

#include "open3d/io/FileFormatIO.h"
#include "open3d/io/PointCloudIO.h"
#include "open3d/utility/FileSystem.h"
#include "open3d/utility/Helper.h"
#include "open3d/utility/Logging.h"
#include "open3d/utility/ProgressReporters.h"

Expand Down Expand Up @@ -54,6 +56,37 @@ bool ReadPointCloudFromXYZ(const std::string &filename,
}
}

bool ReadPointCloudInMemoryFromXYZ(const unsigned char *buffer,
const size_t length,
geometry::PointCloud &pointcloud,
const ReadPointCloudOption &params) {
try {
utility::CountingProgressReporter reporter(params.update_progress);
reporter.SetTotal(static_cast<int64_t>(length));

std::string content(reinterpret_cast<const char *>(buffer), length);
std::istringstream ibs(content);
pointcloud.Clear();
int i = 0;
double x, y, z;
std::string line;
while (std::getline(ibs, line)) {
if (sscanf(line.c_str(), "%lf %lf %lf", &x, &y, &z) == 3) {
pointcloud.points_.push_back(Eigen::Vector3d(x, y, z));
}
if (++i % 1000 == 0) {
reporter.Update(ibs.tellg());
}
}
reporter.Finish();

return true;
} catch (const std::exception &e) {
utility::LogWarning("Read XYZ failed with exception: {}", e.what());
return false;
}
}

bool WritePointCloudToXYZ(const std::string &filename,
const geometry::PointCloud &pointcloud,
const WritePointCloudOption &params) {
Expand Down Expand Up @@ -87,5 +120,40 @@ bool WritePointCloudToXYZ(const std::string &filename,
}
}

bool WritePointCloudInMemoryToXYZ(unsigned char *&buffer,
size_t &length,
const geometry::PointCloud &pointcloud,
const WritePointCloudOption &params) {
try {
utility::CountingProgressReporter reporter(params.update_progress);
reporter.SetTotal(pointcloud.points_.size());

std::string content;
for (size_t i = 0; i < pointcloud.points_.size(); i++) {
const Eigen::Vector3d &point = pointcloud.points_[i];
std::string line = utility::FastFormatString(
"%.10f %.10f %.10f\n", point(0), point(1), point(2));
content.append(line);
if (i % 1000 == 0) {
reporter.Update(i);
}
}
// nothing to report...
if (content.length() == 0) {
reporter.Finish();
return false;
}
length = content.length();
buffer = new unsigned char[length]; // we do this for the caller
std::memcpy(buffer, content.c_str(), length);

reporter.Finish();
return true;
} catch (const std::exception &e) {
utility::LogWarning("Write XYZ failed with exception: {}", e.what());
return false;
}
}

} // namespace io
} // namespace open3d
18 changes: 18 additions & 0 deletions cpp/open3d/utility/Helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,24 @@ inline std::string FormatString(const std::string& format, Args... args) {
buf.get() + size - 1); // We don't want the '\0' inside
};

/// Format string fast (Unix / BSD Only)
template <typename... Args>
inline std::string FastFormatString(const std::string& format, Args... args) {
#ifdef _WIN32
return FormatString(format, args...);
#else
char* buffer = nullptr;
int size_s = asprintf(&buffer, format.c_str(), args...);
if (size_s == -1) {
throw std::runtime_error("Error during formatting.");
}
auto ret = std::string(buffer,
buffer + size_s); // no + 1 since we ignore the \0
std::free(buffer); // asprintf calls malloc
return ret;
#endif // _WIN32
};

void Sleep(int milliseconds);

/// Computes the quotient of x/y with rounding up
Expand Down
Loading

0 comments on commit d7a2cf6

Please sign in to comment.