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.EuclideanTransformation3D;
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 Euclidean 3D transformation for provided collections of
32 * matched 3D point using PROMedS algorithm.
33 */
34 @SuppressWarnings("DuplicatedCode")
35 public class PROMedSEuclideanTransformation3DRobustEstimator extends EuclideanTransformation3DRobustEstimator {
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 PROMedSEuclideanTransformation3DRobustEstimator() {
88 super();
89 stopThreshold = DEFAULT_STOP_THRESHOLD;
90 }
91
92 /**
93 * Constructor with lists of points to be used to estimate an Euclidean 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 an
100 * affine 3D transformation.
101 * @param outputPoints list of output points to be used to estimate an
102 * affine 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 PROMedSEuclideanTransformation3DRobustEstimator(
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 PROMedSEuclideanTransformation3DRobustEstimator(
119 final EuclideanTransformation3DRobustEstimatorListener listener) {
120 super(listener);
121 stopThreshold = DEFAULT_STOP_THRESHOLD;
122 }
123
124 /**
125 * Constructor with listener and lists of points to be used to estimate an
126 * Euclidean 3D transformation.
127 * Points in the list located at the same position are considered to be
128 * matched. Hence, both lists must have the same size, and their size must
129 * be greater or equal than MINIMUM_SIZE.
130 *
131 * @param listener listener to be notified of events such as when estimation
132 * stars, ends or its progress significantly changes.
133 * @param inputPoints list of input points to be used to estimate an
134 * affine 3D transformation.
135 * @param outputPoints list of output points to be used to estimate an
136 * affine 3D transformation.
137 * @throws IllegalArgumentException if provided lists of points don't have
138 * the same size or their size is smaller than MINIMUM_SIZE.
139 */
140 public PROMedSEuclideanTransformation3DRobustEstimator(
141 final EuclideanTransformation3DRobustEstimatorListener listener,
142 final List<Point3D> inputPoints, final List<Point3D> outputPoints) {
143 super(listener, inputPoints, outputPoints);
144 stopThreshold = DEFAULT_STOP_THRESHOLD;
145 }
146
147 /**
148 * Constructor.
149 *
150 * @param qualityScores quality scores corresponding to each pair of matched
151 * points.
152 * @throws IllegalArgumentException if provided quality scores length is
153 * smaller than MINIMUM_SIZE (i.e. 3 samples).
154 */
155 public PROMedSEuclideanTransformation3DRobustEstimator(final double[] qualityScores) {
156 super();
157 stopThreshold = DEFAULT_STOP_THRESHOLD;
158 internalSetQualityScores(qualityScores);
159 }
160
161 /**
162 * Constructor with lists of points to be used to estimate an Euclidean 3D
163 * transformation.
164 * Points in the list located at the same position are considered to be
165 * matched. Hence, both lists must have the same size, and their size must
166 * be greater or equal than MINIMUM_SIZE.
167 *
168 * @param inputPoints list of input points to be used to estimate an
169 * affine 3D transformation.
170 * @param outputPoints list of output points to be used to estimate an
171 * affine 3D transformation.
172 * @param qualityScores quality scores corresponding to each pair of matched
173 * points.
174 * @throws IllegalArgumentException if provided lists of points and array
175 * of quality scores don't have the same size or their size is smaller than
176 * MINIMUM_SIZE.
177 */
178 public PROMedSEuclideanTransformation3DRobustEstimator(
179 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores) {
180 super(inputPoints, outputPoints);
181
182 if (qualityScores.length != inputPoints.size()) {
183 throw new IllegalArgumentException();
184 }
185
186 stopThreshold = DEFAULT_STOP_THRESHOLD;
187 internalSetQualityScores(qualityScores);
188 }
189
190 /**
191 * Constructor.
192 *
193 * @param listener listener to be notified of events such as when estimation
194 * starts, ends or its progress significantly changes.
195 * @param qualityScores quality scores corresponding to each pair of matched
196 * points.
197 * @throws IllegalArgumentException if provided quality scores length is
198 * smaller than MINIMUM_SIZE (i.e. 3 samples).
199 */
200 public PROMedSEuclideanTransformation3DRobustEstimator(
201 final EuclideanTransformation3DRobustEstimatorListener listener, final double[] qualityScores) {
202 super(listener);
203 stopThreshold = DEFAULT_STOP_THRESHOLD;
204 internalSetQualityScores(qualityScores);
205 }
206
207 /**
208 * Constructor with listener and lists of points to be used to estimate an
209 * Euclidean 3D transformation.
210 * Points in the list located at the same position are considered to be
211 * matched. Hence, both lists must have the same size, and their size must
212 * be greater or equal than MINIMUM_SIZE.
213 *
214 * @param listener listener to be notified of events such as when estimation
215 * stars, ends or its progress significantly changes.
216 * @param inputPoints list of input points to be used to estimate an
217 * affine 2D transformation.
218 * @param outputPoints list of output points to be used to estimate an
219 * affine 2D transformation.
220 * @param qualityScores quality scores corresponding to each pair of matched
221 * points.
222 * @throws IllegalArgumentException if provided lists of points don't have
223 * the same size or their size is smaller than MINIMUM_SIZE.
224 */
225 public PROMedSEuclideanTransformation3DRobustEstimator(
226 final EuclideanTransformation3DRobustEstimatorListener listener, final List<Point3D> inputPoints,
227 final List<Point3D> outputPoints, final double[] qualityScores) {
228 super(listener, inputPoints, outputPoints);
229
230 if (qualityScores.length != inputPoints.size()) {
231 throw new IllegalArgumentException();
232 }
233
234 stopThreshold = DEFAULT_STOP_THRESHOLD;
235 internalSetQualityScores(qualityScores);
236 }
237
238 /**
239 * Constructor.
240 *
241 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
242 */
243 public PROMedSEuclideanTransformation3DRobustEstimator(final boolean weakMinimumSizeAllowed) {
244 super(weakMinimumSizeAllowed);
245 stopThreshold = DEFAULT_STOP_THRESHOLD;
246 }
247
248 /**
249 * Constructor with lists of points to be used to estimate an Euclidean 3D
250 * transformation.
251 * Points in the list located at the same position are considered to be
252 * matched. Hence, both lists must have the same size, and their size must
253 * be greater or equal than MINIMUM_SIZE.
254 *
255 * @param inputPoints list of input points to be used to estimate an
256 * affine 3D transformation.
257 * @param outputPoints list of output points to be used to estimate an
258 * affine 3D transformation.
259 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
260 * @throws IllegalArgumentException if provided lists of points don't have
261 * the same size or their size is smaller than MINIMUM_SIZE.
262 */
263 public PROMedSEuclideanTransformation3DRobustEstimator(
264 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final boolean weakMinimumSizeAllowed) {
265 super(inputPoints, outputPoints, weakMinimumSizeAllowed);
266 stopThreshold = DEFAULT_STOP_THRESHOLD;
267 }
268
269 /**
270 * Constructor.
271 *
272 * @param listener listener to be notified of events such as when estimation
273 * starts, ends or its progress significantly changes.
274 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
275 */
276 public PROMedSEuclideanTransformation3DRobustEstimator(
277 final EuclideanTransformation3DRobustEstimatorListener listener, final boolean weakMinimumSizeAllowed) {
278 super(listener, weakMinimumSizeAllowed);
279 stopThreshold = DEFAULT_STOP_THRESHOLD;
280 }
281
282 /**
283 * Constructor with listener and lists of points to be used to estimate an
284 * Euclidean 3D transformation.
285 * Points in the list located at the same position are considered to be
286 * matched. Hence, both lists must have the same size, and their size must
287 * be greater or equal than MINIMUM_SIZE.
288 *
289 * @param listener listener to be notified of events such as when estimation
290 * stars, ends or its progress significantly changes.
291 * @param inputPoints list of input points to be used to estimate an
292 * affine 3D transformation.
293 * @param outputPoints list of output points to be used to estimate an
294 * affine 3D transformation.
295 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
296 * @throws IllegalArgumentException if provided lists of points don't have
297 * the same size or their size is smaller than MINIMUM_SIZE.
298 */
299 public PROMedSEuclideanTransformation3DRobustEstimator(
300 final EuclideanTransformation3DRobustEstimatorListener listener,
301 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final boolean weakMinimumSizeAllowed) {
302 super(listener, inputPoints, outputPoints, weakMinimumSizeAllowed);
303 stopThreshold = DEFAULT_STOP_THRESHOLD;
304 }
305
306 /**
307 * Constructor.
308 *
309 * @param qualityScores quality scores corresponding to each pair of matched
310 * points.
311 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
312 * @throws IllegalArgumentException if provided quality scores length is
313 * smaller than MINIMUM_SIZE (i.e. 3 samples).
314 */
315 public PROMedSEuclideanTransformation3DRobustEstimator(
316 final double[] qualityScores, final boolean weakMinimumSizeAllowed) {
317 super(weakMinimumSizeAllowed);
318 stopThreshold = DEFAULT_STOP_THRESHOLD;
319 internalSetQualityScores(qualityScores);
320 }
321
322 /**
323 * Constructor with lists of points to be used to estimate an Euclidean 3D
324 * transformation.
325 * Points in the list located at the same position are considered to be
326 * matched. Hence, both lists must have the same size, and their size must
327 * be greater or equal than MINIMUM_SIZE.
328 *
329 * @param inputPoints list of input points to be used to estimate an
330 * affine 3D transformation.
331 * @param outputPoints list of output points to be used to estimate an
332 * affine 3D transformation.
333 * @param qualityScores quality scores corresponding to each pair of matched
334 * points.
335 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
336 * @throws IllegalArgumentException if provided lists of points and array
337 * of quality scores don't have the same size or their size is smaller than
338 * MINIMUM_SIZE.
339 */
340 public PROMedSEuclideanTransformation3DRobustEstimator(
341 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores,
342 final boolean weakMinimumSizeAllowed) {
343 super(inputPoints, outputPoints, weakMinimumSizeAllowed);
344
345 if (qualityScores.length != inputPoints.size()) {
346 throw new IllegalArgumentException();
347 }
348
349 stopThreshold = DEFAULT_STOP_THRESHOLD;
350 internalSetQualityScores(qualityScores);
351 }
352
353 /**
354 * Constructor.
355 *
356 * @param listener listener to be notified of events such as when estimation
357 * starts, ends or its progress significantly changes.
358 * @param qualityScores quality scores corresponding to each pair of matched
359 * points.
360 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
361 * @throws IllegalArgumentException if provided quality scores length is
362 * smaller than MINIMUM_SIZE (i.e. 3 samples).
363 */
364 public PROMedSEuclideanTransformation3DRobustEstimator(
365 final EuclideanTransformation3DRobustEstimatorListener listener, final double[] qualityScores,
366 final boolean weakMinimumSizeAllowed) {
367 super(listener, weakMinimumSizeAllowed);
368 stopThreshold = DEFAULT_STOP_THRESHOLD;
369 internalSetQualityScores(qualityScores);
370 }
371
372 /**
373 * Constructor with listener and lists of points to be used to estimate an
374 * Euclidean 3D transformation.
375 * Points in the list located at the same position are considered to be
376 * matched. Hence, both lists must have the same size, and their size must
377 * be greater or equal than MINIMUM_SIZE.
378 *
379 * @param listener listener to be notified of events such as when estimation
380 * stars, ends or its progress significantly changes.
381 * @param inputPoints list of input points to be used to estimate an
382 * affine 2D transformation.
383 * @param outputPoints list of output points to be used to estimate an
384 * affine 2D transformation.
385 * @param qualityScores quality scores corresponding to each pair of matched
386 * points.
387 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
388 * @throws IllegalArgumentException if provided lists of points don't have
389 * the same size or their size is smaller than MINIMUM_SIZE.
390 */
391 public PROMedSEuclideanTransformation3DRobustEstimator(
392 final EuclideanTransformation3DRobustEstimatorListener listener, final List<Point3D> inputPoints,
393 final List<Point3D> outputPoints, final double[] qualityScores, 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 affine 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 an Euclidean 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 an Euclidean 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 EuclideanTransformation3D 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<EuclideanTransformation3D>() {
529
530 // point to be reused when computing residuals
531 private final Point3D testPoint = Point3D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
532
533 private final EuclideanTransformation3DEstimator nonRobustEstimator =
534 new EuclideanTransformation3DEstimator(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<EuclideanTransformation3D> 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 EuclideanTransformation3D 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 PROMedSEuclideanTransformation3DRobustEstimator.this.isReady();
586 }
587
588 @Override
589 public void onEstimateStart(final RobustEstimator<EuclideanTransformation3D> estimator) {
590 if (listener != null) {
591 listener.onEstimateStart(PROMedSEuclideanTransformation3DRobustEstimator.this);
592 }
593 }
594
595 @Override
596 public void onEstimateEnd(final RobustEstimator<EuclideanTransformation3D> estimator) {
597 if (listener != null) {
598 listener.onEstimateEnd(PROMedSEuclideanTransformation3DRobustEstimator.this);
599 }
600 }
601
602 @Override
603 public void onEstimateNextIteration(
604 final RobustEstimator<EuclideanTransformation3D> estimator, final int iteration) {
605 if (listener != null) {
606 listener.onEstimateNextIteration(
607 PROMedSEuclideanTransformation3DRobustEstimator.this, iteration);
608 }
609 }
610
611 @Override
612 public void onEstimateProgressChange(
613 final RobustEstimator<EuclideanTransformation3D> estimator, final float progress) {
614 if (listener != null) {
615 listener.onEstimateProgressChange(
616 PROMedSEuclideanTransformation3DRobustEstimator.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 }