// ---------------------------------------------------------------------------- // - Open3D: www.open3d.org - // ---------------------------------------------------------------------------- // Copyright (c) 2018-2023 www.open3d.org // SPDX-License-Identifier: MIT // ---------------------------------------------------------------------------- #pragma once #include "open3d/core/Tensor.h" #include "open3d/core/TensorCheck.h" #include "open3d/geometry/BoundingVolume.h" #include "open3d/t/geometry/DrawableGeometry.h" #include "open3d/t/geometry/Geometry.h" #include "open3d/utility/Logging.h" namespace open3d { namespace t { namespace geometry { class OrientedBoundingBox; /// \class AxisAlignedBoundingBox /// \brief A bounding box that is aligned along the coordinate axes and defined /// by the min_bound and max_bound. /// /// - (min_bound, max_bound): Lower and upper bounds of the bounding box for all /// axes. /// - Usage /// - AxisAlignedBoundingBox::GetMinBound() /// - AxisAlignedBoundingBox::SetMinBound(const core::Tensor &min_bound) /// - AxisAlignedBoundingBox::GetMaxBound() /// - AxisAlignedBoundingBox::SetMaxBound(const core::Tensor &max_bound) /// - Value tensor must have shape {3,}. /// - Value tensor must have the same data type and device. /// - Value tensor can only be float32 (default) or float64. /// - The device of the tensor determines the device of the box. /// /// - color: Color of the bounding box. /// - Usage /// - AxisAlignedBoundingBox::GetColor() /// - AxisAlignedBoundingBox::SetColor(const core::Tensor &color) /// - Value tensor must have shape {3,}. /// - Value tensor can only be float32 (default) or float64. /// - Value tensor can only be range [0.0, 1.0]. class AxisAlignedBoundingBox : public Geometry, public DrawableGeometry { public: /// \brief Construct an empty AxisAlignedBoundingBox on the provided device. AxisAlignedBoundingBox(const core::Device &device = core::Device("CPU:0")); /// \brief Construct an AxisAlignedBoundingBox from min/max bound. /// /// The AxisAlignedBoundingBox will be created on the device of the given /// bound tensor, which must be on the same device and have the same data /// type. /// \param min_bound Lower bounds of the bounding box for all axes. Tensor /// of shape {3,}, and type float32 or float64. /// \param max_bound Upper bounds of the bounding box for all axes. Tensor /// of shape {3,}, and type float32 or float64. AxisAlignedBoundingBox(const core::Tensor &min_bound, const core::Tensor &max_bound); virtual ~AxisAlignedBoundingBox() override {} /// \brief Returns the device attribute of this AxisAlignedBoundingBox. core::Device GetDevice() const override { return device_; } /// \brief Returns the data type attribute of this AxisAlignedBoundingBox. core::Dtype GetDtype() const { return dtype_; } /// \brief Transfer the AxisAlignedBoundingBox to a specified device. /// \param device The targeted device to convert to. /// \param copy If true, a new AxisAlignedBoundingBox is always created; If /// false, the copy is avoided when the original AxisAlignedBoundingBox is /// already on the targeted device. AxisAlignedBoundingBox To(const core::Device &device, bool copy = false) const; /// Returns copy of the AxisAlignedBoundingBox on the same device. AxisAlignedBoundingBox Clone() const { return To(GetDevice(), /*copy=*/true); } AxisAlignedBoundingBox &Clear() override; bool IsEmpty() const override { return Volume() == 0; } /// \brief Set the min 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 min 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. void SetMinBound(const core::Tensor &min_bound); /// \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 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. /// If the data type of the given tensor differs from the data type of the /// box, an exception will be thrown. /// /// \param color Tensor with {3,} shape, and type float32 or float64, /// with values in range [0.0, 1.0]. void SetColor(const core::Tensor &color); public: core::Tensor GetMinBound() const { return min_bound_; } core::Tensor GetMaxBound() const { return max_bound_; } core::Tensor GetColor() const { return color_; } core::Tensor GetCenter() const { return (min_bound_ + max_bound_) * 0.5; } /// \brief Translate the axis-aligned box by the given translation. /// /// If relative is true, the translation is applied to the current min and /// max bound. If relative is false, the translation is applied to make the /// box's center at the given translation. /// /// \param translation Translation tensor of shape (3,), type float32 or /// float64, device same as the box. /// \param relative Whether to perform relative translation. AxisAlignedBoundingBox &Translate(const core::Tensor &translation, bool relative = true); /// \brief Scale the axis-aligned box. /// If \f$mi\f$ is the min_bound and \f$ma\f$ is the max_bound of /// the axis aligned bounding box, and \f$s\f$ and \f$c\f$ are the /// provided scaling factor and center respectively, then the new /// min_bound and max_bound are given by \f$mi = c + s (mi - c)\f$ /// and \f$ma = c + s (ma - c)\f$. /// The scaling center will be the box center if it is not specified. /// /// \param scale The scale parameter. /// \param center Center used for the scaling operation. Tensor of shape /// {3,}, type float32 or float64, device same as the box. AxisAlignedBoundingBox &Scale( double scale, const utility::optional ¢er = utility::nullopt); /// \brief Add operation for axis-aligned bounding box. /// The device of other box must be the same as the device of the current /// box. AxisAlignedBoundingBox &operator+=(const AxisAlignedBoundingBox &other); /// Get the extent/length of the bounding box in x, y, and z dimension. core::Tensor GetExtent() const { return max_bound_ - min_bound_; } /// Returns the half extent of the bounding box. core::Tensor GetHalfExtent() const { return GetExtent() / 2; } /// \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(); } /// 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(); } /// \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. /// /// \param points Tensor with {N, 3} shape, and type float32 or float64. core::Tensor GetPointIndicesWithinBoundingBox( const core::Tensor &points) const; /// Text description. std::string ToString() const; /// Convert to a legacy Open3D axis-aligned box. open3d::geometry::AxisAlignedBoundingBox ToLegacy() const; /// Convert to an oriented box. OrientedBoundingBox GetOrientedBoundingBox() const; /// Create an AxisAlignedBoundingBox from a legacy Open3D /// axis-aligned box. /// /// \param box Legacy AxisAlignedBoundingBox. /// \param dtype The data type of the box for min_bound, max_bound and /// color. The default is float32. /// \param device The device of the box. The default is CPU:0. static AxisAlignedBoundingBox FromLegacy( const open3d::geometry::AxisAlignedBoundingBox &box, const core::Dtype &dtype = core::Float32, const core::Device &device = core::Device("CPU:0")); /// 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). /// \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_; }; /// \class OrientedBoundingBox /// \brief A bounding box oriented along an arbitrary frame of reference. /// /// - (center, rotation, extent): The oriented bounding box is defined by its /// center position, rotation matrix and extent. /// - Usage /// - OrientedBoundingBox::GetCenter() /// - OrientedBoundingBox::SetCenter(const core::Tensor ¢er) /// - OrientedBoundingBox::GetRotation() /// - OrientedBoundingBox::SetRotation(const core::Tensor &rotation) /// - Value tensor of center and extent must have shape {3,}. /// - Value tensor of rotation must have shape {3, 3}. /// - Value tensor must have the same data type and device. /// - Value tensor can only be float32 (default) or float64. /// - The device of the tensor determines the device of the box. /// /// - color: Color of the bounding box. /// - Usage /// - OrientedBoundingBox::GetColor() /// - OrientedBoundingBox::SetColor(const core::Tensor &color) /// - Value tensor must have shape {3,}. /// - Value tensor can only be float32 (default) or float64. /// - Value tensor can only be range [0.0, 1.0]. class OrientedBoundingBox : public Geometry, public DrawableGeometry { public: /// \brief Construct an empty OrientedBoundingBox on the provided device. OrientedBoundingBox(const core::Device &device = core::Device("CPU:0")); /// \brief Construct an OrientedBoundingBox from center, rotation and /// extent. /// /// The OrientedBoundingBox will be created on the device of the given /// tensors, which must be on the same device and have the same data /// type. /// \param center Center of the bounding box. Tensor of shape {3,}, and type /// float32 or float64. /// \param rotation Rotation matrix of the bounding box. Tensor of shape {3, /// 3}, and type float32 or float64. /// \param extent Extent of the bounding box. Tensor of shape {3,}, and type /// float32 or float64. OrientedBoundingBox(const core::Tensor ¢er, const core::Tensor &rotation, const core::Tensor &extent); virtual ~OrientedBoundingBox() override {} /// \brief Returns the device attribute of this OrientedBoundingBox. core::Device GetDevice() const override { return device_; } /// \brief Returns the data type attribute of this OrientedBoundingBox. core::Dtype GetDtype() const { return dtype_; } /// Transfer the OrientedBoundingBox to a specified device. /// \param device The targeted device to convert to. /// \param copy If true, a new OrientedBoundingBox is always created; if /// false, the copy is avoided when the original OrientedBoundingBox is /// already on the targeted device. OrientedBoundingBox To(const core::Device &device, bool copy = false) const; /// Returns copy of the OrientedBoundingBox on the same device. OrientedBoundingBox Clone() const { return To(GetDevice(), /*copy=*/true); } OrientedBoundingBox &Clear() override; bool IsEmpty() const override { return Volume() == 0; } /// \brief Set the center of the box. /// If the data type of the given tensor differs from the data type of the /// box, an exception will be thrown. /// /// \param center Tensor with {3,} shape, and type float32 or float64. void SetCenter(const core::Tensor ¢er); /// \brief Set the rotation matrix of the box. /// If the data type of the given tensor differs from the data type of the /// box, an exception will be thrown. /// /// \param rotation Tensor with {3, 3} shape, and type float32 or float64. void SetRotation(const core::Tensor &rotation); /// \brief Set the extent of the box. /// If the data type of the given tensor differs from the data type of the /// box, an exception will be thrown. /// /// \param extent Tensor with {3,} shape, and type float32 or float64. void SetExtent(const core::Tensor &extent); /// \brief Set the color of the box. /// /// \param color Tensor with {3,} shape, and type float32 or float64, /// with values in range [0.0, 1.0]. void SetColor(const core::Tensor &color); public: core::Tensor GetMinBound() const; core::Tensor GetMaxBound() const; core::Tensor GetColor() const { return color_; } core::Tensor GetCenter() const { return center_; } core::Tensor GetRotation() const { return rotation_; } core::Tensor GetExtent() const { return extent_; } /// \brief Translate the oriented box by the given translation. /// If relative is true, the translation is added to the center of the box. /// If false, the center will be assigned to the translation. /// /// \param translation Translation tensor of shape {3,}, type float32 or /// float64, device same as the box. /// \param relative Whether to perform relative translation. OrientedBoundingBox &Translate(const core::Tensor &translation, bool relative = true); /// \brief Rotate the oriented box by the given rotation matrix. If the /// rotation matrix is not orthogonal, the rotation will no be applied. /// The rotation center will be the box center if it is not specified. /// /// \param rotation Rotation matrix of shape {3, 3}, type float32 or /// float64, device same as the box. /// \param center Center of the rotation, default is null, which means use /// center of the box as rotation center. OrientedBoundingBox &Rotate( const core::Tensor &rotation, const utility::optional ¢er = utility::nullopt); /// \brief Transform the oriented box by the given transformation matrix. /// /// \param transformation Transformation matrix of shape {4, 4}, type /// float32 or float64, device same as the box. OrientedBoundingBox &Transform(const core::Tensor &transformation); /// \brief Scale the axis-aligned box. /// If \f$mi\f$ is the min_bound and \f$ma\f$ is the max_bound of /// the axis aligned bounding box, and \f$s\f$ and \f$c\f$ are the /// provided scaling factor and center respectively, then the new /// min_bound and max_bound are given by \f$mi = c + s (mi - c)\f$ /// and \f$ma = c + s (ma - c)\f$. /// The scaling center will be the box center if it is not specified. /// /// \param scale The scale parameter. /// \param center Center used for the scaling operation. Tensor of shape /// {3,}, type float32 or float64, device same as the box. OrientedBoundingBox &Scale( double scale, const utility::optional ¢er = utility::nullopt); /// Returns the volume of the bounding box. double Volume() const { return GetExtent().Prod({0}).To(core::Float64).Item(); } /// \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 /// /| /// / | /// / | z /// y /// 0 ------------------- 1 /// /| /| /// / | / | /// / | / | /// / | / | /// 2 ------------------- 7 | /// | |____________|____| 6 /// | /3 | / /// | / | / /// | / | / /// |/ |/ /// 5 ------------------- 4 /// \endverbatim core::Tensor GetBoxPoints() const; /// \brief Indices to points that are within the bounding box. /// /// \param points Tensor with {N, 3} shape, and type float32 or float64. core::Tensor GetPointIndicesWithinBoundingBox( const core::Tensor &points) const; /// Text description. std::string ToString() const; /// Convert to a legacy Open3D oriented box. open3d::geometry::OrientedBoundingBox ToLegacy() const; /// Convert to an axis-aligned box. AxisAlignedBoundingBox GetAxisAlignedBoundingBox() const; /// Create an oriented bounding box from the AxisAlignedBoundingBox. /// /// \param aabb AxisAlignedBoundingBox object from which /// OrientedBoundingBox is created. /// \return OrientedBoundingBox with the same device and dtype as input box. static OrientedBoundingBox CreateFromAxisAlignedBoundingBox( const AxisAlignedBoundingBox &aabb); /// Create an OrientedBoundingBox from a legacy Open3D oriented box. /// /// \param box Legacy OrientedBoundingBox. /// \param dtype The data type of the box for min_bound max_bound and color. /// The default is float32. /// \param device The device of the box. The default is CPU:0. static OrientedBoundingBox FromLegacy( const open3d::geometry::OrientedBoundingBox &box, const core::Dtype &dtype = core::Float32, const core::Device &device = core::Device("CPU:0")); /// Creates an oriented bounding box using a PCA. /// Note that this is only an approximation to the minimum oriented /// bounding box that could be computed for example with O'Rourke's /// algorithm (cf. http://cs.smith.edu/~jorourke/Papers/MinVolBox.pdf, /// https://www.geometrictools.com/Documentation/MinimumVolumeBox.pdf) /// This is a wrapper for a CPU implementation. /// /// \param points A list of points with data type of float32 or float64 (N x /// 3 tensor, where N must be larger than 3). /// \param robust If set to true uses a more robust method which works in /// degenerate cases but introduces noise to the points coordinates. /// \return OrientedBoundingBox with same data type and device as input /// points. static OrientedBoundingBox CreateFromPoints(const core::Tensor &points, bool robust = false); protected: core::Device device_ = core::Device("CPU:0"); core::Dtype dtype_ = core::Float32; core::Tensor center_; core::Tensor rotation_; core::Tensor extent_; core::Tensor color_; }; } // namespace geometry } // namespace t } // namespace open3d