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.MetricTransformation2D;
20 import com.irurueta.geometry.Point2D;
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 2D transformation for provided collections of
32 * matched 2D point using PROMedS algorithm.
33 */
34 public class PROMedSMetricTransformation2DRobustEstimator extends MetricTransformation2DRobustEstimator {
35
36 /**
37 * Default value to be used for stop threshold. Stop threshold can be used
38 * to keep the algorithm iterating in case that best estimated threshold
39 * using median of residuals is not small enough. Once a solution is found
40 * that generates a threshold below this value, the algorithm will stop.
41 * The stop threshold can be used to prevent the LMedS algorithm iterating
42 * too many times in cases where samples have a very similar accuracy.
43 * For instance, in cases where proportion of outliers is very small (close
44 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
45 * iterate for a long time trying to find the best solution when indeed
46 * there is no need to do that if a reasonable threshold has already been
47 * reached.
48 * Because of this behaviour the stop threshold can be set to a value much
49 * lower than the one typically used in RANSAC, and yet the algorithm could
50 * still produce even smaller thresholds in estimated results.
51 */
52 public static final double DEFAULT_STOP_THRESHOLD = 1.0;
53
54 /**
55 * Minimum allowed stop threshold value.
56 */
57 public static final double MIN_STOP_THRESHOLD = 0.0;
58
59 /**
60 * Threshold to be used to keep the algorithm iterating in case that best
61 * estimated threshold using median of residuals is not small enough. Once
62 * a solution is found that generates a threshold below this value, the
63 * algorithm will stop.
64 * The stop threshold can be used to prevent the LMedS algorithm iterating
65 * too many times in cases where samples have a very similar accuracy.
66 * For instance, in cases where proportion of outliers is very small (close
67 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
68 * iterate for a long time trying to find the best solution when indeed
69 * there is no need to do that if a reasonable threshold has already been
70 * reached.
71 * Because of this behaviour the stop threshold can be set to a value much
72 * lower than the one typically used in RANSAC, and yet the algorithm could
73 * still produce even smaller thresholds in estimated results.
74 */
75 private double stopThreshold;
76
77 /**
78 * Quality scores corresponding to each pair of matched points.
79 * The larger the score value the better the quality of the matching.
80 */
81 private double[] qualityScores;
82
83 /**
84 * Constructor.
85 */
86 public PROMedSMetricTransformation2DRobustEstimator() {
87 super();
88 stopThreshold = DEFAULT_STOP_THRESHOLD;
89 }
90
91 /**
92 * Constructor with lists of points to be used to estimate a metric 2D
93 * transformation.
94 * Points in the list located at the same position are considered to be
95 * matched. Hence, both lists must have the same size, and their size must
96 * be greater or equal than MINIMUM_SIZE.
97 *
98 * @param inputPoints list of input points to be used to estimate a
99 * metric 2D transformation.
100 * @param outputPoints list of output points to be used to estimate a
101 * metric 2D transformation.
102 * @throws IllegalArgumentException if provided lists of points don't have
103 * the same size or their size is smaller than MINIMUM_SIZE.
104 */
105 public PROMedSMetricTransformation2DRobustEstimator(
106 final List<Point2D> inputPoints, final List<Point2D> outputPoints) {
107 super(inputPoints, outputPoints);
108 stopThreshold = DEFAULT_STOP_THRESHOLD;
109 }
110
111 /**
112 * Constructor.
113 *
114 * @param listener listener to be notified of events such as when estimation
115 * starts, ends or its progress significantly changes.
116 */
117 public PROMedSMetricTransformation2DRobustEstimator(final MetricTransformation2DRobustEstimatorListener listener) {
118 super(listener);
119 stopThreshold = DEFAULT_STOP_THRESHOLD;
120 }
121
122 /**
123 * Constructor with listener and lists of points to be used to estimate a
124 * metric 2D transformation.
125 * Points in the list located at the same position are considered to be
126 * matched. Hence, both lists must have the same size, and their size must
127 * be greater or equal than MINIMUM_SIZE.
128 *
129 * @param listener listener to be notified of events such as when estimation
130 * stars, ends or its progress significantly changes.
131 * @param inputPoints list of input points to be used to estimate a
132 * metric 2D transformation.
133 * @param outputPoints list of output points to be used to estimate a
134 * metric 2D transformation.
135 * @throws IllegalArgumentException if provided lists of points don't have
136 * the same size or their size is smaller than MINIMUM_SIZE.
137 */
138 public PROMedSMetricTransformation2DRobustEstimator(
139 final MetricTransformation2DRobustEstimatorListener listener,
140 final List<Point2D> inputPoints, final List<Point2D> outputPoints) {
141 super(listener, inputPoints, outputPoints);
142 stopThreshold = DEFAULT_STOP_THRESHOLD;
143 }
144
145 /**
146 * Constructor.
147 *
148 * @param qualityScores quality scores corresponding to each pair of matched
149 * points.
150 * @throws IllegalArgumentException if provided quality scores length is
151 * smaller than MINIMUM_SIZE (i.e. 3 samples).
152 */
153 public PROMedSMetricTransformation2DRobustEstimator(final double[] qualityScores) {
154 super();
155 stopThreshold = DEFAULT_STOP_THRESHOLD;
156 internalSetQualityScores(qualityScores);
157 }
158
159 /**
160 * Constructor with lists of points to be used to estimate a metric 2D
161 * transformation.
162 * Points in the list located at the same position are considered to be
163 * matched. Hence, both lists must have the same size, and their size must
164 * be greater or equal than MINIMUM_SIZE.
165 *
166 * @param inputPoints list of input points to be used to estimate a
167 * metric 2D transformation.
168 * @param outputPoints list of output points to be used to estimate a
169 * metric 2D transformation.
170 * @param qualityScores quality scores corresponding to each pair of matched
171 * points.
172 * @throws IllegalArgumentException if provided lists of points and array
173 * of quality scores don't have the same size or their size is smaller than
174 * MINIMUM_SIZE.
175 */
176 public PROMedSMetricTransformation2DRobustEstimator(
177 final List<Point2D> inputPoints, final List<Point2D> outputPoints, final double[] qualityScores) {
178 super(inputPoints, outputPoints);
179
180 if (qualityScores.length != inputPoints.size()) {
181 throw new IllegalArgumentException();
182 }
183
184 stopThreshold = DEFAULT_STOP_THRESHOLD;
185 internalSetQualityScores(qualityScores);
186 }
187
188 /**
189 * Constructor.
190 *
191 * @param listener listener to be notified of events such as when estimation
192 * starts, ends or its progress significantly changes.
193 * @param qualityScores quality scores corresponding to each pair of matched
194 * points.
195 * @throws IllegalArgumentException if provided quality scores length is
196 * smaller than MINIMUM_SIZE (i.e. 3 samples).
197 */
198 public PROMedSMetricTransformation2DRobustEstimator(
199 final MetricTransformation2DRobustEstimatorListener listener, final double[] qualityScores) {
200 super(listener);
201 stopThreshold = DEFAULT_STOP_THRESHOLD;
202 internalSetQualityScores(qualityScores);
203 }
204
205 /**
206 * Constructor with listener and lists of points to be used to estimate a
207 * metric 2D transformation.
208 * Points in the list located at the same position are considered to be
209 * matched. Hence, both lists must have the same size, and their size must
210 * be greater or equal than MINIMUM_SIZE.
211 *
212 * @param listener listener to be notified of events such as when estimation
213 * stars, ends or its progress significantly changes.
214 * @param inputPoints list of input points to be used to estimate a
215 * metric 2D transformation.
216 * @param outputPoints list of output points to be used to estimate a
217 * metric 2D transformation.
218 * @param qualityScores quality scores corresponding to each pair of matched
219 * points.
220 * @throws IllegalArgumentException if provided lists of points don't have
221 * the same size or their size is smaller than MINIMUM_SIZE.
222 */
223 public PROMedSMetricTransformation2DRobustEstimator(
224 final MetricTransformation2DRobustEstimatorListener listener,
225 final List<Point2D> inputPoints, final List<Point2D> outputPoints, final double[] qualityScores) {
226 super(listener, inputPoints, outputPoints);
227
228 if (qualityScores.length != inputPoints.size()) {
229 throw new IllegalArgumentException();
230 }
231
232 stopThreshold = DEFAULT_STOP_THRESHOLD;
233 internalSetQualityScores(qualityScores);
234 }
235
236 /**
237 * Constructor.
238 *
239 * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
240 */
241 public PROMedSMetricTransformation2DRobustEstimator(final boolean weakMinimumSizeAllowed) {
242 super(weakMinimumSizeAllowed);
243 stopThreshold = DEFAULT_STOP_THRESHOLD;
244 }
245
246 /**
247 * Constructor with lists of points to be used to estimate a metric 2D
248 * transformation.
249 * Points in the list located at the same position are considered to be
250 * matched. Hence, both lists must have the same size, and their size must
251 * be greater or equal than MINIMUM_SIZE.
252 *
253 * @param inputPoints list of input points to be used to estimate a
254 * metric 2D transformation.
255 * @param outputPoints list of output points to be used to estimate a
256 * metric 2D transformation.
257 * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
258 * @throws IllegalArgumentException if provided lists of points don't have
259 * the same size or their size is smaller than MINIMUM_SIZE.
260 */
261 public PROMedSMetricTransformation2DRobustEstimator(
262 final List<Point2D> inputPoints, final List<Point2D> outputPoints, final boolean weakMinimumSizeAllowed) {
263 super(inputPoints, outputPoints, weakMinimumSizeAllowed);
264 stopThreshold = DEFAULT_STOP_THRESHOLD;
265 }
266
267 /**
268 * Constructor.
269 *
270 * @param listener listener to be notified of events such as when estimation
271 * starts, ends or its progress significantly changes.
272 * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
273 */
274 public PROMedSMetricTransformation2DRobustEstimator(
275 final MetricTransformation2DRobustEstimatorListener listener, final boolean weakMinimumSizeAllowed) {
276 super(listener, weakMinimumSizeAllowed);
277 stopThreshold = DEFAULT_STOP_THRESHOLD;
278 }
279
280 /**
281 * Constructor with listener and lists of points to be used to estimate a
282 * metric 2D transformation.
283 * Points in the list located at the same position are considered to be
284 * matched. Hence, both lists must have the same size, and their size must
285 * be greater or equal than MINIMUM_SIZE.
286 *
287 * @param listener listener to be notified of events such as when estimation
288 * stars, ends or its progress significantly changes.
289 * @param inputPoints list of input points to be used to estimate a
290 * metric 2D transformation.
291 * @param outputPoints list of output points to be used to estimate a
292 * metric 2D transformation.
293 * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
294 * @throws IllegalArgumentException if provided lists of points don't have
295 * the same size or their size is smaller than MINIMUM_SIZE.
296 */
297 public PROMedSMetricTransformation2DRobustEstimator(
298 final MetricTransformation2DRobustEstimatorListener listener,
299 final List<Point2D> inputPoints, final List<Point2D> outputPoints, final boolean weakMinimumSizeAllowed) {
300 super(listener, inputPoints, outputPoints, weakMinimumSizeAllowed);
301 stopThreshold = DEFAULT_STOP_THRESHOLD;
302 }
303
304 /**
305 * Constructor.
306 *
307 * @param qualityScores quality scores corresponding to each pair of matched
308 * points.
309 * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
310 * @throws IllegalArgumentException if provided quality scores length is
311 * smaller than MINIMUM_SIZE (i.e. 3 samples).
312 */
313 public PROMedSMetricTransformation2DRobustEstimator(
314 final double[] qualityScores, final boolean weakMinimumSizeAllowed) {
315 super(weakMinimumSizeAllowed);
316 stopThreshold = DEFAULT_STOP_THRESHOLD;
317 internalSetQualityScores(qualityScores);
318 }
319
320 /**
321 * Constructor with lists of points to be used to estimate a metric 2D
322 * transformation.
323 * Points in the list located at the same position are considered to be
324 * matched. Hence, both lists must have the same size, and their size must
325 * be greater or equal than MINIMUM_SIZE.
326 *
327 * @param inputPoints list of input points to be used to estimate a
328 * metric 2D transformation.
329 * @param outputPoints list of output points to be used to estimate a
330 * metric 2D transformation.
331 * @param qualityScores quality scores corresponding to each pair of matched
332 * points.
333 * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
334 * @throws IllegalArgumentException if provided lists of points and array
335 * of quality scores don't have the same size or their size is smaller than
336 * MINIMUM_SIZE.
337 */
338 public PROMedSMetricTransformation2DRobustEstimator(
339 final List<Point2D> inputPoints, final List<Point2D> outputPoints, final double[] qualityScores,
340 final boolean weakMinimumSizeAllowed) {
341 super(inputPoints, outputPoints, weakMinimumSizeAllowed);
342
343 if (qualityScores.length != inputPoints.size()) {
344 throw new IllegalArgumentException();
345 }
346
347 stopThreshold = DEFAULT_STOP_THRESHOLD;
348 internalSetQualityScores(qualityScores);
349 }
350
351 /**
352 * Constructor.
353 *
354 * @param listener listener to be notified of events such as when estimation
355 * starts, ends or its progress significantly changes.
356 * @param qualityScores quality scores corresponding to each pair of matched
357 * points.
358 * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
359 * @throws IllegalArgumentException if provided quality scores length is
360 * smaller than MINIMUM_SIZE (i.e. 3 samples).
361 */
362 public PROMedSMetricTransformation2DRobustEstimator(
363 final MetricTransformation2DRobustEstimatorListener listener, final double[] qualityScores,
364 final boolean weakMinimumSizeAllowed) {
365 super(listener, weakMinimumSizeAllowed);
366 stopThreshold = DEFAULT_STOP_THRESHOLD;
367 internalSetQualityScores(qualityScores);
368 }
369
370 /**
371 * Constructor with listener and lists of points to be used to estimate a
372 * metric 2D transformation.
373 * Points in the list located at the same position are considered to be
374 * matched. Hence, both lists must have the same size, and their size must
375 * be greater or equal than MINIMUM_SIZE.
376 *
377 * @param listener listener to be notified of events such as when estimation
378 * stars, ends or its progress significantly changes.
379 * @param inputPoints list of input points to be used to estimate a
380 * metric 2D transformation.
381 * @param outputPoints list of output points to be used to estimate a
382 * metric 2D transformation.
383 * @param qualityScores quality scores corresponding to each pair of matched
384 * points.
385 * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
386 * @throws IllegalArgumentException if provided lists of points don't have
387 * the same size or their size is smaller than MINIMUM_SIZE.
388 */
389 public PROMedSMetricTransformation2DRobustEstimator(
390 final MetricTransformation2DRobustEstimatorListener listener, final List<Point2D> inputPoints,
391 final List<Point2D> outputPoints, final double[] qualityScores, final boolean weakMinimumSizeAllowed) {
392 super(listener, inputPoints, outputPoints, weakMinimumSizeAllowed);
393
394 if (qualityScores.length != inputPoints.size()) {
395 throw new IllegalArgumentException();
396 }
397
398 stopThreshold = DEFAULT_STOP_THRESHOLD;
399 internalSetQualityScores(qualityScores);
400 }
401
402 /**
403 * Returns threshold to be used to keep the algorithm iterating in case that
404 * best estimated threshold using median of residuals is not small enough.
405 * Once a solution is found that generates a threshold below this value, the
406 * algorithm will stop.
407 * As in LMedS, the stop threshold can be used to prevent the PROMedS
408 * algorithm iterating too many times in cases where samples have a very
409 * similar accuracy.
410 * For instance, in cases where proportion of outliers is very small (close
411 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
412 * iterate for a long time trying to find the best solution when indeed
413 * there is no need to do that if a reasonable threshold has already been
414 * reached.
415 * Because of this behaviour the stop threshold can be set to a value much
416 * lower than the one typically used in RANSAC, and yet the algorithm could
417 * still produce even smaller thresholds in estimated results.
418 *
419 * @return stop threshold to stop the algorithm prematurely when a certain
420 * accuracy has been reached.
421 */
422 public double getStopThreshold() {
423 return stopThreshold;
424 }
425
426 /**
427 * Sets threshold to be used to keep the algorithm iterating in case that
428 * best estimated threshold using median of residuals is not small enough.
429 * Once a solution is found that generates a threshold below this value, the
430 * algorithm will stop.
431 * As in LMedS, the stop threshold can be used to prevent the PROMedS
432 * algorithm iterating too many times in cases where samples have a very
433 * similar accuracy.
434 * For instance, in cases where proportion of outliers is very small (close
435 * to 0%), and samples are very accurate (i.e. 1e-6), the algorithm would
436 * iterate for a long time trying to find the best solution when indeed
437 * there is no need to do that if a reasonable threshold has already been
438 * reached.
439 * Because of this behaviour the stop threshold can be set to a value much
440 * lower than the one typically used in RANSAC, and yet the algorithm could
441 * still produce even smaller thresholds in estimated results.
442 *
443 * @param stopThreshold stop threshold to stop the algorithm prematurely
444 * when a certain accuracy has been reached.
445 * @throws IllegalArgumentException if provided value is zero or negative
446 * @throws LockedException if robust estimator is locked because an
447 * estimation is already in progress.
448 */
449 public void setStopThreshold(final double stopThreshold) throws LockedException {
450 if (isLocked()) {
451 throw new LockedException();
452 }
453 if (stopThreshold <= MIN_STOP_THRESHOLD) {
454 throw new IllegalArgumentException();
455 }
456
457 this.stopThreshold = stopThreshold;
458 }
459
460 /**
461 * Returns quality scores corresponding to each pair of matched points.
462 * The larger the score value the better the quality of the matching.
463 *
464 * @return quality scores corresponding to each pair of matched points.
465 */
466 @Override
467 public double[] getQualityScores() {
468 return qualityScores;
469 }
470
471 /**
472 * Sets quality scores corresponding to each pair of matched points.
473 * The larger the score value the better the quality of the matching.
474 *
475 * @param qualityScores quality scores corresponding to each pair of matched
476 * points.
477 * @throws LockedException if robust estimator is locked because an
478 * estimation is already in progress.
479 * @throws IllegalArgumentException if provided quality scores length is
480 * smaller than MINIMUM_SIZE (i.e. 3 samples).
481 */
482 @Override
483 public void setQualityScores(final double[] qualityScores) throws LockedException {
484 if (isLocked()) {
485 throw new LockedException();
486 }
487 internalSetQualityScores(qualityScores);
488 }
489
490 /**
491 * Indicates if estimator is ready to start the metric 2D transformation
492 * estimation.
493 * This is true when input data (i.e. lists of matched points and quality
494 * scores) are provided and a minimum of MINIMUM_SIZE points are available.
495 *
496 * @return true if estimator is ready, false otherwise.
497 */
498 @Override
499 public boolean isReady() {
500 return super.isReady() && qualityScores != null && qualityScores.length == inputPoints.size();
501 }
502
503 /**
504 * Estimates a metric 2D transformation using a robust estimator and
505 * the best set of matched 2D point correspondences found using the robust
506 * estimator.
507 *
508 * @return a metric 2D transformation.
509 * @throws LockedException if robust estimator is locked because an
510 * estimation is already in progress.
511 * @throws NotReadyException if provided input data is not enough to start
512 * the estimation.
513 * @throws RobustEstimatorException if estimation fails for any reason
514 * (i.e. numerical instability, no solution available, etc).
515 */
516 @SuppressWarnings("DuplicatedCode")
517 @Override
518 public MetricTransformation2D estimate() throws LockedException, NotReadyException, RobustEstimatorException {
519 if (isLocked()) {
520 throw new LockedException();
521 }
522 if (!isReady()) {
523 throw new NotReadyException();
524 }
525
526 final var innerEstimator = new PROMedSRobustEstimator<>(
527 new PROMedSRobustEstimatorListener<MetricTransformation2D>() {
528
529 // point to be reused when computing residuals
530 private final Point2D testPoint = Point2D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
531
532 private final MetricTransformation2DEstimator nonRobustEstimator =
533 new MetricTransformation2DEstimator(isWeakMinimumSizeAllowed());
534
535 private final List<Point2D> subsetInputPoints = new ArrayList<>();
536 private final List<Point2D> subsetOutputPoints = new ArrayList<>();
537
538 @Override
539 public double getThreshold() {
540 return stopThreshold;
541 }
542
543 @Override
544 public int getTotalSamples() {
545 return inputPoints.size();
546 }
547
548 @Override
549 public int getSubsetSize() {
550 return MetricTransformation2DRobustEstimator.MINIMUM_SIZE;
551 }
552
553 @SuppressWarnings("DuplicatedCode")
554 @Override
555 public void estimatePreliminarSolutions(
556 final int[] samplesIndices, final List<MetricTransformation2D> 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 MetricTransformation2D 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 PROMedSMetricTransformation2DRobustEstimator.this.isReady();
586 }
587
588 @Override
589 public void onEstimateStart(final RobustEstimator<MetricTransformation2D> estimator) {
590 if (listener != null) {
591 listener.onEstimateStart(PROMedSMetricTransformation2DRobustEstimator.this);
592 }
593 }
594
595 @Override
596 public void onEstimateEnd(final RobustEstimator<MetricTransformation2D> estimator) {
597 if (listener != null) {
598 listener.onEstimateEnd(PROMedSMetricTransformation2DRobustEstimator.this);
599 }
600 }
601
602 @Override
603 public void onEstimateNextIteration(
604 final RobustEstimator<MetricTransformation2D> estimator, final int iteration) {
605 if (listener != null) {
606 listener.onEstimateNextIteration(
607 PROMedSMetricTransformation2DRobustEstimator.this, iteration);
608 }
609 }
610
611 @Override
612 public void onEstimateProgressChange(
613 final RobustEstimator<MetricTransformation2D> estimator, final float progress) {
614 if (listener != null) {
615 listener.onEstimateProgressChange(
616 PROMedSMetricTransformation2DRobustEstimator.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 }