Point Cloud Library (PCL) 1.15.1-dev
Loading...
Searching...
No Matches
sac_model_ellipse3d.hpp
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 <limits>
13
14#include <pcl/sample_consensus/sac_model_ellipse3d.h>
15#include <pcl/common/concatenate.h>
16
17#include <Eigen/Eigenvalues>
18
19
20//////////////////////////////////////////////////////////////////////////
21template <typename PointT> bool
23 const Indices &samples) const
24{
25 if (samples.size () != sample_size_)
26 {
27 PCL_ERROR ("[pcl::SampleConsensusModelEllipse3D::isSampleGood] Wrong number of samples (is %lu, should be %lu)!\n", samples.size (), sample_size_);
28 return (false);
29 }
30
31 // Use three points out of the 6 samples
32 const Eigen::Vector3d p0 ((*input_)[samples[0]].x, (*input_)[samples[0]].y, (*input_)[samples[0]].z);
33 const Eigen::Vector3d p1 ((*input_)[samples[1]].x, (*input_)[samples[1]].y, (*input_)[samples[1]].z);
34 const Eigen::Vector3d p2 ((*input_)[samples[2]].x, (*input_)[samples[2]].y, (*input_)[samples[2]].z);
35
36 // Check if the squared norm of the cross-product is non-zero, otherwise
37 // common_helper_vec, which plays an important role in computeModelCoefficients,
38 // would likely be ill-formed.
39 if ((p1 - p0).cross(p1 - p2).squaredNorm() < Eigen::NumTraits<float>::dummy_precision ())
40 {
41 PCL_ERROR ("[pcl::SampleConsensusModelEllipse3D::isSampleGood] Sample points too similar or collinear!\n");
42 return (false);
43 }
44
45 return (true);
46}
47
48//////////////////////////////////////////////////////////////////////////
49template <typename PointT> bool
50pcl::SampleConsensusModelEllipse3D<PointT>::computeModelCoefficients (const Indices &samples, Eigen::VectorXf &model_coefficients) const
51{
52 // Uses 6 samples
53 if (samples.size () != sample_size_)
54 {
55 PCL_ERROR ("[pcl::SampleConsensusModelEllipse3D::computeModelCoefficients] Invalid set of samples given (%lu)!\n", samples.size ());
56 return (false);
57 }
58
59 model_coefficients.resize (model_size_); // 11 coefs
60
61 const Eigen::Vector3f p0((*input_)[samples[0]].x, (*input_)[samples[0]].y, (*input_)[samples[0]].z);
62 const Eigen::Vector3f p1((*input_)[samples[1]].x, (*input_)[samples[1]].y, (*input_)[samples[1]].z);
63 const Eigen::Vector3f p2((*input_)[samples[2]].x, (*input_)[samples[2]].y, (*input_)[samples[2]].z);
64 const Eigen::Vector3f p3((*input_)[samples[3]].x, (*input_)[samples[3]].y, (*input_)[samples[3]].z);
65 const Eigen::Vector3f p4((*input_)[samples[4]].x, (*input_)[samples[4]].y, (*input_)[samples[4]].z);
66 const Eigen::Vector3f p5((*input_)[samples[5]].x, (*input_)[samples[5]].y, (*input_)[samples[5]].z);
67
68 const Eigen::Vector3f common_helper_vec = (p1 - p0).cross(p1 - p2);
69 const Eigen::Vector3f ellipse_normal = common_helper_vec.normalized();
70
71 // The same check is implemented in isSampleGood, so be sure to look there too
72 // if you find the need to change something here.
73 if (common_helper_vec.squaredNorm() < Eigen::NumTraits<float>::dummy_precision ())
74 {
75 PCL_ERROR ("[pcl::SampleConsensusModelEllipse3D::computeModelCoefficients] Sample points too similar or collinear!\n");
76 return (false);
77 }
78
79 // Definition of the local reference frame of the ellipse
80 Eigen::Vector3f x_axis = (p1 - p0).normalized();
81 const Eigen::Vector3f z_axis = ellipse_normal.normalized();
82 const Eigen::Vector3f y_axis = z_axis.cross(x_axis).normalized();
83
84 // Compute the rotation matrix and its transpose
85 const Eigen::Matrix3f Rot = (Eigen::Matrix3f(3,3)
86 << x_axis(0), y_axis(0), z_axis(0),
87 x_axis(1), y_axis(1), z_axis(1),
88 x_axis(2), y_axis(2), z_axis(2))
89 .finished();
90 const Eigen::Matrix3f Rot_T = Rot.transpose();
91
92 // Convert the points to local coordinates
93 const Eigen::Vector3f p0_ = Rot_T * (p0 - p0);
94 const Eigen::Vector3f p1_ = Rot_T * (p1 - p0);
95 const Eigen::Vector3f p2_ = Rot_T * (p2 - p0);
96 const Eigen::Vector3f p3_ = Rot_T * (p3 - p0);
97 const Eigen::Vector3f p4_ = Rot_T * (p4 - p0);
98 const Eigen::Vector3f p5_ = Rot_T * (p5 - p0);
99
100
101 // Fit an ellipse to the samples to obtain its conic equation parameters
102 // (this implementation follows the manuscript "Direct Least Square Fitting of Ellipses"
103 // A. Fitzgibbon, M. Pilu and R. Fisher, IEEE TPAMI, 21(5) : 476–480, May 1999).
104
105 // xOy projections only
106 const Eigen::VectorXf X = (Eigen::VectorXf(6) << p0_(0), p1_(0), p2_(0), p3_(0), p4_(0), p5_(0)).finished();
107 const Eigen::VectorXf Y = (Eigen::VectorXf(6) << p0_(1), p1_(1), p2_(1), p3_(1), p4_(1), p5_(1)).finished();
108
109 // Design matrix D
110 const Eigen::MatrixXf D = (Eigen::MatrixXf(6,6)
111 << X(0) * X(0), X(0) * Y(0), Y(0) * Y(0), X(0), Y(0), 1.0,
112 X(1) * X(1), X(1) * Y(1), Y(1) * Y(1), X(1), Y(1), 1.0,
113 X(2) * X(2), X(2) * Y(2), Y(2) * Y(2), X(2), Y(2), 1.0,
114 X(3) * X(3), X(3) * Y(3), Y(3) * Y(3), X(3), Y(3), 1.0,
115 X(4) * X(4), X(4) * Y(4), Y(4) * Y(4), X(4), Y(4), 1.0,
116 X(5) * X(5), X(5) * Y(5), Y(5) * Y(5), X(5), Y(5), 1.0)
117 .finished();
118
119 // Scatter matrix S
120 const Eigen::MatrixXf S = D.transpose() * D;
121
122 // Constraint matrix C
123 const Eigen::MatrixXf C = (Eigen::MatrixXf(6,6)
124 << 0.0, 0.0, -2.0, 0.0, 0.0, 0.0,
125 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
126 -2.0, 0.0, 0.0, 0.0, 0.0, 0.0,
127 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
128 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
129 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
130 .finished();
131
132 // Solve the Generalized Eigensystem: S*a = lambda*C*a
133 Eigen::GeneralizedEigenSolver<Eigen::MatrixXf> solver;
134 solver.compute(S, C);
135 const Eigen::VectorXf eigvals = solver.eigenvalues().real();
136
137 // Find the negative eigenvalue 'neigvec' (the largest, if many exist)
138 int idx(-1);
139 float absmin(0.0);
140 for (size_t i(0); i < static_cast<size_t>(eigvals.size()); ++i) {
141 if (eigvals(i) < absmin && !std::isinf(eigvals(i))) {
142 idx = i;
143 }
144 }
145 // Return "false" in case the negative eigenvalue was not found
146 if (idx == -1) {
147 PCL_DEBUG("[pcl::SampleConsensusModelEllipse3D::computeModelCoefficients] Failed to find the negative eigenvalue in the GES.\n");
148 return (false);
149 }
150 const Eigen::VectorXf neigvec = solver.eigenvectors().real().col(idx).normalized();
151
152
153 // Convert the conic model parameters to parametric ones
154
155 // Conic parameters
156 const float con_A(neigvec(0));
157 const float con_B(neigvec(1));
158 const float con_C(neigvec(2));
159 const float con_D(neigvec(3));
160 const float con_E(neigvec(4));
161 const float con_F(neigvec(5));
162
163 // Build matrix M0
164 const Eigen::Matrix3f M0 = (Eigen::Matrix3f()
165 << con_F, con_D/2.0, con_E/2.0,
166 con_D/2.0, con_A, con_B/2.0,
167 con_E/2.0, con_B/2.0, con_C)
168 .finished();
169
170 // Build matrix M
171 const Eigen::Matrix2f M = (Eigen::Matrix2f()
172 << con_A, con_B/2.0,
173 con_B/2.0, con_C)
174 .finished();
175
176 // Calculate the eigenvalues and eigenvectors of matrix M
177 Eigen::SelfAdjointEigenSolver<Eigen::Matrix2f> solver_M;
178 solver_M.computeDirect(M, Eigen::EigenvaluesOnly);
179
180 Eigen::Vector2f eigvals_M = solver_M.eigenvalues();
181
182 // Order the eigenvalues so that |lambda_0 - con_A| <= |lambda_0 - con_C|
183 float aux_eigval(0.0);
184 if (std::abs(eigvals_M(0) - con_A) > std::abs(eigvals_M(0) - con_C)) {
185 aux_eigval = eigvals_M(0);
186 eigvals_M(0) = eigvals_M(1);
187 eigvals_M(1) = aux_eigval;
188 }
189
190 // Parametric parameters of the ellipse
191 float par_a = std::sqrt(-M0.determinant() / (M.determinant() * eigvals_M(0)));
192 float par_b = std::sqrt(-M0.determinant() / (M.determinant() * eigvals_M(1)));
193 const float par_h = (con_B * con_E - 2.0 * con_C * con_D) / (4.0 * con_A * con_C - std::pow(con_B, 2));
194 const float par_k = (con_B * con_D - 2.0 * con_A * con_E) / (4.0 * con_A * con_C - std::pow(con_B, 2));
195 const float par_t = (M_PI / 2.0 - std::atan((con_A - con_C) / con_B)) / 2.0; // equivalent to: acot((con_A - con_C) / con_B) / 2.0;
196
197 // Convert the center point of the ellipse to global coordinates
198 // (the if statement ensures that 'par_a' always refers to the semi-major axis length)
199 Eigen::Vector3f p_ctr;
200 float aux_par(0.0);
201 if (par_a > par_b) {
202 p_ctr.noalias() = p0 + Rot * Eigen::Vector3f(par_h, par_k, 0.0);
203 } else {
204 aux_par = par_a;
205 par_a = par_b;
206 par_b = aux_par;
207 p_ctr.noalias() = p0 + Rot * Eigen::Vector3f(par_k, par_h, 0.0);
208 }
209
210 // Center (x, y, z)
211 model_coefficients[0] = static_cast<float>(p_ctr(0));
212 model_coefficients[1] = static_cast<float>(p_ctr(1));
213 model_coefficients[2] = static_cast<float>(p_ctr(2));
214
215 // Semi-major axis length 'a' (along the local x-axis)
216 model_coefficients[3] = static_cast<float>(par_a);
217 // Semi-minor axis length 'b' (along the local y-axis)
218 model_coefficients[4] = static_cast<float>(par_b);
219
220 // Ellipse normal
221 model_coefficients[5] = static_cast<float>(ellipse_normal[0]);
222 model_coefficients[6] = static_cast<float>(ellipse_normal[1]);
223 model_coefficients[7] = static_cast<float>(ellipse_normal[2]);
224
225 // Retrieve the ellipse point at the tilt angle t (par_t), along the local x-axis
226 const Eigen::VectorXf params = (Eigen::VectorXf(5) << par_a, par_b, par_h, par_k, par_t).finished();
227 Eigen::Vector3f p_th_(0.0, 0.0, 0.0);
228 internal::get_ellipse_point(params, par_t, p_th_(0), p_th_(1));
229
230 // Redefinition of the x-axis of the ellipse's local reference frame
231 x_axis = (Rot * p_th_).normalized();
232 model_coefficients[8] = static_cast<float>(x_axis[0]);
233 model_coefficients[9] = static_cast<float>(x_axis[1]);
234 model_coefficients[10] = static_cast<float>(x_axis[2]);
235
236
237 PCL_DEBUG ("[pcl::SampleConsensusModelEllipse3D::computeModelCoefficients] Model is (%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g).\n",
238 model_coefficients[0], model_coefficients[1], model_coefficients[2], model_coefficients[3],
239 model_coefficients[4], model_coefficients[5], model_coefficients[6], model_coefficients[7],
240 model_coefficients[8], model_coefficients[9], model_coefficients[10]);
241 return (true);
242}
243
244
245//////////////////////////////////////////////////////////////////////////
246template <typename PointT> void
247pcl::SampleConsensusModelEllipse3D<PointT>::getDistancesToModel (const Eigen::VectorXf &model_coefficients, std::vector<double> &distances) const
248{
249 // Check if the model is valid given the user constraints
250 if (!isModelValid (model_coefficients))
251 {
252 distances.clear ();
253 return;
254 }
255 distances.resize (indices_->size ());
256
257 // c : Ellipse Center
258 const Eigen::Vector3f c(model_coefficients[0], model_coefficients[1], model_coefficients[2]);
259 // n : Ellipse (Plane) Normal
260 const Eigen::Vector3f n_axis(model_coefficients[5], model_coefficients[6], model_coefficients[7]);
261 // x : Ellipse (Plane) X-Axis
262 const Eigen::Vector3f x_axis(model_coefficients[8], model_coefficients[9], model_coefficients[10]);
263 // y : Ellipse (Plane) Y-Axis
264 const Eigen::Vector3f y_axis = n_axis.cross(x_axis).normalized();
265 // a : Ellipse semi-major axis (X) length
266 const float par_a(model_coefficients[3]);
267 // b : Ellipse semi-minor axis (Y) length
268 const float par_b(model_coefficients[4]);
269
270 // Compute the rotation matrix and its transpose
271 const Eigen::Matrix3f Rot = (Eigen::Matrix3f(3,3)
272 << x_axis(0), y_axis(0), n_axis(0),
273 x_axis(1), y_axis(1), n_axis(1),
274 x_axis(2), y_axis(2), n_axis(2))
275 .finished();
276 const Eigen::Matrix3f Rot_T = Rot.transpose();
277
278 // Ellipse parameters
279 const Eigen::VectorXf params = (Eigen::VectorXf(5) << par_a, par_b, 0.0, 0.0, 0.0).finished();
280 float th_opt;
281
282 // Iterate through the 3D points and calculate the distances from them to the ellipse
283 std::size_t i = 0;
284 for (const auto& index : *indices_)
285 {
286 // Calculate the distance from the point to the ellipse:
287 // 1. calculate intersection point of the plane in which the ellipse lies and the
288 // line from the sample point with the direction of the plane normal (projected point)
289 // 2. calculate the intersection point of the line from the ellipse center to the projected point
290 // with the ellipse
291 // 3. calculate distance from corresponding point on the ellipse to the sample point
292
293 // p : Sample Point
294 const Eigen::Vector3f p((*input_)[index].x, (*input_)[index].y, (*input_)[index].z);
295
296 // Local coordinates of sample point p
297 const Eigen::Vector3f p_ = Rot_T * (p - c);
298
299 // k : Point on Ellipse
300 // Calculate the shortest distance from the point to the ellipse which is given by
301 // the norm of a vector that is normal to the ellipse tangent calculated at the
302 // point it intersects the tangent.
303 const Eigen::Vector2f distanceVector = internal::dvec2ellipse(params, p_(0), p_(1), th_opt);
304
305 distances[i++] = distanceVector.norm();
306 }
307}
308
309//////////////////////////////////////////////////////////////////////////
310template <typename PointT> void
312 const Eigen::VectorXf &model_coefficients, const double threshold,
313 Indices &inliers)
314{
315 inliers.clear();
316 error_sqr_dists_.clear();
317 // Check if the model is valid given the user constraints
318 if (!isModelValid (model_coefficients))
319 {
320 return;
321 }
322 inliers.reserve (indices_->size ());
323 error_sqr_dists_.reserve (indices_->size ());
324
325 // c : Ellipse Center
326 const Eigen::Vector3f c(model_coefficients[0], model_coefficients[1], model_coefficients[2]);
327 // n : Ellipse (Plane) Normal
328 const Eigen::Vector3f n_axis(model_coefficients[5], model_coefficients[6], model_coefficients[7]);
329 // x : Ellipse (Plane) X-Axis
330 const Eigen::Vector3f x_axis(model_coefficients[8], model_coefficients[9], model_coefficients[10]);
331 // y : Ellipse (Plane) Y-Axis
332 const Eigen::Vector3f y_axis = n_axis.cross(x_axis).normalized();
333 // a : Ellipse semi-major axis (X) length
334 const float par_a(model_coefficients[3]);
335 // b : Ellipse semi-minor axis (Y) length
336 const float par_b(model_coefficients[4]);
337
338 // Compute the rotation matrix and its transpose
339 const Eigen::Matrix3f Rot = (Eigen::Matrix3f(3,3)
340 << x_axis(0), y_axis(0), n_axis(0),
341 x_axis(1), y_axis(1), n_axis(1),
342 x_axis(2), y_axis(2), n_axis(2))
343 .finished();
344 const Eigen::Matrix3f Rot_T = Rot.transpose();
345
346 const auto squared_threshold = threshold * threshold;
347 // Iterate through the 3d points and calculate the distances from them to the ellipse
348 for (const auto& index : *indices_)
349 {
350 // p : Sample Point
351 const Eigen::Vector3f p((*input_)[index].x, (*input_)[index].y, (*input_)[index].z);
352
353 // Local coordinates of sample point p
354 const Eigen::Vector3f p_ = Rot_T * (p - c);
355
356 // k : Point on Ellipse
357 // Calculate the shortest distance from the point to the ellipse which is given by
358 // the norm of a vector that is normal to the ellipse tangent calculated at the
359 // point it intersects the tangent.
360 const Eigen::VectorXf params = (Eigen::VectorXf(5) << par_a, par_b, 0.0, 0.0, 0.0).finished();
361 float th_opt;
362 const Eigen::Vector2f distanceVector = internal::dvec2ellipse(params, p_(0), p_(1), th_opt);
363
364 const double sqr_dist = distanceVector.squaredNorm();
365 if (sqr_dist < squared_threshold)
366 {
367 // Returns the indices of the points whose distances are smaller than the threshold
368 inliers.push_back (index);
369 error_sqr_dists_.push_back (sqr_dist);
370 }
371 }
372}
373
374//////////////////////////////////////////////////////////////////////////
375template <typename PointT> std::size_t
377 const Eigen::VectorXf &model_coefficients, const double threshold) const
378{
379 // Check if the model is valid given the user constraints
380 if (!isModelValid (model_coefficients))
381 return (0);
382 std::size_t nr_p = 0;
383
384 // c : Ellipse Center
385 const Eigen::Vector3f c(model_coefficients[0], model_coefficients[1], model_coefficients[2]);
386 // n : Ellipse (Plane) Normal
387 const Eigen::Vector3f n_axis(model_coefficients[5], model_coefficients[6], model_coefficients[7]);
388 // x : Ellipse (Plane) X-Axis
389 const Eigen::Vector3f x_axis(model_coefficients[8], model_coefficients[9], model_coefficients[10]);
390 // y : Ellipse (Plane) Y-Axis
391 const Eigen::Vector3f y_axis = n_axis.cross(x_axis).normalized();
392 // a : Ellipse semi-major axis (X) length
393 const float par_a(model_coefficients[3]);
394 // b : Ellipse semi-minor axis (Y) length
395 const float par_b(model_coefficients[4]);
396
397 // Compute the rotation matrix and its transpose
398 const Eigen::Matrix3f Rot = (Eigen::Matrix3f(3,3)
399 << x_axis(0), y_axis(0), n_axis(0),
400 x_axis(1), y_axis(1), n_axis(1),
401 x_axis(2), y_axis(2), n_axis(2))
402 .finished();
403 const Eigen::Matrix3f Rot_T = Rot.transpose();
404
405 const auto squared_threshold = threshold * threshold;
406 // Iterate through the 3d points and calculate the distances from them to the ellipse
407 for (const auto& index : *indices_)
408 {
409 // p : Sample Point
410 const Eigen::Vector3f p((*input_)[index].x, (*input_)[index].y, (*input_)[index].z);
411
412 // Local coordinates of sample point p
413 const Eigen::Vector3f p_ = Rot_T * (p - c);
414
415 // k : Point on Ellipse
416 // Calculate the shortest distance from the point to the ellipse which is given by
417 // the norm of a vector that is normal to the ellipse tangent calculated at the
418 // point it intersects the tangent.
419 const Eigen::VectorXf params = (Eigen::VectorXf(5) << par_a, par_b, 0.0, 0.0, 0.0).finished();
420 float th_opt;
421 const Eigen::Vector2f distanceVector = internal::dvec2ellipse(params, p_(0), p_(1), th_opt);
422
423 if (distanceVector.squaredNorm() < squared_threshold)
424 nr_p++;
425 }
426 return (nr_p);
427}
428
429//////////////////////////////////////////////////////////////////////////
430template <typename PointT> void
432 const Indices &inliers,
433 const Eigen::VectorXf &model_coefficients,
434 Eigen::VectorXf &optimized_coefficients) const
435{
436 optimized_coefficients = model_coefficients;
437
438 // Needs a set of valid model coefficients
439 if (!isModelValid (model_coefficients))
440 {
441 PCL_ERROR ("[pcl::SampleConsensusModelEllipse3D::optimizeModelCoefficients] Given model is invalid!\n");
442 return;
443 }
444
445 // Need more than the minimum sample size to make a difference
446 if (inliers.size () <= sample_size_)
447 {
448 PCL_ERROR ("[pcl::SampleConsensusModelEllipse3D::optimizeModelCoefficients] Not enough inliers found to optimize model coefficients (%lu)! Returning the same coefficients.\n", inliers.size ());
449 return;
450 }
451
452 Eigen::ArrayXf x (inliers.size ()), y (inliers.size ()), z (inliers.size ());
453 std::size_t i = 0;
454 for (const auto& inlier : inliers)
455 {
456 const auto& pt = (*input_)[inlier];
457 x[i] = pt.x; y[i] = pt.y; z[i] = pt.z;
458 ++i;
459 }
460
461 internal::optimizeModelCoefficientsEllipse3D (optimized_coefficients, x, y, z);
462}
463
464//////////////////////////////////////////////////////////////////////////
465template <typename PointT> void
467 const Indices &inliers,
468 const Eigen::VectorXf &model_coefficients,
469 PointCloud &projected_points,
470 bool copy_data_fields) const
471{
472 // Needs a valid set of model coefficients
473 if (!isModelValid (model_coefficients))
474 {
475 PCL_ERROR ("[pcl::SampleConsensusModelEllipse3D::projectPoints] Given model is invalid!\n");
476 return;
477 }
478
479 projected_points.header = input_->header;
480 projected_points.is_dense = input_->is_dense;
481
482 // Copy all the data fields from the input cloud to the projected one?
483 if (copy_data_fields)
484 {
485 // Allocate enough space and copy the basics
486 projected_points.resize (input_->size ());
487 projected_points.width = input_->width;
488 projected_points.height = input_->height;
489
490 using FieldList = typename pcl::traits::fieldList<PointT>::type;
491 // Iterate over each point
492 for (std::size_t i = 0; i < projected_points.size(); ++i)
493 {
494 // Iterate over each dimension
495 pcl::for_each_type<FieldList>(NdConcatenateFunctor<PointT, PointT>((*input_)[i], projected_points[i]));
496 }
497
498 // c : Ellipse Center
499 const Eigen::Vector3f c(model_coefficients[0], model_coefficients[1], model_coefficients[2]);
500 // n : Ellipse (Plane) Normal
501 const Eigen::Vector3f n_axis(model_coefficients[5], model_coefficients[6], model_coefficients[7]);
502 // x : Ellipse (Plane) X-Axis
503 const Eigen::Vector3f x_axis(model_coefficients[8], model_coefficients[9], model_coefficients[10]);
504 // y : Ellipse (Plane) Y-Axis
505 const Eigen::Vector3f y_axis = n_axis.cross(x_axis).normalized();
506 // a : Ellipse semi-major axis (X) length
507 const float par_a(model_coefficients[3]);
508 // b : Ellipse semi-minor axis (Y) length
509 const float par_b(model_coefficients[4]);
510
511 // Compute the rotation matrix and its transpose
512 const Eigen::Matrix3f Rot = (Eigen::Matrix3f(3,3)
513 << x_axis(0), y_axis(0), n_axis(0),
514 x_axis(1), y_axis(1), n_axis(1),
515 x_axis(2), y_axis(2), n_axis(2))
516 .finished();
517 const Eigen::Matrix3f Rot_T = Rot.transpose();
518
519 // Iterate through the 3d points and calculate the distances from them to the plane
520 for (const auto& inlier : inliers)
521 {
522 // p : Sample Point
523 const Eigen::Vector3f p((*input_)[inlier].x, (*input_)[inlier].y, (*input_)[inlier].z);
524
525 // Local coordinates of sample point p
526 const Eigen::Vector3f p_ = Rot_T * (p - c);
527
528 // k : Point on Ellipse
529 // Calculate the shortest distance from the point to the ellipse which is given by
530 // the norm of a vector that is normal to the ellipse tangent calculated at the
531 // point it intersects the tangent.
532 const Eigen::VectorXf params = (Eigen::VectorXf(5) << par_a, par_b, 0.0, 0.0, 0.0).finished();
533 float th_opt;
534 internal::dvec2ellipse(params, p_(0), p_(1), th_opt);
535
536 // Retrieve the ellipse point at the tilt angle t, along the local x-axis
537 Eigen::Vector3f k_(0.0, 0.0, 0.0);
538 internal::get_ellipse_point(params, th_opt, k_[0], k_[1]);
539
540 const Eigen::Vector3f k = c + Rot * k_;
541
542 projected_points[inlier].x = static_cast<float> (k[0]);
543 projected_points[inlier].y = static_cast<float> (k[1]);
544 projected_points[inlier].z = static_cast<float> (k[2]);
545 }
546 }
547 else
548 {
549 // Allocate enough space and copy the basics
550 projected_points.resize (inliers.size ());
551 projected_points.width = inliers.size ();
552 projected_points.height = 1;
553
554 using FieldList = typename pcl::traits::fieldList<PointT>::type;
555 // Iterate over each point
556 for (std::size_t i = 0; i < inliers.size (); ++i)
557 // Iterate over each dimension
558 pcl::for_each_type <FieldList> (NdConcatenateFunctor <PointT, PointT> ((*input_)[inliers[i]], projected_points[i]));
559
560 // c : Ellipse Center
561 const Eigen::Vector3f c(model_coefficients[0], model_coefficients[1], model_coefficients[2]);
562 // n : Ellipse (Plane) Normal
563 const Eigen::Vector3f n_axis(model_coefficients[5], model_coefficients[6], model_coefficients[7]);
564 // x : Ellipse (Plane) X-Axis
565 const Eigen::Vector3f x_axis(model_coefficients[8], model_coefficients[9], model_coefficients[10]);
566 // y : Ellipse (Plane) Y-Axis
567 const Eigen::Vector3f y_axis = n_axis.cross(x_axis).normalized();
568 // a : Ellipse semi-major axis (X) length
569 const float par_a(model_coefficients[3]);
570 // b : Ellipse semi-minor axis (Y) length
571 const float par_b(model_coefficients[4]);
572
573 // Compute the rotation matrix and its transpose
574 const Eigen::Matrix3f Rot = (Eigen::Matrix3f(3,3)
575 << x_axis(0), y_axis(0), n_axis(0),
576 x_axis(1), y_axis(1), n_axis(1),
577 x_axis(2), y_axis(2), n_axis(2))
578 .finished();
579 const Eigen::Matrix3f Rot_T = Rot.transpose();
580
581 // Iterate through the 3d points and calculate the distances from them to the plane
582 std::size_t i = 0;
583 for (const auto& inlier : inliers)
584 {
585 // p : Sample Point
586 const Eigen::Vector3f p((*input_)[inlier].x, (*input_)[inlier].y, (*input_)[inlier].z);
587
588 // Local coordinates of sample point p
589 const Eigen::Vector3f p_ = Rot_T * (p - c);
590
591 // k : Point on Ellipse
592 // Calculate the shortest distance from the point to the ellipse which is given by
593 // the norm of a vector that is normal to the ellipse tangent calculated at the
594 // point it intersects the tangent.
595 const Eigen::VectorXf params = (Eigen::VectorXf(5) << par_a, par_b, 0.0, 0.0, 0.0).finished();
596 float th_opt;
597 internal::dvec2ellipse(params, p_(0), p_(1), th_opt);
598
599 // Retrieve the ellipse point at the tilt angle t, along the local x-axis
600 Eigen::Vector3f k_(0.0, 0.0, 0.0);
601 internal::get_ellipse_point(params, th_opt, k_[0], k_[1]);
602
603 const Eigen::Vector3f k = c + Rot * k_;
604
605 projected_points[i].x = static_cast<float> (k[0]);
606 projected_points[i].y = static_cast<float> (k[1]);
607 projected_points[i++].z = static_cast<float> (k[2]);
608 }
609 }
610}
611
612//////////////////////////////////////////////////////////////////////////
613template <typename PointT> bool
615 const std::set<index_t> &indices,
616 const Eigen::VectorXf &model_coefficients,
617 const double threshold) const
618{
619 // Needs a valid model coefficients
620 if (!isModelValid (model_coefficients))
621 {
622 PCL_ERROR ("[pcl::SampleConsensusModelEllipse3D::doSamplesVerifyModel] Given model is invalid!\n");
623 return (false);
624 }
625
626 // c : Ellipse Center
627 const Eigen::Vector3f c(model_coefficients[0], model_coefficients[1], model_coefficients[2]);
628 // n : Ellipse (Plane) Normal
629 const Eigen::Vector3f n_axis(model_coefficients[5], model_coefficients[6], model_coefficients[7]);
630 // x : Ellipse (Plane) X-Axis
631 const Eigen::Vector3f x_axis(model_coefficients[8], model_coefficients[9], model_coefficients[10]);
632 // y : Ellipse (Plane) Y-Axis
633 const Eigen::Vector3f y_axis = n_axis.cross(x_axis).normalized();
634 // a : Ellipse semi-major axis (X) length
635 const float par_a(model_coefficients[3]);
636 // b : Ellipse semi-minor axis (Y) length
637 const float par_b(model_coefficients[4]);
638
639 // Compute the rotation matrix and its transpose
640 const Eigen::Matrix3f Rot = (Eigen::Matrix3f(3,3)
641 << x_axis(0), y_axis(0), n_axis(0),
642 x_axis(1), y_axis(1), n_axis(1),
643 x_axis(2), y_axis(2), n_axis(2))
644 .finished();
645 const Eigen::Matrix3f Rot_T = Rot.transpose();
646
647 const auto squared_threshold = threshold * threshold;
648 for (const auto &index : indices)
649 {
650 // p : Sample Point
651 const Eigen::Vector3f p((*input_)[index].x, (*input_)[index].y, (*input_)[index].z);
652
653 // Local coordinates of sample point p
654 const Eigen::Vector3f p_ = Rot_T * (p - c);
655
656 // k : Point on Ellipse
657 // Calculate the shortest distance from the point to the ellipse which is given by
658 // the norm of a vector that is normal to the ellipse tangent calculated at the
659 // point it intersects the tangent.
660 const Eigen::VectorXf params = (Eigen::VectorXf(5) << par_a, par_b, 0.0, 0.0, 0.0).finished();
661 float th_opt;
662 const Eigen::Vector2f distanceVector = internal::dvec2ellipse(params, p_(0), p_(1), th_opt);
663
664 if (distanceVector.squaredNorm() > squared_threshold)
665 return (false);
666 }
667 return (true);
668}
669
670//////////////////////////////////////////////////////////////////////////
671template <typename PointT> bool
672pcl::SampleConsensusModelEllipse3D<PointT>::isModelValid (const Eigen::VectorXf &model_coefficients) const
673{
674 if (!SampleConsensusModel<PointT>::isModelValid (model_coefficients))
675 return (false);
676
677 if (radius_min_ != std::numeric_limits<double>::lowest() && (model_coefficients[3] < radius_min_ || model_coefficients[4] < radius_min_))
678 {
679 PCL_DEBUG ("[pcl::SampleConsensusModelEllipse3D::isModelValid] Semi-minor axis OR semi-major axis (radii) of ellipse is/are too small: should be larger than %g, but are {%g, %g}.\n",
680 radius_min_, model_coefficients[3], model_coefficients[4]);
681 return (false);
682 }
683 if (radius_max_ != std::numeric_limits<double>::max() && (model_coefficients[3] > radius_max_ || model_coefficients[4] > radius_max_))
684 {
685 PCL_DEBUG ("[pcl::SampleConsensusModelEllipse3D::isModelValid] Semi-minor axis OR semi-major axis (radii) of ellipse is/are too big: should be smaller than %g, but are {%g, %g}.\n",
686 radius_max_, model_coefficients[3], model_coefficients[4]);
687 return (false);
688 }
689
690 return (true);
691}
692
693#define PCL_INSTANTIATE_SampleConsensusModelEllipse3D(T) template class PCL_EXPORTS pcl::SampleConsensusModelEllipse3D<T>;
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.
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.
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...
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.
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.
SampleConsensusModel represents the base model class.
Definition sac_model.h:72
IndicesAllocator<> Indices
Type used for indices in PCL.
Definition types.h:133
#define M_PI
Definition pcl_macros.h:201
Helper functor structure for concatenate.
Definition concatenate.h:50