165 return solveSpecificAxisFit_( points, cone );
168 return solveHemisphereSearchFit_( points, cone );
171 return solveApproximationPCM_( points, cone );
174 return std::numeric_limits<T>::max();
183 Cone3ApproximationParams params_;
187 Cone3<T>& cone,
bool useConeInputAsInitialGuess =
false )
190 ConeFittingFunctor<T> coneFittingFunctor;
191 coneFittingFunctor.setPoints( points );
192 Eigen::LevenbergMarquardt<ConeFittingFunctor<T>, T> lm( coneFittingFunctor );
193 lm.parameters.maxfev = params_.levenbergMarquardtMaxIteration;
196 computeCenterAndNormal_( points, center, U );
199 if ( useConeInputAsInitialGuess )
205 cone = computeInitialCone_( points, center, U );
208 Eigen::VectorX<T> fittedParams( 6 );
209 coneToFitParams_( cone, fittedParams );
210 [[maybe_unused]] Eigen::LevenbergMarquardtSpace::Status result = lm.minimize( fittedParams );
215 fitParamsToCone_( fittedParams, cone );
217 T
const one_v =
static_cast< T
>( 1 );
218 auto cosAngle = std::clamp( one_v / coneAxis.
length(),
static_cast< T
>( 0 ), one_v );
219 cone.angle = std::acos( cosAngle );
220 cone.direction() = cone.direction().normalized();
221 cone.height = calculateConeHeight_( points, cone );
223 return getApproximationRMS_( points, cone );
226 T solveApproximationPCM_(
const std::vector<
MR::Vector3<T>>& points, Cone3<T>& cone )
228 return solveFixedAxis_( points, cone,
false );
231 T solveSpecificAxisFit_(
const std::vector<
MR::Vector3<T>>& points, Cone3<T>& cone )
233 return solveFixedAxis_( points, cone,
true );
237 T solveHemisphereSearchFit_(
const std::vector<
MR::Vector3<T>>& points, Cone3<T>& cone )
239 Vector3<T> center = computeCenter_( points );
240 ConeFittingFunctor<T> coneFittingFunctor;
241 coneFittingFunctor.setPoints( points );
243 constexpr T pi2 =
static_cast< T
>( PI2 );
244 T
const theraStep =
static_cast< T
>( 2 * PI ) / params_.hemisphereSearchPhiResolution;
245 T
const phiStep = pi2 / params_.hemisphereSearchPhiResolution;
249 T minError = std::numeric_limits<T> ::max();
251 std::vector<BestCone> bestCones;
252 bestCones.resize( params_.hemisphereSearchPhiResolution + 1 );
254 ParallelFor( 0, params_.hemisphereSearchPhiResolution + 1, [&] (
int j )
257 T cosPhi = std::cos( phi );
258 T sinPhi = std::sin( phi );
259 for ( size_t i = 0; i < params_.hemisphereSearchThetaResolution; ++i )
261 T theta = theraStep * i;
262 T cosTheta = std::cos( theta );
263 T sinTheta = std::sin( theta );
266 Vector3<T> U( cosTheta * sinPhi, sinTheta * sinPhi, cosPhi );
268 auto tmpCone = computeInitialCone_( points, center, U );
270 Eigen::VectorX<T> fittedParams( 6 );
271 coneToFitParams_( tmpCone, fittedParams );
274 Eigen::LevenbergMarquardt<ConeFittingFunctor<T>, T> lm( coneFittingFunctor );
275 lm.parameters.maxfev = params_.levenbergMarquardtMaxIteration;
276 [[maybe_unused]] Eigen::LevenbergMarquardtSpace::Status result = lm.minimize( fittedParams );
281 fitParamsToCone_( fittedParams, tmpCone );
283 T const one_v = static_cast< T >( 1 );
284 auto cosAngle = std::clamp( one_v / tmpCone.direction().length(), static_cast< T >( 0 ), one_v );
285 tmpCone.angle = std::acos( cosAngle );
286 tmpCone.direction() = tmpCone.direction().normalized();
289 T error = getApproximationRMS_( points, tmpCone );
290 if ( error < bestCones[j].minError )
292 bestCones[j].minError = error;
293 bestCones[j].bestCone = tmpCone;
299 auto bestAppox = std::min_element( bestCones.begin(), bestCones.end(), [] (
const BestCone& a,
const BestCone& b )
301 return a.minError < b.minError;
304 cone = bestAppox->bestCone;
307 cone.height = calculateConeHeight_( points, cone );
309 return bestAppox->minError;
313 T calculateConeHeight_(
const std::vector<
MR::Vector3<T>>& points, Cone3<T>& cone )
315 T
length =
static_cast< T
> ( 0 );
316 for (
auto i = 0; i < points.size(); ++i )
318 length = std::max(
length, std::abs( MR::dot( points[i] - cone.apex(), cone.direction() ) ) );
323 T getApproximationRMS_(
const std::vector<
MR::Vector3<T>>& points,
const Cone3<T>& cone )
325 if ( points.size() == 0 )
326 return std::numeric_limits<T>::max();
329 for (
auto p : points )
330 error = error +
distanceSq( cone.projectPoint( p ), p );
332 return error / points.size();
339 for (
auto i = 0; i < points.size(); ++i )
343 center = center /
static_cast< T
>( points.size() );
350 center = computeCenter_( points );
354 for (
auto i = 0; i < points.size(); ++i )
356 Vector3<T>
Z = points[i] - center;
357 U +=
Z * MR::dot(
Z,
Z );
368 result.direction() = axis;
370 T& coneAngle = result.
angle;
377 std::vector<Vector2<T>> hrPairs( points.size() );
378 T hMin = std::numeric_limits<T>::max(), hMax = -hMin;
379 for (
auto i = 0; i < points.size(); ++i )
382 T h = MR::dot( U, delta );
383 hMin = std::min( hMin, h );
384 hMax = std::max( hMax, h );
385 Vector3<T> projection = delta - MR::dot( U, delta ) * U;
386 T r = projection.
length();
387 hrPairs[i] = { h, r };
395 findBestFitLine_( hrPairs, a, b, &avgPoint );
396 T hAverage = avgPoint.
x;
397 T rAverage = avgPoint.
y;
407 std::swap( hMin, hMax );
413 T rMin = rAverage + hrSlope * ( hMin - hAverage );
414 T rMax = rAverage + hrSlope * ( hMax - hAverage );
415 T hRange = hMax - hMin;
416 T rRange = rMax - rMin;
419 T tanAngle = rRange / hRange;
420 coneAngle = std::atan2( rRange, hRange );
423 T offset = rMax / tanAngle - hMax;
424 coneApex = center - offset * U;
431 auto numPoints = xyPairs.size();
432 Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> A( numPoints, 2 );
433 Eigen::Vector<T, Eigen::Dynamic> b( numPoints );
435 for (
auto i = 0; i < numPoints; ++i )
437 A( i, 0 ) = xyPairs[i].x;
439 b( i ) = xyPairs[i].y;
441 *avg = *avg + xyPairs[i];
445 *avg = *avg /
static_cast < T
> ( xyPairs.size() );
448 MR_SUPPRESS_WARNING_PUSH
449 MR_SUPPRESS_WARNING(
"-Wdeprecated-declarations", 4996 )
450 Eigen::Matrix<T, Eigen::Dynamic, 1> x = A.bdcSvd( Eigen::ComputeThinU | Eigen::ComputeThinV ).solve( b );
451 MR_SUPPRESS_WARNING_POP
458 *avg = *avg /
static_cast < T
> ( xyPairs.size() );
459 avg->y = lineA * avg->x + lineB;
464 void fitParamsToCone_( Eigen::Vector<T, Eigen::Dynamic>& fittedParams, Cone3<T>& cone )
466 cone.apex().x = fittedParams[0];
467 cone.apex().y = fittedParams[1];
468 cone.apex().z = fittedParams[2];
470 cone.direction().x = fittedParams[3];
471 cone.direction().y = fittedParams[4];
472 cone.direction().z = fittedParams[5];
476 void coneToFitParams_( Cone3<T>& cone, Eigen::Vector<T, Eigen::Dynamic>& fittedParams )
479 fittedParams[0] = cone.apex().x;
480 fittedParams[1] = cone.apex().y;
481 fittedParams[2] = cone.apex().z;
484 T coneCosAngle = std::cos( cone.angle );
485 fittedParams[3] = cone.direction().x / coneCosAngle;
486 fittedParams[4] = cone.direction().y / coneCosAngle;
487 fittedParams[5] = cone.direction().z / coneCosAngle;