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

Adding in memory support for xyz files #5866

Merged
merged 10 commits into from
Dec 22, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* Fix printing of tensor in gpu and add validation check for bounds of axis-aligned bounding box (PR #6444)
* Python 3.11 support. bump pybind11 v2.6.2 -> v2.11.1
* Check for support of CUDA Memory Pools at runtime (#4679)
* 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 <iostream>
ssheorey marked this conversation as resolved.
Show resolved Hide resolved

#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);
samypr100 marked this conversation as resolved.
Show resolved Hide resolved
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
Loading