43 enum class CylinderFitterType
58 CylinderFitterType fitter_ = CylinderFitterType::HemisphereSearchFit;
61 Eigen::Vector<T, 3> baseCylinderAxis_;
64 size_t thetaResolution_ = 0;
65 size_t phiResolution_ = 0;
66 bool isMultithread_ =
true;
69 std::vector<Eigen::Vector<T, 3>> normalizedPoints_ = {};
79 Eigen::Vector <T, 6> precomputedMu_ = {};
80 Eigen::Matrix <T, 3, 3> precomputedF0_ = {};
81 Eigen::Matrix <T, 3, 6> precomputedF1_ = {};
82 Eigen::Matrix <T, 6, 6> precomputedF2_ = {};
94 precomputedMu_.setZero();
95 precomputedF0_.setZero();
96 precomputedF1_.setZero();
97 precomputedF2_.setZero();
98 normalizedPoints_.clear();
104 thetaResolution_ = theta;
105 phiResolution_ = phi;
106 isMultithread_ = isMultithread;
107 fitter_ = CylinderFitterType::HemisphereSearchFit;
108 assert( thetaResolution_ > 0 );
109 assert( phiResolution_ > 0 );
110 auto result = solve( points, cylinder );
121 fitter_ = CylinderFitterType::SpecificAxisFit;
122 assert( baseCylinderAxis_.isZero() ==
false );
123 auto result = solve( points, cylinder );
131 if ( points.size() < 6 )
133 spdlog::warn(
"Cylinder3Approximation :: Too low point for cylinder approximation count={}" , points.size() );
136 assert( points.size() >= 6 );
138 normalizedPoints_.clear();
141 Eigen::Vector<T, 3> bestPC;
142 Eigen::Vector<T, 3> bestW;
147 updatePrecomputeParams( points, avgPoint );
150 if ( fitter_ == CylinderFitterType::HemisphereSearchFit )
152 if ( isMultithread_ )
153 error = fitCylindeHemisphereMultiThreaded( bestPC, bestW, rootSquare );
156 error = fitCylindeHemisphereSingleThreaded( bestPC, bestW, rootSquare );
158 else if ( fitter_ == CylinderFitterType::SpecificAxisFit )
160 error = SpecificAxisFit( bestPC, bestW, rootSquare );
164 spdlog::warn(
"Cylinder3Approximation :: unsupported fitter" );
169 assert( rootSquare >= 0 );
173 cylinder.
radius = std::sqrt( rootSquare );
177 T hmin = std::numeric_limits<T>::max();
178 T hmax = -std::numeric_limits<T>::max();
180 for (
size_t i = 0; i < points.size(); ++i )
183 hmin = std::min( h, hmin );
184 hmax = std::max( h, hmax );
186 T hmid = ( hmin + hmax ) / 2;
190 cylinder.
length = hmax - hmin;
192 assert( cylinder.
length >= 0 );
200 normalizedPoints_.resize( points.size() );
204 for (
size_t i = 0; i < points.size(); ++i )
205 average += points[i];
206 average = average /
static_cast< T
> ( points.size() );
209 for (
size_t i = 0; i < points.size(); ++i )
210 normalizedPoints_[i] =
toEigen( points[i] - average );
212 const Eigen::Vector<T, 6> zeroEigenVector6{ 0, 0, 0, 0, 0, 0 };
213 std::vector<Eigen::Vector<T, 6>> products( normalizedPoints_.size(), zeroEigenVector6 );
214 precomputedMu_ = zeroEigenVector6;
216 for (
size_t i = 0; i < normalizedPoints_.size(); ++i )
218 products[i][0] = normalizedPoints_[i][0] * normalizedPoints_[i][0];
219 products[i][1] = normalizedPoints_[i][0] * normalizedPoints_[i][1];
220 products[i][2] = normalizedPoints_[i][0] * normalizedPoints_[i][2];
221 products[i][3] = normalizedPoints_[i][1] * normalizedPoints_[i][1];
222 products[i][4] = normalizedPoints_[i][1] * normalizedPoints_[i][2];
223 products[i][5] = normalizedPoints_[i][2] * normalizedPoints_[i][2];
224 precomputedMu_[0] += products[i][0];
225 precomputedMu_[1] += 2 * products[i][1];
226 precomputedMu_[2] += 2 * products[i][2];
227 precomputedMu_[3] += products[i][3];
228 precomputedMu_[4] += 2 * products[i][4];
229 precomputedMu_[5] += products[i][5];
231 precomputedMu_ = precomputedMu_ / points.size();
233 precomputedF0_.setZero();
234 precomputedF1_.setZero();
235 precomputedF2_.setZero();
236 for (
size_t i = 0; i < normalizedPoints_.size(); ++i )
238 Eigen::Vector<T, 6> delta{};
239 delta[0] = products[i][0] - precomputedMu_[0];
240 delta[1] = 2 * products[i][1] - precomputedMu_[1];
241 delta[2] = 2 * products[i][2] - precomputedMu_[2];
242 delta[3] = products[i][3] - precomputedMu_[3];
243 delta[4] = 2 * products[i][4] - precomputedMu_[4];
244 delta[5] = products[i][5] - precomputedMu_[5];
245 precomputedF0_( 0, 0 ) += products[i][0];
246 precomputedF0_( 0, 1 ) += products[i][1];
247 precomputedF0_( 0, 2 ) += products[i][2];
248 precomputedF0_( 1, 1 ) += products[i][3];
249 precomputedF0_( 1, 2 ) += products[i][4];
250 precomputedF0_( 2, 2 ) += products[i][5];
251 precomputedF1_ = precomputedF1_ + normalizedPoints_[i] * delta.transpose();
252 precomputedF2_ += delta * delta.transpose();
254 precomputedF0_ = precomputedF0_ /
static_cast < T
> ( points.size() );
255 precomputedF0_( 1, 0 ) = precomputedF0_( 0, 1 );
256 precomputedF0_( 2, 0 ) = precomputedF0_( 0, 2 );
257 precomputedF0_( 2, 1 ) = precomputedF0_( 1, 2 );
258 precomputedF1_ = precomputedF1_ /
static_cast < T
> ( points.size() );
259 precomputedF2_ = precomputedF2_ /
static_cast < T
> ( points.size() );
266 T G(
const Eigen::Vector<T, 3>& W, Eigen::Vector<T, 3>& PC, T& rsqr )
const
268 Eigen::Matrix<T, 3, 3> P = Eigen::Matrix<T, 3, 3>::Identity() - ( W * W.transpose() );
270 Eigen::Matrix<T, 3, 3> S;
275 Eigen::Matrix<T, 3, 3> A = P * precomputedF0_ * P;
276 Eigen::Matrix<T, 3, 3> hatA = -( S * A * S );
277 Eigen::Matrix<T, 3, 3> hatAA = hatA * A;
278 T trace = hatAA.trace();
283 return std::numeric_limits<T>::max();
285 Eigen::Matrix<T, 3, 3> Q = hatA / trace;
286 Eigen::Vector<T, 6> pVec{ P( 0, 0 ), P( 0, 1 ), P( 0, 2 ), P( 1, 1 ), P( 1, 2 ), P( 2, 2 ) };
287 Eigen::Vector<T, 3> alpha = precomputedF1_ * pVec;
288 Eigen::Vector<T, 3> beta = Q * alpha;
289 T error = ( pVec.dot( precomputedF2_ * pVec ) - 4 * alpha.dot( beta ) + 4 * beta.dot( precomputedF0_ * beta ) ) /
static_cast< T
>( normalizedPoints_.size() );
294 error = std::abs( error );
297 rsqr = std::max( T(0), pVec.dot( precomputedMu_ ) + beta.dot( beta ) );
302 T fitCylindeHemisphereSingleThreaded( Eigen::Vector<T, 3>& PC, Eigen::Vector<T, 3>& W, T& resultedRootSquare )
const
304 T
const theraStep =
static_cast< T
>( 2 * PI ) / thetaResolution_;
305 T
const phiStep =
static_cast< T
>( PI2 ) / phiResolution_;
309 T minError = G( W, PC, resultedRootSquare );
311 for (
size_t j = 1; j <= phiResolution_; ++j )
314 T cosPhi = std::cos( phi );
315 T sinPhi = std::sin( phi );
316 for (
size_t i = 0; i < thetaResolution_; ++i )
318 T theta = theraStep * i;
319 T cosTheta = std::cos( theta );
320 T sinTheta = std::sin( theta );
321 Eigen::Vector<T, 3> currW{ cosTheta * sinPhi, sinTheta * sinPhi, cosPhi };
322 Eigen::Vector<T, 3> currPC{};
324 T error = G( currW, currPC, rsqr );
325 if ( error < minError )
328 resultedRootSquare = rsqr;
338 class BestHemisphereStoredData
341 T error = std::numeric_limits<T>::max();
342 T rootSquare = std::numeric_limits<T>::max();
343 Eigen::Vector<T, 3> W;
344 Eigen::Vector<T, 3> PC;
347 T fitCylindeHemisphereMultiThreaded( Eigen::Vector<T, 3>& PC, Eigen::Vector<T, 3>& W, T& resultedRootSquare )
const
349 T
const theraStep =
static_cast< T
>( 2 * PI ) / thetaResolution_;
350 T
const phiStep =
static_cast< T
>( PI2 ) / phiResolution_;
354 T minError = G( W, PC, resultedRootSquare );
356 std::vector<BestHemisphereStoredData> storedData;
357 storedData.resize( phiResolution_ + 1 );
359 tbb::parallel_for( tbb::blocked_range<size_t>(
size_t( 0 ), phiResolution_ + 1 ),
360 [&] (
const tbb::blocked_range<size_t>& range )
362 for (
size_t j = range.begin(); j < range.end(); ++j )
366 T cosPhi = std::cos( phi );
367 T sinPhi = std::sin( phi );
368 for (
size_t i = 0; i < thetaResolution_; ++i )
371 T theta = theraStep * i;
372 T cosTheta = std::cos( theta );
373 T sinTheta = std::sin( theta );
374 Eigen::Vector<T, 3> currW{ cosTheta * sinPhi, sinTheta * sinPhi, cosPhi };
375 Eigen::Vector<T, 3> currPC{};
377 T error = G( currW, currPC, rsqr );
379 if ( error < storedData[j].error )
381 storedData[j].error = error;
382 storedData[j].rootSquare = rsqr;
383 storedData[j].W = currW;
384 storedData[j].PC = currPC;
391 for (
size_t i = 0; i <= phiResolution_; ++i )
393 if ( storedData[i].error < minError )
395 minError = storedData[i].error;
396 resultedRootSquare = storedData[i].rootSquare;
398 PC = storedData[i].PC;
405 T SpecificAxisFit( Eigen::Vector<T, 3>& PC, Eigen::Vector<T, 3>& W, T& resultedRootSquare )
407 W = baseCylinderAxis_;
408 return G( W, PC, resultedRootSquare );