1 /*
2 * Copyright (C) 2017 Alberto Irurueta Carro (alberto@irurueta.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.irurueta.geometry.estimators;
17
18 import com.irurueta.geometry.CoordinatesType;
19 import com.irurueta.geometry.MetricTransformation3D;
20 import com.irurueta.geometry.Point3D;
21 import com.irurueta.numerical.robust.PROMedSRobustEstimator;
22 import com.irurueta.numerical.robust.PROMedSRobustEstimatorListener;
23 import com.irurueta.numerical.robust.RobustEstimator;
24 import com.irurueta.numerical.robust.RobustEstimatorException;
25 import com.irurueta.numerical.robust.RobustEstimatorMethod;
26
27 import java.util.ArrayList;
28 import java.util.List;
29
30 /**
31 * Finds the best metric 3D transformation for provided collections of
32 * matched 3D point using PROMedS algorithm.
33 */
34 @SuppressWarnings("DuplicatedCode")
35 public class PROMedSMetricTransformation3DRobustEstimator extends MetricTransformation3DRobustEstimator {
36
37 /**
38 * Default value to be used for stop threshold. Stop threshold can be used
39 * to keep the algorithm iterating in case that best estimated threshold
40 * using median of residuals is not small enough. Once a solution is found
41 * that generates a threshold below this value, the algorithm will stop.
42 * The stop threshold can be used to prevent the LMedS algorithm iterating
43 * too many times in cases where samples have a very similar accuracy.
44 * For instance, in cases where proportion of outliers is very small (close
45 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
46 * iterate for a long time trying to find the best solution when indeed
47 * there is no need to do that if a reasonable threshold has already been
48 * reached.
49 * Because of this behaviour the stop threshold can be set to a value much
50 * lower than the one typically used in RANSAC, and yet the algorithm could
51 * still produce even smaller thresholds in estimated results.
52 */
53 public static final double DEFAULT_STOP_THRESHOLD = 1.0;
54
55 /**
56 * Minimum allowed stop threshold value.
57 */
58 public static final double MIN_STOP_THRESHOLD = 0.0;
59
60 /**
61 * Threshold to be used to keep the algorithm iterating in case that best
62 * estimated threshold using median of residuals is not small enough. Once
63 * a solution is found that generates a threshold below this value, the
64 * algorithm will stop.
65 * The stop threshold can be used to prevent the LMedS algorithm iterating
66 * too many times in cases where samples have a very similar accuracy.
67 * For instance, in cases where proportion of outliers is very small (close
68 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
69 * iterate for a long time trying to find the best solution when indeed
70 * there is no need to do that if a reasonable threshold has already been
71 * reached.
72 * Because of this behaviour the stop threshold can be set to a value much
73 * lower than the one typically used in RANSAC, and yet the algorithm could
74 * still produce even smaller thresholds in estimated results.
75 */
76 private double stopThreshold;
77
78 /**
79 * Quality scores corresponding to each pair of matched points.
80 * The larger the score value the better the quality of the matching.
81 */
82 private double[] qualityScores;
83
84 /**
85 * Constructor.
86 */
87 public PROMedSMetricTransformation3DRobustEstimator() {
88 super();
89 stopThreshold = DEFAULT_STOP_THRESHOLD;
90 }
91
92 /**
93 * Constructor with lists of points to be used to estimate a metric 3D
94 * transformation.
95 * Points in the list located at the same position are considered to be
96 * matched. Hence, both lists must have the same size, and their size must
97 * be greater or equal than MINIMUM_SIZE.
98 *
99 * @param inputPoints list of input points to be used to estimate a
100 * metric 3D transformation.
101 * @param outputPoints list of output points to be used to estimate a
102 * metric 3D transformation.
103 * @throws IllegalArgumentException if provided lists of points don't have
104 * the same size or their size is smaller than MINIMUM_SIZE.
105 */
106 public PROMedSMetricTransformation3DRobustEstimator(
107 final List<Point3D> inputPoints, final List<Point3D> outputPoints) {
108 super(inputPoints, outputPoints);
109 stopThreshold = DEFAULT_STOP_THRESHOLD;
110 }
111
112 /**
113 * Constructor.
114 *
115 * @param listener listener to be notified of events such as when estimation
116 * starts, ends or its progress significantly changes.
117 */
118 public PROMedSMetricTransformation3DRobustEstimator(final MetricTransformation3DRobustEstimatorListener listener) {
119 super(listener);
120 stopThreshold = DEFAULT_STOP_THRESHOLD;
121 }
122
123 /**
124 * Constructor with listener and lists of points to be used to estimate a
125 * metric 3D transformation.
126 * Points in the list located at the same position are considered to be
127 * matched. Hence, both lists must have the same size, and their size must
128 * be greater or equal than MINIMUM_SIZE.
129 *
130 * @param listener listener to be notified of events such as when estimation
131 * stars, ends or its progress significantly changes.
132 * @param inputPoints list of input points to be used to estimate a
133 * metric 3D transformation.
134 * @param outputPoints list of output points to be used to estimate a
135 * metric 3D transformation.
136 * @throws IllegalArgumentException if provided lists of points don't have
137 * the same size or their size is smaller than MINIMUM_SIZE.
138 */
139 public PROMedSMetricTransformation3DRobustEstimator(
140 final MetricTransformation3DRobustEstimatorListener listener,
141 final List<Point3D> inputPoints, final List<Point3D> outputPoints) {
142 super(listener, inputPoints, outputPoints);
143 stopThreshold = DEFAULT_STOP_THRESHOLD;
144 }
145
146 /**
147 * Constructor.
148 *
149 * @param qualityScores quality scores corresponding to each pair of matched
150 * points.
151 * @throws IllegalArgumentException if provided quality scores length is
152 * smaller than MINIMUM_SIZE (i.e. 3 samples).
153 */
154 public PROMedSMetricTransformation3DRobustEstimator(final double[] qualityScores) {
155 super();
156 stopThreshold = DEFAULT_STOP_THRESHOLD;
157 internalSetQualityScores(qualityScores);
158 }
159
160 /**
161 * Constructor with lists of points to be used to estimate a metric 3D
162 * transformation.
163 * Points in the list located at the same position are considered to be
164 * matched. Hence, both lists must have the same size, and their size must
165 * be greater or equal than MINIMUM_SIZE.
166 *
167 * @param inputPoints list of input points to be used to estimate a
168 * metric 3D transformation.
169 * @param outputPoints list of output points to be used to estimate a
170 * metric 3D transformation.
171 * @param qualityScores quality scores corresponding to each pair of matched
172 * points.
173 * @throws IllegalArgumentException if provided lists of points and array
174 * of quality scores don't have the same size or their size is smaller than
175 * MINIMUM_SIZE.
176 */
177 public PROMedSMetricTransformation3DRobustEstimator(
178 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores) {
179 super(inputPoints, outputPoints);
180
181 if (qualityScores.length != inputPoints.size()) {
182 throw new IllegalArgumentException();
183 }
184
185 stopThreshold = DEFAULT_STOP_THRESHOLD;
186 internalSetQualityScores(qualityScores);
187 }
188
189 /**
190 * Constructor.
191 *
192 * @param listener listener to be notified of events such as when estimation
193 * starts, ends or its progress significantly changes.
194 * @param qualityScores quality scores corresponding to each pair of matched
195 * points.
196 * @throws IllegalArgumentException if provided quality scores length is
197 * smaller than MINIMUM_SIZE (i.e. 3 samples).
198 */
199 public PROMedSMetricTransformation3DRobustEstimator(
200 final MetricTransformation3DRobustEstimatorListener listener, final double[] qualityScores) {
201 super(listener);
202 stopThreshold = DEFAULT_STOP_THRESHOLD;
203 internalSetQualityScores(qualityScores);
204 }
205
206 /**
207 * Constructor with listener and lists of points to be used to estimate a
208 * metric 3D transformation.
209 * Points in the list located at the same position are considered to be
210 * matched. Hence, both lists must have the same size, and their size must
211 * be greater or equal than MINIMUM_SIZE.
212 *
213 * @param listener listener to be notified of events such as when estimation
214 * stars, ends or its progress significantly changes.
215 * @param inputPoints list of input points to be used to estimate a
216 * metric 3D transformation.
217 * @param outputPoints list of output points to be used to estimate a
218 * metric 3D transformation.
219 * @param qualityScores quality scores corresponding to each pair of matched
220 * points.
221 * @throws IllegalArgumentException if provided lists of points don't have
222 * the same size or their size is smaller than MINIMUM_SIZE.
223 */
224 public PROMedSMetricTransformation3DRobustEstimator(
225 final MetricTransformation3DRobustEstimatorListener listener,
226 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores) {
227 super(listener, inputPoints, outputPoints);
228
229 if (qualityScores.length != inputPoints.size()) {
230 throw new IllegalArgumentException();
231 }
232
233 stopThreshold = DEFAULT_STOP_THRESHOLD;
234 internalSetQualityScores(qualityScores);
235 }
236
237 /**
238 * Constructor.
239 *
240 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
241 */
242 public PROMedSMetricTransformation3DRobustEstimator(final boolean weakMinimumSizeAllowed) {
243 super(weakMinimumSizeAllowed);
244 stopThreshold = DEFAULT_STOP_THRESHOLD;
245 }
246
247 /**
248 * Constructor with lists of points to be used to estimate a metric 3D
249 * transformation.
250 * Points in the list located at the same position are considered to be
251 * matched. Hence, both lists must have the same size, and their size must
252 * be greater or equal than MINIMUM_SIZE.
253 *
254 * @param inputPoints list of input points to be used to estimate a
255 * metric 3D transformation.
256 * @param outputPoints list of output points to be used to estimate a
257 * metric 3D transformation.
258 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
259 * @throws IllegalArgumentException if provided lists of points don't have
260 * the same size or their size is smaller than MINIMUM_SIZE.
261 */
262 public PROMedSMetricTransformation3DRobustEstimator(
263 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final boolean weakMinimumSizeAllowed) {
264 super(inputPoints, outputPoints, weakMinimumSizeAllowed);
265 stopThreshold = DEFAULT_STOP_THRESHOLD;
266 }
267
268 /**
269 * Constructor.
270 *
271 * @param listener listener to be notified of events such as when estimation
272 * starts, ends or its progress significantly changes.
273 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
274 */
275 public PROMedSMetricTransformation3DRobustEstimator(
276 final MetricTransformation3DRobustEstimatorListener listener, final boolean weakMinimumSizeAllowed) {
277 super(listener, weakMinimumSizeAllowed);
278 stopThreshold = DEFAULT_STOP_THRESHOLD;
279 }
280
281 /**
282 * Constructor with listener and lists of points to be used to estimate a
283 * metric 3D transformation.
284 * Points in the list located at the same position are considered to be
285 * matched. Hence, both lists must have the same size, and their size must
286 * be greater or equal than MINIMUM_SIZE.
287 *
288 * @param listener listener to be notified of events such as when estimation
289 * stars, ends or its progress significantly changes.
290 * @param inputPoints list of input points to be used to estimate a
291 * metric 3D transformation.
292 * @param outputPoints list of output points to be used to estimate a
293 * metric 3D transformation.
294 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
295 * @throws IllegalArgumentException if provided lists of points don't have
296 * the same size or their size is smaller than MINIMUM_SIZE.
297 */
298 public PROMedSMetricTransformation3DRobustEstimator(
299 final MetricTransformation3DRobustEstimatorListener listener, final List<Point3D> inputPoints,
300 final List<Point3D> outputPoints, final boolean weakMinimumSizeAllowed) {
301 super(listener, inputPoints, outputPoints, weakMinimumSizeAllowed);
302 stopThreshold = DEFAULT_STOP_THRESHOLD;
303 }
304
305 /**
306 * Constructor.
307 *
308 * @param qualityScores quality scores corresponding to each pair of matched
309 * points.
310 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
311 * @throws IllegalArgumentException if provided quality scores length is
312 * smaller than MINIMUM_SIZE (i.e. 3 samples).
313 */
314 public PROMedSMetricTransformation3DRobustEstimator(
315 final double[] qualityScores, final boolean weakMinimumSizeAllowed) {
316 super(weakMinimumSizeAllowed);
317 stopThreshold = DEFAULT_STOP_THRESHOLD;
318 internalSetQualityScores(qualityScores);
319 }
320
321 /**
322 * Constructor with lists of points to be used to estimate a metric 3D
323 * transformation.
324 * Points in the list located at the same position are considered to be
325 * matched. Hence, both lists must have the same size, and their size must
326 * be greater or equal than MINIMUM_SIZE.
327 *
328 * @param inputPoints list of input points to be used to estimate a
329 * metric 3D transformation.
330 * @param outputPoints list of output points to be used to estimate a
331 * metric 3D transformation.
332 * @param qualityScores quality scores corresponding to each pair of matched
333 * points.
334 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
335 * @throws IllegalArgumentException if provided lists of points and array
336 * of quality scores don't have the same size or their size is smaller than
337 * MINIMUM_SIZE.
338 */
339 public PROMedSMetricTransformation3DRobustEstimator(
340 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores,
341 final boolean weakMinimumSizeAllowed) {
342 super(inputPoints, outputPoints, weakMinimumSizeAllowed);
343
344 if (qualityScores.length != inputPoints.size()) {
345 throw new IllegalArgumentException();
346 }
347
348 stopThreshold = DEFAULT_STOP_THRESHOLD;
349 internalSetQualityScores(qualityScores);
350 }
351
352 /**
353 * Constructor.
354 *
355 * @param listener listener to be notified of events such as when estimation
356 * starts, ends or its progress significantly changes.
357 * @param qualityScores quality scores corresponding to each pair of matched
358 * points.
359 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
360 * @throws IllegalArgumentException if provided quality scores length is
361 * smaller than MINIMUM_SIZE (i.e. 3 samples).
362 */
363 public PROMedSMetricTransformation3DRobustEstimator(
364 final MetricTransformation3DRobustEstimatorListener listener, final double[] qualityScores,
365 final boolean weakMinimumSizeAllowed) {
366 super(listener, weakMinimumSizeAllowed);
367 stopThreshold = DEFAULT_STOP_THRESHOLD;
368 internalSetQualityScores(qualityScores);
369 }
370
371 /**
372 * Constructor with listener and lists of points to be used to estimate a
373 * metric 3D transformation.
374 * Points in the list located at the same position are considered to be
375 * matched. Hence, both lists must have the same size, and their size must
376 * be greater or equal than MINIMUM_SIZE.
377 *
378 * @param listener listener to be notified of events such as when estimation
379 * stars, ends or its progress significantly changes.
380 * @param inputPoints list of input points to be used to estimate a
381 * metric 3D transformation.
382 * @param outputPoints list of output points to be used to estimate a
383 * metric 3D transformation.
384 * @param qualityScores quality scores corresponding to each pair of matched
385 * points.
386 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
387 * @throws IllegalArgumentException if provided lists of points don't have
388 * the same size or their size is smaller than MINIMUM_SIZE.
389 */
390 public PROMedSMetricTransformation3DRobustEstimator(
391 final MetricTransformation3DRobustEstimatorListener listener,
392 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores,
393 final boolean weakMinimumSizeAllowed) {
394 super(listener, inputPoints, outputPoints, weakMinimumSizeAllowed);
395
396 if (qualityScores.length != inputPoints.size()) {
397 throw new IllegalArgumentException();
398 }
399
400 stopThreshold = DEFAULT_STOP_THRESHOLD;
401 internalSetQualityScores(qualityScores);
402 }
403
404 /**
405 * Returns threshold to be used to keep the algorithm iterating in case that
406 * best estimated threshold using median of residuals is not small enough.
407 * Once a solution is found that generates a threshold below this value, the
408 * algorithm will stop.
409 * As in LMedS, the stop threshold can be used to prevent the PROMedS
410 * algorithm iterating too many times in cases where samples have a very
411 * similar accuracy.
412 * For instance, in cases where proportion of outliers is very small (close
413 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
414 * iterate for a long time trying to find the best solution when indeed
415 * there is no need to do that if a reasonable threshold has already been
416 * reached.
417 * Because of this behaviour the stop threshold can be set to a value much
418 * lower than the one typically used in RANSAC, and yet the algorithm could
419 * still produce even smaller thresholds in estimated results.
420 *
421 * @return stop threshold to stop the algorithm prematurely when a certain
422 * accuracy has been reached.
423 */
424 public double getStopThreshold() {
425 return stopThreshold;
426 }
427
428 /**
429 * Sets threshold to be used to keep the algorithm iterating in case that
430 * best estimated threshold using median of residuals is not small enough.
431 * Once a solution is found that generates a threshold below this value, the
432 * algorithm will stop.
433 * As in LMedS, the stop threshold can be used to prevent the PROMedS
434 * algorithm iterating too many times in cases where samples have a very
435 * similar accuracy.
436 * For instance, in cases where proportion of outliers is very small (close
437 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
438 * iterate for a long time trying to find the best solution when indeed
439 * there is no need to do that if a reasonable threshold has already been
440 * reached.
441 * Because of this behaviour the stop threshold can be set to a value much
442 * lower than the one typically used in RANSAC, and yet the algorithm could
443 * still produce even smaller thresholds in estimated results.
444 *
445 * @param stopThreshold stop threshold to stop the algorithm prematurely
446 * when a certain accuracy has been reached.
447 * @throws IllegalArgumentException if provided value is zero or negative.
448 * @throws LockedException if robust estimator is locked because an
449 * estimation is already in progress.
450 */
451 public void setStopThreshold(final double stopThreshold) throws LockedException {
452 if (isLocked()) {
453 throw new LockedException();
454 }
455 if (stopThreshold <= MIN_STOP_THRESHOLD) {
456 throw new IllegalArgumentException();
457 }
458
459 this.stopThreshold = stopThreshold;
460 }
461
462 /**
463 * Returns quality scores corresponding to each pair of matched points.
464 * The larger the score value the better the quality of the matching.
465 *
466 * @return quality scores corresponding to each pair of matched points.
467 */
468 @Override
469 public double[] getQualityScores() {
470 return qualityScores;
471 }
472
473 /**
474 * Sets quality scores corresponding to each pair of matched points.
475 * The larger the score value the better the quality of the matching.
476 *
477 * @param qualityScores quality scores corresponding to each pair of matched
478 * points.
479 * @throws LockedException if robust estimator is locked because an
480 * estimation is already in progress.
481 * @throws IllegalArgumentException if provided quality scores length is
482 * smaller than MINIMUM_SIZE (i.e. 3 samples).
483 */
484 @Override
485 public void setQualityScores(final double[] qualityScores) throws LockedException {
486 if (isLocked()) {
487 throw new LockedException();
488 }
489 internalSetQualityScores(qualityScores);
490 }
491
492 /**
493 * Indicates if estimator is ready to start the metric 3D transformation
494 * estimation.
495 * This is true when input data (i.e. lists of matched points and quality
496 * scores) are provided and a minimum of MINIMUM_SIZE points are available.
497 *
498 * @return true if estimator is ready, false otherwise.
499 */
500 @Override
501 public boolean isReady() {
502 return super.isReady() && qualityScores != null && qualityScores.length == inputPoints.size();
503 }
504
505 /**
506 * Estimates a metric 3D transformation using a robust estimator and
507 * the best set of matched 3D point correspondences found using the robust
508 * estimator.
509 *
510 * @return a metric 3D transformation.
511 * @throws LockedException if robust estimator is locked because an
512 * estimation is already in progress.
513 * @throws NotReadyException if provided input data is not enough to start
514 * the estimation.
515 * @throws RobustEstimatorException if estimation fails for any reason
516 * (i.e. numerical instability, no solution available, etc).
517 */
518 @Override
519 public MetricTransformation3D estimate() throws LockedException, NotReadyException, RobustEstimatorException {
520 if (isLocked()) {
521 throw new LockedException();
522 }
523 if (!isReady()) {
524 throw new NotReadyException();
525 }
526
527 final var innerEstimator = new PROMedSRobustEstimator<>(
528 new PROMedSRobustEstimatorListener<MetricTransformation3D>() {
529
530 // point to be reused when computing residuals
531 private final Point3D testPoint = Point3D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
532
533 private final MetricTransformation3DEstimator nonRobustEstimator =
534 new MetricTransformation3DEstimator(isWeakMinimumSizeAllowed());
535
536 private final List<Point3D> subsetInputPoints = new ArrayList<>();
537 private final List<Point3D> subsetOutputPoints = new ArrayList<>();
538
539 @Override
540 public double getThreshold() {
541 return stopThreshold;
542 }
543
544 @Override
545 public int getTotalSamples() {
546 return inputPoints.size();
547 }
548
549 @Override
550 public int getSubsetSize() {
551 return nonRobustEstimator.getMinimumPoints();
552 }
553
554 @Override
555 public void estimatePreliminarSolutions(
556 final int[] samplesIndices, final List<MetricTransformation3D> solutions) {
557 subsetInputPoints.clear();
558 subsetOutputPoints.clear();
559 for (final var samplesIndex : samplesIndices) {
560 subsetInputPoints.add(inputPoints.get(samplesIndex));
561 subsetOutputPoints.add(outputPoints.get(samplesIndex));
562 }
563
564 try {
565 nonRobustEstimator.setPoints(subsetInputPoints, subsetOutputPoints);
566 solutions.add(nonRobustEstimator.estimate());
567 } catch (final Exception e) {
568 // if points are coincident, no solution is added
569 }
570 }
571
572 @Override
573 public double computeResidual(final MetricTransformation3D currentEstimation, final int i) {
574 final var inputPoint = inputPoints.get(i);
575 final var outputPoint = outputPoints.get(i);
576
577 // transform input point and store result in mTestPoint
578 currentEstimation.transform(inputPoint, testPoint);
579
580 return outputPoint.distanceTo(testPoint);
581 }
582
583 @Override
584 public boolean isReady() {
585 return PROMedSMetricTransformation3DRobustEstimator.this.isReady();
586 }
587
588 @Override
589 public void onEstimateStart(final RobustEstimator<MetricTransformation3D> estimator) {
590 if (listener != null) {
591 listener.onEstimateStart(PROMedSMetricTransformation3DRobustEstimator.this);
592 }
593 }
594
595 @Override
596 public void onEstimateEnd(final RobustEstimator<MetricTransformation3D> estimator) {
597 if (listener != null) {
598 listener.onEstimateEnd(PROMedSMetricTransformation3DRobustEstimator.this);
599 }
600 }
601
602 @Override
603 public void onEstimateNextIteration(
604 final RobustEstimator<MetricTransformation3D> estimator, final int iteration) {
605 if (listener != null) {
606 listener.onEstimateNextIteration(
607 PROMedSMetricTransformation3DRobustEstimator.this, iteration);
608 }
609 }
610
611 @Override
612 public void onEstimateProgressChange(
613 final RobustEstimator<MetricTransformation3D> estimator, final float progress) {
614 if (listener != null) {
615 listener.onEstimateProgressChange(
616 PROMedSMetricTransformation3DRobustEstimator.this, progress);
617 }
618 }
619
620 @Override
621 public double[] getQualityScores() {
622 return qualityScores;
623 }
624 });
625
626 try {
627 locked = true;
628 inliersData = null;
629 innerEstimator.setConfidence(confidence);
630 innerEstimator.setMaxIterations(maxIterations);
631 innerEstimator.setProgressDelta(progressDelta);
632 final var transformation = innerEstimator.estimate();
633 inliersData = innerEstimator.getInliersData();
634 return attemptRefine(transformation);
635 } catch (final com.irurueta.numerical.LockedException e) {
636 throw new LockedException(e);
637 } catch (final com.irurueta.numerical.NotReadyException e) {
638 throw new NotReadyException(e);
639 } finally {
640 locked = false;
641 }
642 }
643
644 /**
645 * Returns method being used for robust estimation.
646 *
647 * @return method being used for robust estimation.
648 */
649 @Override
650 public RobustEstimatorMethod getMethod() {
651 return RobustEstimatorMethod.PROMEDS;
652 }
653
654 /**
655 * Gets standard deviation used for Levenberg-Marquardt fitting during
656 * refinement.
657 * Returned value gives an indication of how much variance each residual
658 * has.
659 * Typically, this value is related to the threshold used on each robust
660 * estimation, since residuals of found inliers are within the range of such
661 * threshold.
662 *
663 * @return standard deviation used for refinement.
664 */
665 @Override
666 protected double getRefinementStandardDeviation() {
667 final var inliersData = (PROMedSRobustEstimator.PROMedSInliersData) getInliersData();
668 return inliersData.getEstimatedThreshold();
669 }
670
671 /**
672 * Sets quality scores corresponding to each pair of matched points.
673 * This method is used internally and does not check whether instance is
674 * locked or not.
675 *
676 * @param qualityScores quality scores to be set.
677 * @throws IllegalArgumentException if provided quality scores length is
678 * smaller than MINIMUM_SIZE.
679 */
680 private void internalSetQualityScores(final double[] qualityScores) {
681 if (qualityScores.length < getMinimumPoints()) {
682 throw new IllegalArgumentException();
683 }
684
685 this.qualityScores = qualityScores;
686 }
687 }