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

Bugfixes and docs improvement of AABB tensor class #6486

Merged
Merged
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)
* Fix `toString`, `CreateFromPoints` methods and improve docs in `AxisAlignedBoundingBox`. 🐛📝

## 0.13

Expand Down
5 changes: 4 additions & 1 deletion cpp/open3d/geometry/BoundingVolume.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ AxisAlignedBoundingBox& AxisAlignedBoundingBox::Scale(
AxisAlignedBoundingBox& AxisAlignedBoundingBox::Rotate(
const Eigen::Matrix3d& rotation, const Eigen::Vector3d& center) {
utility::LogError(
"A rotation of a AxisAlignedBoundingBox would not be axis aligned "
"A rotation of an AxisAlignedBoundingBox would not be axis-aligned "
"anymore, convert it to an OrientedBoundingBox first");
return *this;
}
Expand All @@ -330,6 +330,9 @@ AxisAlignedBoundingBox AxisAlignedBoundingBox::CreateFromPoints(
const std::vector<Eigen::Vector3d>& points) {
AxisAlignedBoundingBox box;
if (points.empty()) {
utility::LogWarning(
"The number of points is 0 when creating axis-aligned bounding "
"box.");
box.min_bound_ = Eigen::Vector3d(0.0, 0.0, 0.0);
box.max_bound_ = Eigen::Vector3d(0.0, 0.0, 0.0);
} else {
Expand Down
9 changes: 8 additions & 1 deletion cpp/open3d/geometry/BoundingVolume.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ class OrientedBoundingBox : public Geometry3D {

/// \class AxisAlignedBoundingBox
///
/// \brief A bounding box that is aligned along the coordinate axes.
/// \brief A bounding box that is aligned along the coordinate axes and defined
/// by the min_bound and max_bound.
///
/// The AxisAlignedBoundingBox uses the coordinate axes for bounding box
/// generation. This means that the bounding box is oriented along the
Expand Down Expand Up @@ -227,14 +228,20 @@ class AxisAlignedBoundingBox : public Geometry3D {
/// extents.
double GetMaxExtent() const { return (max_bound_ - min_bound_).maxCoeff(); }

/// Calculates the percentage position of the given x-coordinate within
/// the x-axis range of this AxisAlignedBoundingBox.
double GetXPercentage(double x) const {
return (x - min_bound_(0)) / (max_bound_(0) - min_bound_(0));
}

/// Calculates the percentage position of the given y-coordinate within
/// the y-axis range of this AxisAlignedBoundingBox.
double GetYPercentage(double y) const {
return (y - min_bound_(1)) / (max_bound_(1) - min_bound_(1));
}

/// Calculates the percentage position of the given z-coordinate within
/// the z-axis range of this AxisAlignedBoundingBox.
double GetZPercentage(double z) const {
return (z - min_bound_(2)) / (max_bound_(2) - min_bound_(2));
}
Expand Down
29 changes: 18 additions & 11 deletions cpp/open3d/t/geometry/BoundingVolume.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@ AxisAlignedBoundingBox::AxisAlignedBoundingBox(const core::Tensor &min_bound,

// Check if the bounding box is valid.
if (Volume() < 0) {
utility::LogError(
"Invalid axis-aligned bounding box. Please make sure all "
"the elements in max bound are larger than min bound.");
utility::LogWarning(
"max_bound {} of bounding box is smaller than min_bound {} in "
"one or more axes. Fix input values to remove this warning.",
max_bound_.ToString(false), min_bound_.ToString(false));
max_bound_ = open3d::core::Maximum(min_bound, max_bound);
min_bound_ = open3d::core::Minimum(min_bound, max_bound);
}
}

Expand Down Expand Up @@ -80,7 +83,7 @@ void AxisAlignedBoundingBox::SetMinBound(const core::Tensor &min_bound) {
if (Volume() < 0) {
utility::LogWarning(
"Invalid axis-aligned bounding box. Please make sure all "
"the elements in min bound are smaller than min bound.");
"the elements in min bound are smaller than max bound.");
min_bound_ = tmp;
}
}
Expand Down Expand Up @@ -110,8 +113,8 @@ void AxisAlignedBoundingBox::SetColor(const core::Tensor &color) {
if (color.Max({0}).To(core::Float64).Item<double>() > 1.0 ||
color.Min({0}).To(core::Float64).Item<double>() < 0.0) {
utility::LogError(
"The color must be in the range [0, 1], but for range [{}, "
"{}].",
"The color must be in the range [0, 1], but found in range "
"[{}, {}].",
color.Min({0}).To(core::Float64).Item<double>(),
color.Max({0}).To(core::Float64).Item<double>());
}
Expand Down Expand Up @@ -220,16 +223,20 @@ core::Tensor AxisAlignedBoundingBox::GetPointIndicesWithinBoundingBox(
}

std::string AxisAlignedBoundingBox::ToString() const {
return fmt::format("AxisAlignedBoundingBox[{}, {}]", GetDtype().ToString(),
return fmt::format("AxisAlignedBoundingBox[{} - {}, {}, {}]",
GetMinBound().ToString(false),
GetMaxBound().ToString(false), GetDtype().ToString(),
GetDevice().ToString());
}

AxisAlignedBoundingBox AxisAlignedBoundingBox::CreateFromPoints(
const core::Tensor &points) {
core::AssertTensorShape(points, {utility::nullopt, 3});
core::AssertTensorDtypes(points, {core::Float32, core::Float64});
if (points.GetLength() <= 3) {
utility::LogWarning("The points number is less than 3.");
if (points.GetLength() <= 0) {
utility::LogWarning(
"The number of points is 0 when creating axis-aligned bounding "
"box.");
return AxisAlignedBoundingBox(points.GetDevice());
} else {
const core::Tensor min_bound = points.Min({0});
Expand Down Expand Up @@ -385,8 +392,8 @@ void OrientedBoundingBox::SetColor(const core::Tensor &color) {
if (color.Max({0}).To(core::Float64).Item<double>() > 1.0 ||
color.Min({0}).To(core::Float64).Item<double>() < 0.0) {
utility::LogError(
"The color must be in the range [0, 1], but for range [{}, "
"{}].",
"The color must be in the range [0, 1], but found in range "
"[{}, {}].",
color.Min({0}).To(core::Float64).Item<double>(),
color.Max({0}).To(core::Float64).Item<double>());
}
Expand Down
29 changes: 21 additions & 8 deletions cpp/open3d/t/geometry/BoundingVolume.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,12 @@ class AxisAlignedBoundingBox : public Geometry, public DrawableGeometry {
/// \param min_bound Tensor with {3,} shape, and type float32 or float64.
void SetMinBound(const core::Tensor &min_bound);

/// \brief Set the max boundof the box.
/// \brief Set the max bound of the box.
/// If the data type of the given tensor differs from the data type of the
/// box, an exception will be thrown.
///
/// If the max bound makes the box invalid, it will not be set to the box.
/// \param min_bound Tensor with {3,} shape, and type float32 or float64.
/// \param max_bound Tensor with {3,} shape, and type float32 or float64.
void SetMaxBound(const core::Tensor &max_bound);

/// \brief Set the color of the box.
Expand Down Expand Up @@ -156,25 +156,32 @@ class AxisAlignedBoundingBox : public Geometry, public DrawableGeometry {
/// Returns the half extent of the bounding box.
core::Tensor GetHalfExtent() const { return GetExtent() / 2; }

/// Returns the maximum extent, i.e. the maximum of X, Y and Z axis'
/// \brief Returns the maximum extent, i.e. the maximum of X, Y and Z axis'
/// extents.
double GetMaxExtent() const {
return GetExtent().Max({0}).To(core::Float64).Item<double>();
}

/// Calculates the percentage position of the given x-coordinate within
/// the x-axis range of this AxisAlignedBoundingBox.
double GetXPercentage(double x) const;

/// Calculates the percentage position of the given y-coordinate within
/// the y-axis range of this AxisAlignedBoundingBox.
double GetYPercentage(double y) const;

/// Calculates the percentage position of the given z-coordinate within
/// the z-axis range of this AxisAlignedBoundingBox.
double GetZPercentage(double z) const;

/// Returns the volume of the bounding box.
double Volume() const {
return GetExtent().Prod({0}).To(core::Float64).Item<double>();
}

/// Returns the eight points that define the bounding box. The Return tensor
/// has shape {8, 3} and data type same as the box.
/// \brief Returns the eight points that define the bounding box.
///
/// The Return tensor has shape {8, 3} and data type same as the box.
core::Tensor GetBoxPoints() const;

/// \brief Indices to points that are within the bounding box.
Expand Down Expand Up @@ -206,16 +213,21 @@ class AxisAlignedBoundingBox : public Geometry, public DrawableGeometry {

/// Creates the axis-aligned box that encloses the set of points.
/// \param points A list of points with data type of float32 or float64 (N x
/// 3 tensor, where N must be larger than 3).
/// 3 tensor).
/// \return AxisAlignedBoundingBox with same data type and device as input
/// points.
static AxisAlignedBoundingBox CreateFromPoints(const core::Tensor &points);

protected:
/// The device to use for the bounding box. The default is CPU:0.
core::Device device_ = core::Device("CPU:0");
/// The data type of the bounding box.
core::Dtype dtype_ = core::Float32;
/// The lower x, y, z bounds of the bounding box.
core::Tensor min_bound_;
/// The upper x, y, z bounds of the bounding box.
core::Tensor max_bound_;
/// The color of the bounding box in RGB. The default is white.
core::Tensor color_;
};

Expand Down Expand Up @@ -373,8 +385,9 @@ class OrientedBoundingBox : public Geometry, public DrawableGeometry {
return GetExtent().Prod({0}).To(core::Float64).Item<double>();
}

/// Returns the eight points that define the bounding box. The Return tensor
/// has shape {8, 3} and data type same as the box.
/// \brief Returns the eight points that define the bounding box.
///
/// The Return tensor has shape {8, 3} and data type same as the box.
///
/// \verbatim
/// ------- x
Expand Down
2 changes: 2 additions & 0 deletions cpp/open3d/visualization/visualizer/Visualizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class Visualizer {
/// Visualizer should be updated accordingly.
///
/// \param geometry_ptr The Geometry object.
/// \param reset_bounding_box Reset viewpoint to view all geometries.
virtual bool AddGeometry(
std::shared_ptr<const geometry::Geometry> geometry_ptr,
bool reset_bounding_box = true);
Expand All @@ -140,6 +141,7 @@ class Visualizer {
/// added by AddGeometry
///
/// \param geometry_ptr The Geometry object.
/// \param reset_bounding_box Reset viewpoint to view all geometries.
virtual bool RemoveGeometry(
std::shared_ptr<const geometry::Geometry> geometry_ptr,
bool reset_bounding_box = true);
Expand Down
2 changes: 1 addition & 1 deletion cpp/pybind/t/geometry/boundingvolume.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ The scaling center will be the box center if it is not specified.)",
m, "AxisAlignedBoundingBox", "create_from_points",
{{"points",
"A list of points with data type of float32 or float64 (N x 3 "
"tensor, where N must be larger than 3)."}});
"tensor)."}});

py::class_<OrientedBoundingBox, PyGeometry<OrientedBoundingBox>,
std::shared_ptr<OrientedBoundingBox>, Geometry, DrawableGeometry>
Expand Down
16 changes: 11 additions & 5 deletions cpp/tests/t/geometry/AxisAlignedBoundingBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ INSTANTIATE_TEST_SUITE_P(
AxisAlignedBoundingBoxPermuteDevicePairs::TestCases()));

TEST_P(AxisAlignedBoundingBoxPermuteDevices, ConstructorNoArg) {
using ::testing::AnyOf;
t::geometry::AxisAlignedBoundingBox aabb;

// Inherited from Geometry3D.
Expand All @@ -51,7 +52,11 @@ TEST_P(AxisAlignedBoundingBoxPermuteDevices, ConstructorNoArg) {
EXPECT_EQ(aabb.GetDevice(), core::Device("CPU:0"));

// Print Information.
EXPECT_EQ(aabb.ToString(), "AxisAlignedBoundingBox[Float32, CPU:0]");
EXPECT_THAT(
aabb.ToString(), // Compiler dependent output
AnyOf("AxisAlignedBoundingBox[[0 0 0] - [0 0 0], Float32, CPU:0]",
"AxisAlignedBoundingBox[[0.0 0.0 0.0] - [0.0 0.0 0.0], "
"Float32, CPU:0]"));
}

TEST_P(AxisAlignedBoundingBoxPermuteDevices, Constructor) {
Expand All @@ -60,10 +65,6 @@ TEST_P(AxisAlignedBoundingBoxPermuteDevices, Constructor) {
core::Tensor min_bound = core::Tensor::Init<float>({-1, -1, -1}, device);
core::Tensor max_bound = core::Tensor::Init<float>({1, 1, 1}, device);

// Attempt to construct with invalid min/max bound.
EXPECT_THROW(t::geometry::AxisAlignedBoundingBox(max_bound, min_bound),
std::runtime_error);

t::geometry::AxisAlignedBoundingBox aabb(min_bound, max_bound);

// Public members.
Expand All @@ -76,6 +77,11 @@ TEST_P(AxisAlignedBoundingBoxPermuteDevices, Constructor) {
core::Tensor::Init<float>({1, 1, 1}, device)));

EXPECT_EQ(aabb.GetDevice(), device);

// Attempt to construct with invalid min/max bound should create a valid
// bounding box with a warning.
t::geometry::AxisAlignedBoundingBox aabb_invalid(max_bound, min_bound);
EXPECT_TRUE(aabb_invalid.GetBoxPoints().AllClose(aabb.GetBoxPoints()));
}

TEST_P(AxisAlignedBoundingBoxPermuteDevicePairs, CopyDevice) {
Expand Down
7 changes: 0 additions & 7 deletions docs/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -1146,13 +1146,6 @@ VERBATIM_HEADERS = YES

ALPHABETICAL_INDEX = YES

# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
# which the alphabetical index list will be split.
# Minimum value: 1, maximum value: 20, default value: 5.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.

COLS_IN_ALPHA_INDEX = 5

# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
Expand Down
Loading