Skip to content

Commit 24aee9a

Browse files
author
zatchbell1311
committed
Fix: use deterministic x_axis for ShapeContext3D azimuth orientation
1 parent c09da20 commit 24aee9a

2 files changed

Lines changed: 194 additions & 165 deletions

File tree

features/include/pcl/features/3dsc.h

Lines changed: 110 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,27 @@
4747

4848
namespace pcl
4949
{
50-
/**
51-
* \brief ShapeContext3DEstimation implements the 3D shape context descriptor
52-
* based on Frome et al. (ECCV 2004).
53-
*
54-
* Notes:
55-
* - The suggested PointOutT is pcl::ShapeContext1980 (fixed-length descriptor).
56-
* - PCL requires one descriptor per input point and descriptors to be a fixed length.
57-
* - The original paper suggests multiple azimuth rotations (L rotations). That approach
58-
* would expand descriptor length to descriptor_length_ * azimuth_bins_, which breaks
59-
* PCL assumptions (fixed size and one-to-one mapping). Therefore the implementation
60-
* below uses a deterministic azimuth normalization (shift) that keeps the descriptor
61-
* length unchanged while removing the randomness introduced by the random local X axis.
62-
*/
50+
/** \brief ShapeContext3DEstimation implements the 3D shape context descriptor as
51+
* described in:
52+
* - Andrea Frome, Daniel Huber, Ravi Kolluri and Thomas Bülow, Jitendra Malik
53+
* Recognizing Objects in Range Data Using Regional Point Descriptors,
54+
* In proceedings of the 8th European Conference on Computer Vision (ECCV),
55+
* Prague, May 11-14, 2004
56+
*
57+
* The suggested PointOutT is pcl::ShapeContext1980
58+
*
59+
* \attention
60+
* The convention for a 3D shape context descriptor is:
61+
* - if a query point's nearest neighbors cannot be estimated, the feature descriptor will be set to NaN (not a number), and the RF to 0
62+
* - it is impossible to estimate a 3D shape context descriptor for a
63+
* point that doesn't have finite 3D coordinates. Therefore, any point
64+
* that contains NaN data on x, y, or z, will have its boundary feature
65+
* property set to NaN.
66+
*
67+
* \author Alessandro Franchi, Samuele Salti, Federico Tombari (original code)
68+
* \author Nizar Sallem (port to PCL)
69+
* \ingroup features
70+
*/
6371
template <typename PointInT, typename PointNT, typename PointOutT = pcl::ShapeContext1980>
6472
class ShapeContext3DEstimation : public FeatureFromNormals<PointInT, PointNT, PointOutT>
6573
{
@@ -82,19 +90,20 @@ namespace pcl
8290

8391
/** \brief Constructor.
8492
* \param[in] random If true the random seed is set to current time, else it is
85-
* set to 12345 prior to computing the descriptor (used to select X axis).
93+
* set to 12345 prior to computing the descriptor (used to select X axis)
8694
*/
8795
ShapeContext3DEstimation (bool random = false) :
8896
radii_interval_(0),
8997
theta_divisions_(0),
9098
phi_divisions_(0),
9199
volume_lut_(0),
100+
92101
rng_dist_ (0.0f, 1.0f)
93102
{
94103
feature_name_ = "ShapeContext3DEstimation";
95104
search_radius_ = 2.5;
96105

97-
// Initialize RNG: deterministic by default (helps reproducible testing)
106+
// Create a random number generator object
98107
if (random)
99108
{
100109
std::random_device rd;
@@ -106,78 +115,123 @@ namespace pcl
106115

107116
~ShapeContext3DEstimation() override = default;
108117

118+
//inline void
119+
//setAzimuthBins (std::size_t bins) { azimuth_bins_ = bins; }
120+
109121
/** \return the number of bins along the azimuth */
110-
inline std::size_t getAzimuthBins () { return (azimuth_bins_); }
122+
inline std::size_t
123+
getAzimuthBins () { return (azimuth_bins_); }
124+
125+
//inline void
126+
//setElevationBins (std::size_t bins) { elevation_bins_ = bins; }
111127

112128
/** \return The number of bins along the elevation */
113-
inline std::size_t getElevationBins () { return (elevation_bins_); }
129+
inline std::size_t
130+
getElevationBins () { return (elevation_bins_); }
131+
132+
//inline void
133+
//setRadiusBins (std::size_t bins) { radius_bins_ = bins; }
114134

115135
/** \return The number of bins along the radii direction */
116-
inline std::size_t getRadiusBins () { return (radius_bins_); }
136+
inline std::size_t
137+
getRadiusBins () { return (radius_bins_); }
117138

118-
inline void setMinimalRadius (double radius) { min_radius_ = radius; }
119-
inline double getMinimalRadius () { return (min_radius_); }
139+
/** \brief The minimal radius value for the search sphere (rmin) in the original paper
140+
* \param[in] radius the desired minimal radius
141+
*/
142+
inline void
143+
setMinimalRadius (double radius) { min_radius_ = radius; }
144+
145+
/** \return The minimal sphere radius */
146+
inline double
147+
getMinimalRadius () { return (min_radius_); }
120148

121-
inline void setPointDensityRadius (double radius) { point_density_radius_ = radius; }
122-
inline double getPointDensityRadius () { return (point_density_radius_); }
149+
/** \brief This radius is used to compute local point density
150+
* density = number of points within this radius
151+
* \param[in] radius value of the point density search radius
152+
*/
153+
inline void
154+
setPointDensityRadius (double radius) { point_density_radius_ = radius; }
155+
156+
/** \return The point density search radius */
157+
inline double
158+
getPointDensityRadius () { return (point_density_radius_); }
123159

124160
protected:
125-
/** Initialize internal tables (radii intervals, angular divisions, volume LUT). */
126-
bool initCompute () override;
127-
128-
/**
129-
* Estimate a descriptor for a given point.
130-
* - index: point index to estimate descriptor for
131-
* - normals: normals used (precomputed)
132-
* - rf: output reference frame (3x3 stored in rf[9]); 3DSC does not define repeatable RF so we set rf=0
133-
* - desc: output descriptor (must be descriptor_length_)
134-
*/
135-
bool computePoint (std::size_t index, const pcl::PointCloud<PointNT> &normals, float rf[9], std::vector<float> &desc);
161+
/** \brief Initialize computation by allocating all the intervals and the volume lookup table. */
162+
bool
163+
initCompute () override;
164+
165+
/** \brief Estimate a descriptor for a given point.
166+
* \param[in] index the index of the point to estimate a descriptor for
167+
* \param[in] normals a pointer to the set of normals
168+
* \param[out] rf the reference frame
169+
* \param[out] desc the resultant estimated descriptor
170+
* \return true if the descriptor was computed successfully, false if there was an error
171+
* (e.g. the nearest neighbor didn't return any neighbors)
172+
*/
173+
bool
174+
computePoint (std::size_t index, const pcl::PointCloud<PointNT> &normals, float rf[9], std::vector<float> &desc);
136175

137-
/** Compute feature for all indices (fills output cloud). */
138-
void computeFeature (PointCloudOut &output) override;
176+
/** \brief Estimate the actual feature.
177+
* \param[out] output the resultant feature
178+
*/
179+
void
180+
computeFeature (PointCloudOut &output) override;
139181

140-
/* Lookup / intermediate data */
182+
/** \brief Values of the radii interval */
141183
std::vector<float> radii_interval_;
184+
185+
/** \brief Theta divisions interval */
142186
std::vector<float> theta_divisions_;
187+
188+
/** \brief Phi divisions interval */
143189
std::vector<float> phi_divisions_;
190+
191+
/** \brief Volumes look up table */
144192
std::vector<float> volume_lut_;
145193

146-
/* Histogram bin configuration (defaults chosen to match ShapeContext1980) */
194+
/** \brief Bins along the azimuth dimension */
147195
std::size_t azimuth_bins_{12};
196+
197+
/** \brief Bins along the elevation dimension */
148198
std::size_t elevation_bins_{11};
199+
200+
/** \brief Bins along the radius dimension */
149201
std::size_t radius_bins_{15};
150202

151-
/* Parameters */
203+
/** \brief Minimal radius value */
152204
double min_radius_{0.1};
205+
206+
/** \brief Point density radius */
153207
double point_density_radius_{0.2};
208+
209+
/** \brief Descriptor length */
154210
std::size_t descriptor_length_{};
155211

156-
/* RNG for random local x-axis selection */
212+
/** \brief Random number generator algorithm. */
157213
std::mt19937 rng_;
158-
std::uniform_real_distribution<float> rng_dist_;
159214

160-
/* Old (commented) API for L-rotation approach left in file for history; we DO NOT use it.
161-
* //void shiftAlongAzimuth (std::size_t block_size, std::vector<float>& desc);
162-
*/
215+
/** \brief Random number generator distribution. */
216+
std::uniform_real_distribution<float> rng_dist_;
163217

164-
/**
165-
* Our deterministic azimuth normalization:
166-
* Rotate azimuth bins so that the azimuth block with the largest accumulated
167-
* energy becomes the first block (index 0). This removes randomness introduced
168-
* by the local random X axis selection while preserving a fixed descriptor length.
169-
*
170-
* - Input: desc (size == descriptor_length_)
171-
* - Output: desc is circularly shifted in-place so that the dominant azimuth block
172-
* is aligned to the start of the descriptor array.
218+
/* \brief Shift computed descriptor "L" times along the azimuthal direction
219+
* \param[in] block_size the size of each azimuthal block
220+
* \param[in] desc at input desc == original descriptor and on output it contains
221+
* shifted descriptor resized descriptor_length_ * azimuth_bins_
173222
*/
174-
void shiftAlongAzimuth (std::vector<float>& desc) const;
223+
//void
224+
//shiftAlongAzimuth (std::size_t block_size, std::vector<float>& desc);
175225

176-
/** Return a random float in [0,1) using the internal RNG. */
177-
inline float rnd () { return (rng_dist_ (rng_)); }
226+
/** \brief Boost-based random number generator. */
227+
inline float
228+
rnd ()
229+
{
230+
return (rng_dist_ (rng_));
231+
}
178232
};
179233
}
180234

181235
#ifdef PCL_NO_PRECOMPILE
182236
#include <pcl/features/impl/3dsc.hpp>
183-
#endif
237+
#endif

0 commit comments

Comments
 (0)