Point Cloud Library (PCL) 1.15.1-dev
Loading...
Searching...
No Matches
sac_model_ellipse3d.h
1/*
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Point Cloud Library (PCL) - www.pointclouds.org
5 * Copyright (c) 2014-, Open Perception Inc.
6 *
7 * All rights reserved
8 */
9
10#pragma once
11
12#include <pcl/sample_consensus/sac_model.h>
13#include <pcl/sample_consensus/model_types.h>
14
15namespace pcl
16{
17 namespace internal
18 {
19 /** \brief Internal function to optimize ellipse coefficients. */
20 PCL_EXPORTS int
21 optimizeModelCoefficientsEllipse3D (Eigen::VectorXf &coeff,
22 const Eigen::ArrayXf &pts_x,
23 const Eigen::ArrayXf &pts_y,
24 const Eigen::ArrayXf &pts_z);
25
26 /** \brief Internal function to compute ellipse point from parametric coefficients and angle.
27 * \param[in] par the parametric coefficients (a, b, h, k, slant)
28 * \param[in] th the angle (in radians)
29 * \param[out] x the resultant X coordinate in local frame
30 * \param[out] y the resultant Y coordinate in local frame
31 */
32 inline void
33 get_ellipse_point (const Eigen::VectorXf& par, float th, float& x, float& y)
34 {
35 const float par_a (par [0]);
36 const float par_b (par [1]);
37 const float par_h (par [2]);
38 const float par_k (par [3]);
39 const float par_t (par [4]);
40
41 x = par_h + std::cos (par_t) * par_a * std::cos (th) - std::sin (par_t) * par_b * std::sin (th);
42 y = par_k + std::sin (par_t) * par_a * std::cos (th) + std::cos (par_t) * par_b * std::sin (th);
43 }
44
45 /** \brief Internal function to find the optimal angle using Golden Section Search.
46 * \param[in] par the ellipse coefficients (a, b, 0, 0, 0)
47 * \param[in] u point X coordinate in local frame
48 * \param[in] v point Y coordinate in local frame
49 * \param[in] th_min search interval lower bound
50 * \param[in] th_max search interval upper bound
51 * \param[in] epsilon search convergence tolerance
52 * \return the optimal angle (in radians)
53 */
54 inline float
55 golden_section_search (const Eigen::VectorXf& par, float u, float v, float th_min, float th_max, float epsilon)
56 {
57 constexpr float phi (1.61803398874989484820f);
58 float tl (th_min), tu (th_max);
59 float ta = tl + (tu - tl) * (1.0f - 1.0f / phi);
60 float tb = tl + (tu - tl) * 1.0f / phi;
61
62 while ((tu - tl) > epsilon)
63 {
64 float x_a (0.0f), y_a (0.0f);
65 get_ellipse_point (par, ta, x_a, y_a);
66 float squared_dist_ta = (u - x_a) * (u - x_a) + (v - y_a) * (v - y_a);
67
68 float x_b (0.0f), y_b (0.0f);
69 get_ellipse_point (par, tb, x_b, y_b);
70 float squared_dist_tb = (u - x_b) * (u - x_b) + (v - y_b) * (v - y_b);
71
72 if (squared_dist_ta < squared_dist_tb)
73 {
74 tu = tb;
75 tb = ta;
76 ta = tl + (tu - tl) * (1.0f - 1.0f / phi);
77 }
78 else if (squared_dist_ta > squared_dist_tb)
79 {
80 tl = ta;
81 ta = tb;
82 tb = tl + (tu - tl) * 1.0f / phi;
83 }
84 else
85 {
86 tl = ta;
87 tu = tb;
88 ta = tl + (tu - tl) * (1.0f - 1.0f / phi);
89 tb = tl + (tu - tl) * 1.0f / phi;
90 }
91 }
92 return (tl + tu) / 2.0f;
93 }
94
95 /** \brief Internal function to compute the shortest distance vector from a point to an ellipse.
96 * \param[in] par the ellipse coefficients (a, b, 0, 0, 0)
97 * \param[in] u point X coordinate in local frame
98 * \param[in] v point Y coordinate in local frame
99 * \param[out] th_opt the resultant optimal angle on the ellipse
100 * \return the distance vector from the point to its projection on the ellipse
101 */
102 inline Eigen::Vector2f
103 dvec2ellipse (const Eigen::VectorXf& par, float u, float v, float& th_opt)
104 {
105 const float par_h = par [2];
106 const float par_k = par [3];
107
108 const Eigen::Vector2f center (par_h, par_k);
109 Eigen::Vector2f p (u, v);
110 p -= center;
111
112 Eigen::Vector2f x_axis (0.0f, 0.0f);
113 get_ellipse_point (par, 0.0f, x_axis (0), x_axis (1));
114 x_axis -= center;
115
116 Eigen::Vector2f y_axis (0.0f, 0.0f);
117 get_ellipse_point (par, static_cast<float> (M_PI / 2.0), y_axis (0), y_axis (1));
118 y_axis -= center;
119
120 const float x_proj = p.dot (x_axis) / x_axis.norm ();
121 const float y_proj = p.dot (y_axis) / y_axis.norm ();
122
123 float th_min (0.0f), th_max (0.0f);
124 const float th = std::atan2 (y_proj, x_proj);
125
126 if (th < -static_cast<float> (M_PI / 2.0))
127 {
128 th_min = -static_cast<float> (M_PI);
129 th_max = -static_cast<float> (M_PI / 2.0);
130 }
131 else if (th < 0.0f)
132 {
133 th_min = -static_cast<float> (M_PI / 2.0);
134 th_max = 0.0f;
135 }
136 else if (th < static_cast<float> (M_PI / 2.0))
137 {
138 th_min = 0.0f;
139 th_max = static_cast<float> (M_PI / 2.0);
140 }
141 else
142 {
143 th_min = static_cast<float> (M_PI / 2.0);
144 th_max = static_cast<float> (M_PI);
145 }
146
147 th_opt = golden_section_search (par, u, v, th_min, th_max, 1.e-3f);
148 float x (0.0f), y (0.0f);
149 get_ellipse_point (par, th_opt, x, y);
150 return {u - x, v - y};
151 }
152 }
153
154 /** \brief SampleConsensusModelEllipse3D defines a model for 3D ellipse segmentation.
155 *
156 * The model coefficients are defined as:
157 * - \b center.x : the X coordinate of the ellipse's center
158 * - \b center.y : the Y coordinate of the ellipse's center
159 * - \b center.z : the Z coordinate of the ellipse's center
160 * - \b semi_axis.u : semi-major axis length along the local u-axis of the ellipse
161 * - \b semi_axis.v : semi-minor axis length along the local v-axis of the ellipse
162 * - \b normal.x : the X coordinate of the normal's direction
163 * - \b normal.y : the Y coordinate of the normal's direction
164 * - \b normal.z : the Z coordinate of the normal's direction
165 * - \b u.x : the X coordinate of the local u-axis of the ellipse
166 * - \b u.y : the Y coordinate of the local u-axis of the ellipse
167 * - \b u.z : the Z coordinate of the local u-axis of the ellipse
168 *
169 * For more details please refer to the following manuscript:
170 * "Semi-autonomous Prosthesis Control Using Minimal Depth Information and Vibrotactile Feedback",
171 * Miguel N. Castro & Strahinja Dosen. IEEE Transactions on Human-Machine Systems [under review]. arXiv:2210.00541.
172 * (@ github.com/mnobrecastro/pcl-ellipse-fitting)
173 *
174 * \author Miguel Nobre Castro (mnobrecastro@gmail.com)
175 * \ingroup sample_consensus
176 */
177 template <typename PointT>
179 {
180 public:
187
191
192 using Ptr = shared_ptr<SampleConsensusModelEllipse3D<PointT>>;
193 using ConstPtr = shared_ptr<const SampleConsensusModelEllipse3D<PointT>>;
194
195 /** \brief Constructor for base SampleConsensusModelEllipse3D.
196 * \param[in] cloud the input point cloud dataset
197 * \param[in] random if true set the random seed to the current time, else set to 12345 (default: false)
198 */
199 SampleConsensusModelEllipse3D (const PointCloudConstPtr &cloud, bool random = false)
200 : SampleConsensusModel<PointT> (cloud, random)
201 {
202 model_name_ = "SampleConsensusModelEllipse3D";
203 sample_size_ = 6;
204 model_size_ = 11;
205 }
206
207 /** \brief Constructor for base SampleConsensusModelEllipse3D.
208 * \param[in] cloud the input point cloud dataset
209 * \param[in] indices a vector of point indices to be used from \a cloud
210 * \param[in] random if true set the random seed to the current time, else set to 12345 (default: false)
211 */
213 const Indices &indices,
214 bool random = false)
215 : SampleConsensusModel<PointT> (cloud, indices, random)
216 {
217 model_name_ = "SampleConsensusModelEllipse3D";
218 sample_size_ = 6;
219 model_size_ = 11;
220 }
221
222 /** \brief Empty destructor */
223 ~SampleConsensusModelEllipse3D () override = default;
224
225 /** \brief Copy constructor.
226 * \param[in] source the model to copy into this
227 */
230 {
231 *this = source;
232 model_name_ = "SampleConsensusModelEllipse3D";
233 }
234
235 /** \brief Copy constructor.
236 * \param[in] source the model to copy into this
237 */
240 {
242 return *this;
243 }
244
245 /** \brief Check whether the given index samples can form a valid 3D ellipse model, compute the model coefficients
246 * from these samples and store them in model_coefficients. The ellipse coefficients are: x, y, R.
247 * \param[in] samples the point indices found as possible good candidates for creating a valid model
248 * \param[out] model_coefficients the resultant model coefficients
249 */
250 bool
251 computeModelCoefficients (const Indices &samples,
252 Eigen::VectorXf &model_coefficients) const override;
253
254 /** \brief Compute all distances from the cloud data to a given 3D ellipse model.
255 * \param[in] model_coefficients the coefficients of a 3D ellipse model that we need to compute distances to
256 * \param[out] distances the resultant estimated distances
257 */
258 void
259 getDistancesToModel (const Eigen::VectorXf &model_coefficients,
260 std::vector<double> &distances) const override;
261
262 /** \brief Compute all distances from the cloud data to a given 3D ellipse model.
263 * \param[in] model_coefficients the coefficients of a 3D ellipse model that we need to compute distances to
264 * \param[in] threshold a maximum admissible distance threshold for determining the inliers from the outliers
265 * \param[out] inliers the resultant model inliers
266 */
267 void
268 selectWithinDistance (const Eigen::VectorXf &model_coefficients,
269 const double threshold,
270 Indices &inliers) override;
271
272 /** \brief Count all the points which respect the given model coefficients as inliers.
273 *
274 * \param[in] model_coefficients the coefficients of a model that we need to compute distances to
275 * \param[in] threshold maximum admissible distance threshold for determining the inliers from the outliers
276 * \return the resultant number of inliers
277 */
278 std::size_t
279 countWithinDistance (const Eigen::VectorXf &model_coefficients,
280 const double threshold) const override;
281
282 /** \brief Recompute the 3d ellipse coefficients using the given inlier set and return them to the user.
283 * @note: these are the coefficients of the 3d ellipse model after refinement (e.g. after SVD)
284 * \param[in] inliers the data inliers found as supporting the model
285 * \param[in] model_coefficients the initial guess for the optimization
286 * \param[out] optimized_coefficients the resultant recomputed coefficients after non-linear optimization
287 */
288 void
289 optimizeModelCoefficients (const Indices &inliers,
290 const Eigen::VectorXf &model_coefficients,
291 Eigen::VectorXf &optimized_coefficients) const override;
292
293 /** \brief Create a new point cloud with inliers projected onto the 3d ellipse model.
294 * \param[in] inliers the data inliers that we want to project on the 3d ellipse model
295 * \param[in] model_coefficients the coefficients of a 3d ellipse model
296 * \param[out] projected_points the resultant projected points
297 * \param[in] copy_data_fields set to true if we need to copy the other data fields
298 */
299 void
300 projectPoints (const Indices &inliers,
301 const Eigen::VectorXf &model_coefficients,
302 PointCloud &projected_points,
303 bool copy_data_fields = true) const override;
304
305 /** \brief Verify whether a subset of indices verifies the given 3d ellipse model coefficients.
306 * \param[in] indices the data indices that need to be tested against the 3d ellipse model
307 * \param[in] model_coefficients the 3d ellipse model coefficients
308 * \param[in] threshold a maximum admissible distance threshold for determining the inliers from the outliers
309 */
310 bool
311 doSamplesVerifyModel (const std::set<index_t> &indices,
312 const Eigen::VectorXf &model_coefficients,
313 const double threshold) const override;
314
315 /** \brief Return a unique id for this model (SACMODEL_ELLIPSE3D). */
316 inline pcl::SacModel
317 getModelType () const override { return (SACMODEL_ELLIPSE3D); }
318
319 protected:
322
323 /** \brief Check whether a model is valid given the user constraints.
324 * \param[in] model_coefficients the set of model coefficients
325 */
326 bool
327 isModelValid (const Eigen::VectorXf &model_coefficients) const override;
328
329 /** \brief Check if a sample of indices results in a good sample of points indices.
330 * \param[in] samples the resultant index samples
331 */
332 bool
333 isSampleGood(const Indices &samples) const override;
334 };
335}
336
337#ifdef PCL_NO_PRECOMPILE
338#include <pcl/sample_consensus/impl/sac_model_ellipse3d.hpp>
339#endif
PointCloud represents the base class in PCL for storing collections of 3D points.
SampleConsensusModelEllipse3D defines a model for 3D ellipse segmentation.
typename SampleConsensusModel< PointT >::PointCloudConstPtr PointCloudConstPtr
void optimizeModelCoefficients(const Indices &inliers, const Eigen::VectorXf &model_coefficients, Eigen::VectorXf &optimized_coefficients) const override
Recompute the 3d ellipse coefficients using the given inlier set and return them to the user.
typename SampleConsensusModel< PointT >::PointCloudPtr PointCloudPtr
pcl::SacModel getModelType() const override
Return a unique id for this model (SACMODEL_ELLIPSE3D).
std::size_t countWithinDistance(const Eigen::VectorXf &model_coefficients, const double threshold) const override
Count all the points which respect the given model coefficients as inliers.
SampleConsensusModelEllipse3D(const SampleConsensusModelEllipse3D &source)
Copy constructor.
shared_ptr< const SampleConsensusModelEllipse3D< PointT > > ConstPtr
SampleConsensusModelEllipse3D & operator=(const SampleConsensusModelEllipse3D &source)
Copy constructor.
void projectPoints(const Indices &inliers, const Eigen::VectorXf &model_coefficients, PointCloud &projected_points, bool copy_data_fields=true) const override
Create a new point cloud with inliers projected onto the 3d ellipse model.
bool computeModelCoefficients(const Indices &samples, Eigen::VectorXf &model_coefficients) const override
Check whether the given index samples can form a valid 3D ellipse model, compute the model coefficien...
SampleConsensusModelEllipse3D(const PointCloudConstPtr &cloud, bool random=false)
Constructor for base SampleConsensusModelEllipse3D.
~SampleConsensusModelEllipse3D() override=default
Empty destructor.
typename SampleConsensusModel< PointT >::PointCloud PointCloud
bool doSamplesVerifyModel(const std::set< index_t > &indices, const Eigen::VectorXf &model_coefficients, const double threshold) const override
Verify whether a subset of indices verifies the given 3d ellipse model coefficients.
void selectWithinDistance(const Eigen::VectorXf &model_coefficients, const double threshold, Indices &inliers) override
Compute all distances from the cloud data to a given 3D ellipse model.
SampleConsensusModelEllipse3D(const PointCloudConstPtr &cloud, const Indices &indices, bool random=false)
Constructor for base SampleConsensusModelEllipse3D.
bool isSampleGood(const Indices &samples) const override
Check if a sample of indices results in a good sample of points indices.
bool isModelValid(const Eigen::VectorXf &model_coefficients) const override
Check whether a model is valid given the user constraints.
void getDistancesToModel(const Eigen::VectorXf &model_coefficients, std::vector< double > &distances) const override
Compute all distances from the cloud data to a given 3D ellipse model.
shared_ptr< SampleConsensusModelEllipse3D< PointT > > Ptr
SampleConsensusModel represents the base model class.
Definition sac_model.h:72
double radius_min_
The minimum and maximum radius limits for the model.
Definition sac_model.h:565
unsigned int sample_size_
The size of a sample from which the model is computed.
Definition sac_model.h:589
typename PointCloud::ConstPtr PointCloudConstPtr
Definition sac_model.h:75
IndicesPtr indices_
A pointer to the vector of point indices to use.
Definition sac_model.h:556
PointCloudConstPtr input_
A boost shared pointer to the point cloud data array.
Definition sac_model.h:553
std::string model_name_
The model name.
Definition sac_model.h:550
unsigned int model_size_
The number of coefficients in the model.
Definition sac_model.h:592
typename PointCloud::Ptr PointCloudPtr
Definition sac_model.h:76
std::vector< double > error_sqr_dists_
A vector holding the distances to the computed model.
Definition sac_model.h:586
void get_ellipse_point(const Eigen::VectorXf &par, float th, float &x, float &y)
Internal function to compute ellipse point from parametric coefficients and angle.
float golden_section_search(const Eigen::VectorXf &par, float u, float v, float th_min, float th_max, float epsilon)
Internal function to find the optimal angle using Golden Section Search.
Eigen::Vector2f dvec2ellipse(const Eigen::VectorXf &par, float u, float v, float &th_opt)
Internal function to compute the shortest distance vector from a point to an ellipse.
PCL_EXPORTS int optimizeModelCoefficientsEllipse3D(Eigen::VectorXf &coeff, const Eigen::ArrayXf &pts_x, const Eigen::ArrayXf &pts_y, const Eigen::ArrayXf &pts_z)
Internal function to optimize ellipse coefficients.
@ SACMODEL_ELLIPSE3D
Definition model_types.h:65
IndicesAllocator<> Indices
Type used for indices in PCL.
Definition types.h:133
#define M_PI
Definition pcl_macros.h:201
A point structure representing Euclidean xyz coordinates, and the RGB color.