View Javadoc
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.PROSACRobustEstimator;
22  import com.irurueta.numerical.robust.PROSACRobustEstimatorListener;
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 points using PROSAC algorithm.
33   */
34  @SuppressWarnings("DuplicatedCode")
35  public class PROSACMetricTransformation2DRobustEstimator extends MetricTransformation2DRobustEstimator {
36  
37      /**
38       * Constant defining default threshold to determine whether points are
39       * inliers or not.
40       * By default, 1.0 is considered a good value for cases where measures are
41       * done on pixels, since typically the minimum resolution is 1 pixel.
42       */
43      public static final double DEFAULT_THRESHOLD = 1.0;
44  
45      /**
46       * Minimum value that can be set as threshold.
47       * Threshold must be strictly greater than 0.0.
48       */
49      public static final double MIN_THRESHOLD = 0.0;
50  
51      /**
52       * Indicates that by default inliers will only be computed but not kept.
53       */
54      public static final boolean DEFAULT_COMPUTE_AND_KEEP_INLIERS = false;
55  
56      /**
57       * Indicates that by default residuals will only be computed but not kept.
58       */
59      public static final boolean DEFAULT_COMPUTE_AND_KEEP_RESIDUALS = false;
60  
61      /**
62       * Threshold to determine whether points are inliers or not when testing
63       * possible estimation solutions.
64       * The threshold refers to the amount of error (i.e. distance) a possible
65       * solution has on a matched pair of points.
66       */
67      private double threshold;
68  
69      /**
70       * Quality scores corresponding to each pair of matched points.
71       * The larger the score value the better the quality of the matching.
72       */
73      private double[] qualityScores;
74  
75      /**
76       * Indicates whether inliers must be computed and kept.
77       */
78      private boolean computeAndKeepInliers;
79  
80      /**
81       * Indicates whether residuals must be computed and kept.
82       */
83      private boolean computeAndKeepResiduals;
84  
85      /**
86       * Constructor.
87       */
88      public PROSACMetricTransformation2DRobustEstimator() {
89          super();
90          threshold = DEFAULT_THRESHOLD;
91          computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
92          computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
93      }
94  
95      /**
96       * Constructor with lists of points to be used to estimate a metric 2D
97       * transformation.
98       * Points in the list located at the same position are considered to be
99       * matched. Hence, both lists must have the same size, and their size must
100      * be greater or equal than MINIMUM_SIZE.
101      *
102      * @param inputPoints  list of input points to be used to estimate a
103      *                     metric 2D transformation.
104      * @param outputPoints list of output points to be used to estimate a
105      *                     metric 2D transformation.
106      * @throws IllegalArgumentException if provided lists of points don't have
107      *                                  the same size or their size is smaller than MINIMUM_SIZE.
108      */
109     public PROSACMetricTransformation2DRobustEstimator(
110             final List<Point2D> inputPoints, final List<Point2D> outputPoints) {
111         super(inputPoints, outputPoints);
112         threshold = DEFAULT_THRESHOLD;
113         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
114         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
115     }
116 
117     /**
118      * Constructor.
119      *
120      * @param listener listener to be notified of events such as when estimation
121      *                 starts, ends or its progress significantly changes.
122      */
123     public PROSACMetricTransformation2DRobustEstimator(final MetricTransformation2DRobustEstimatorListener listener) {
124         super(listener);
125         threshold = DEFAULT_THRESHOLD;
126         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
127         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
128     }
129 
130     /**
131      * Constructor with listener and lists of points to be used to estimate a
132      * metric 2D transformation.
133      * Points in the list located at the same position are considered to be
134      * matched. Hence, both lists must have the same size, and their size must
135      * be greater or equal than MINIMUM_SIZE.
136      *
137      * @param listener     listener to be notified of events such as when estimation
138      *                     stars, ends or its progress significantly changes.
139      * @param inputPoints  list of input points to be used to estimate a
140      *                     metric 2D transformation.
141      * @param outputPoints list of output points to be used to estimate a
142      *                     metric 2D transformation.
143      * @throws IllegalArgumentException if provided lists of points don't have
144      *                                  the same size or their size is smaller than MINIMUM_SIZE.
145      */
146     public PROSACMetricTransformation2DRobustEstimator(
147             final MetricTransformation2DRobustEstimatorListener listener,
148             final List<Point2D> inputPoints, final List<Point2D> outputPoints) {
149         super(listener, inputPoints, outputPoints);
150         threshold = DEFAULT_THRESHOLD;
151         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
152         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
153     }
154 
155     /**
156      * Constructor.
157      *
158      * @param qualityScores quality scores corresponding to each pair of matched
159      *                      points.
160      * @throws IllegalArgumentException if provided quality scores length is
161      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
162      */
163     public PROSACMetricTransformation2DRobustEstimator(final double[] qualityScores) {
164         super();
165         threshold = DEFAULT_THRESHOLD;
166         internalSetQualityScores(qualityScores);
167         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
168         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
169     }
170 
171     /**
172      * Constructor with lists of points to be used to estimate a metric 2D
173      * transformation.
174      * Points in the list located at the same position are considered to be
175      * matched. Hence, both lists must have the same size, and their size must
176      * be greater or equal than MINIMUM_SIZE.
177      *
178      * @param inputPoints   list of input points to be used to estimate a
179      *                      metric 2D transformation.
180      * @param outputPoints  list of output points to be used to estimate a
181      *                      metric 2D transformation.
182      * @param qualityScores quality scores corresponding to each pair of matched
183      *                      points.
184      * @throws IllegalArgumentException if provided lists of points and array
185      *                                  of quality scores don't have the same size or their size is smaller than
186      *                                  MINIMUM_SIZE.
187      */
188     public PROSACMetricTransformation2DRobustEstimator(
189             final List<Point2D> inputPoints, final List<Point2D> outputPoints, final double[] qualityScores) {
190         super(inputPoints, outputPoints);
191 
192         if (qualityScores.length != inputPoints.size()) {
193             throw new IllegalArgumentException();
194         }
195 
196         threshold = DEFAULT_THRESHOLD;
197         internalSetQualityScores(qualityScores);
198         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
199         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
200     }
201 
202     /**
203      * Constructor.
204      *
205      * @param listener      listener to be notified of events such as when estimation
206      *                      starts, ends or its progress significantly changes.
207      * @param qualityScores quality scores corresponding to each pair of matched
208      *                      points.
209      * @throws IllegalArgumentException if provided quality scores length is
210      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
211      */
212     public PROSACMetricTransformation2DRobustEstimator(
213             final MetricTransformation2DRobustEstimatorListener listener, final double[] qualityScores) {
214         super(listener);
215         threshold = DEFAULT_THRESHOLD;
216         internalSetQualityScores(qualityScores);
217         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
218         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
219     }
220 
221     /**
222      * Constructor with listener and lists of points to be used to estimate a
223      * metric 2D transformation.
224      * Points in the list located at the same position are considered to be
225      * matched. Hence, both lists must have the same size, and their size must
226      * be greater or equal than MINIMUM_SIZE.
227      *
228      * @param listener      listener to be notified of events such as when estimation
229      *                      stars, ends or its progress significantly changes.
230      * @param inputPoints   list of input points to be used to estimate a
231      *                      metric 2D transformation.
232      * @param outputPoints  list of output points to be used to estimate a
233      *                      metric 2D transformation.
234      * @param qualityScores quality scores corresponding to each pair of matched
235      *                      points.
236      * @throws IllegalArgumentException if provided lists of points don't have
237      *                                  the same size or their size is smaller than MINIMUM_SIZE.
238      */
239     public PROSACMetricTransformation2DRobustEstimator(
240             final MetricTransformation2DRobustEstimatorListener listener,
241             final List<Point2D> inputPoints, final List<Point2D> outputPoints, final double[] qualityScores) {
242         super(listener, inputPoints, outputPoints);
243 
244         if (qualityScores.length != inputPoints.size()) {
245             throw new IllegalArgumentException();
246         }
247 
248         threshold = DEFAULT_THRESHOLD;
249         internalSetQualityScores(qualityScores);
250         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
251         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
252     }
253 
254     /**
255      * Constructor.
256      *
257      * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
258      */
259     public PROSACMetricTransformation2DRobustEstimator(final boolean weakMinimumSizeAllowed) {
260         super(weakMinimumSizeAllowed);
261         threshold = DEFAULT_THRESHOLD;
262         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
263         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
264     }
265 
266     /**
267      * Constructor with lists of points to be used to estimate a metric 2D
268      * transformation.
269      * Points in the list located at the same position are considered to be
270      * matched. Hence, both lists must have the same size, and their size must
271      * be greater or equal than MINIMUM_SIZE.
272      *
273      * @param inputPoints            list of input points to be used to estimate a
274      *                               metric 2D transformation.
275      * @param outputPoints           list of output points to be used to estimate a
276      *                               metric 2D transformation.
277      * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
278      * @throws IllegalArgumentException if provided lists of points don't have
279      *                                  the same size or their size is smaller than MINIMUM_SIZE.
280      */
281     public PROSACMetricTransformation2DRobustEstimator(
282             final List<Point2D> inputPoints, final List<Point2D> outputPoints, final boolean weakMinimumSizeAllowed) {
283         super(inputPoints, outputPoints, weakMinimumSizeAllowed);
284         threshold = DEFAULT_THRESHOLD;
285         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
286         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
287     }
288 
289     /**
290      * Constructor.
291      *
292      * @param listener               listener to be notified of events such as when estimation
293      *                               starts, ends or its progress significantly changes.
294      * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
295      */
296     public PROSACMetricTransformation2DRobustEstimator(
297             final MetricTransformation2DRobustEstimatorListener listener, final boolean weakMinimumSizeAllowed) {
298         super(listener, weakMinimumSizeAllowed);
299         threshold = DEFAULT_THRESHOLD;
300         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
301         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
302     }
303 
304     /**
305      * Constructor with listener and lists of points to be used to estimate a
306      * metric 2D transformation.
307      * Points in the list located at the same position are considered to be
308      * matched. Hence, both lists must have the same size, and their size must
309      * be greater or equal than MINIMUM_SIZE.
310      *
311      * @param listener               listener to be notified of events such as when estimation
312      *                               stars, ends or its progress significantly changes.
313      * @param inputPoints            list of input points to be used to estimate a
314      *                               metric 2D transformation.
315      * @param outputPoints           list of output points to be used to estimate a
316      *                               metric 2D transformation.
317      * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
318      * @throws IllegalArgumentException if provided lists of points don't have
319      *                                  the same size or their size is smaller than MINIMUM_SIZE.
320      */
321     public PROSACMetricTransformation2DRobustEstimator(
322             final MetricTransformation2DRobustEstimatorListener listener,
323             final List<Point2D> inputPoints, final List<Point2D> outputPoints, final boolean weakMinimumSizeAllowed) {
324         super(listener, inputPoints, outputPoints, weakMinimumSizeAllowed);
325         threshold = DEFAULT_THRESHOLD;
326         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
327         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
328     }
329 
330     /**
331      * Constructor.
332      *
333      * @param qualityScores          quality scores corresponding to each pair of matched
334      *                               points.
335      * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
336      * @throws IllegalArgumentException if provided quality scores length is
337      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
338      */
339     public PROSACMetricTransformation2DRobustEstimator(
340             final double[] qualityScores, final boolean weakMinimumSizeAllowed) {
341         super(weakMinimumSizeAllowed);
342         threshold = DEFAULT_THRESHOLD;
343         internalSetQualityScores(qualityScores);
344         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
345         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
346     }
347 
348     /**
349      * Constructor with lists of points to be used to estimate a metric 2D
350      * transformation.
351      * Points in the list located at the same position are considered to be
352      * matched. Hence, both lists must have the same size, and their size must
353      * be greater or equal than MINIMUM_SIZE.
354      *
355      * @param inputPoints            list of input points to be used to estimate a
356      *                               metric 2D transformation.
357      * @param outputPoints           list of output points to be used to estimate a
358      *                               metric 2D transformation.
359      * @param qualityScores          quality scores corresponding to each pair of matched
360      *                               points.
361      * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
362      * @throws IllegalArgumentException if provided lists of points and array
363      *                                  of quality scores don't have the same size or their size is smaller than
364      *                                  MINIMUM_SIZE.
365      */
366     public PROSACMetricTransformation2DRobustEstimator(
367             final List<Point2D> inputPoints, final List<Point2D> outputPoints, final double[] qualityScores,
368             final boolean weakMinimumSizeAllowed) {
369         super(inputPoints, outputPoints, weakMinimumSizeAllowed);
370 
371         if (qualityScores.length != inputPoints.size()) {
372             throw new IllegalArgumentException();
373         }
374 
375         threshold = DEFAULT_THRESHOLD;
376         internalSetQualityScores(qualityScores);
377         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
378         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
379     }
380 
381     /**
382      * Constructor.
383      *
384      * @param listener               listener to be notified of events such as when estimation
385      *                               starts, ends or its progress significantly changes.
386      * @param qualityScores          quality scores corresponding to each pair of matched
387      *                               points.
388      * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
389      * @throws IllegalArgumentException if provided quality scores length is
390      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
391      */
392     public PROSACMetricTransformation2DRobustEstimator(
393             final MetricTransformation2DRobustEstimatorListener listener, final double[] qualityScores,
394             final boolean weakMinimumSizeAllowed) {
395         super(listener, weakMinimumSizeAllowed);
396         threshold = DEFAULT_THRESHOLD;
397         internalSetQualityScores(qualityScores);
398         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
399         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
400     }
401 
402     /**
403      * Constructor with listener and lists of points to be used to estimate a
404      * metric 2D transformation.
405      * Points in the list located at the same position are considered to be
406      * matched. Hence, both lists must have the same size, and their size must
407      * be greater or equal than MINIMUM_SIZE.
408      *
409      * @param listener               listener to be notified of events such as when estimation
410      *                               stars, ends or its progress significantly changes.
411      * @param inputPoints            list of input points to be used to estimate a
412      *                               metric 2D transformation.
413      * @param outputPoints           list of output points to be used to estimate a
414      *                               metric 2D transformation.
415      * @param qualityScores          quality scores corresponding to each pair of matched
416      *                               points.
417      * @param weakMinimumSizeAllowed true allows 2 points, false requires 3.
418      * @throws IllegalArgumentException if provided lists of points don't have
419      *                                  the same size or their size is smaller than MINIMUM_SIZE.
420      */
421     public PROSACMetricTransformation2DRobustEstimator(
422             final MetricTransformation2DRobustEstimatorListener listener,
423             final List<Point2D> inputPoints, final List<Point2D> outputPoints,
424             final double[] qualityScores, final boolean weakMinimumSizeAllowed) {
425         super(listener, inputPoints, outputPoints, weakMinimumSizeAllowed);
426 
427         if (qualityScores.length != inputPoints.size()) {
428             throw new IllegalArgumentException();
429         }
430 
431         threshold = DEFAULT_THRESHOLD;
432         internalSetQualityScores(qualityScores);
433         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
434         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
435     }
436 
437     /**
438      * Returns threshold to determine whether points are inliers or not when
439      * testing possible estimation solutions.
440      * The threshold refers to the amount of error (i.e. Euclidean distance) a
441      * possible solution has on a matched pair of points.
442      *
443      * @return threshold to determine whether points are inliers or not when
444      * testing possible estimation solutions.
445      */
446     public double getThreshold() {
447         return threshold;
448     }
449 
450     /**
451      * Sets threshold to determine whether points are inliers or not when
452      * testing possible estimation solutions.
453      * The threshold refers to the amount of error (i.e. Euclidean distance) a
454      * possible solution has on a matched pair of points.
455      *
456      * @param threshold threshold to determine whether points are inliers or not
457      *                  when testing possible estimation solutions.
458      * @throws IllegalArgumentException if provided values is equal or less than
459      *                                  zero.
460      * @throws LockedException          if robust estimator is locked because an
461      *                                  estimation is already in progress.
462      */
463     public void setThreshold(final double threshold) throws LockedException {
464         if (isLocked()) {
465             throw new LockedException();
466         }
467         if (threshold <= MIN_THRESHOLD) {
468             throw new IllegalArgumentException();
469         }
470         this.threshold = threshold;
471     }
472 
473     /**
474      * Returns quality scores corresponding to each pair of matched points.
475      * The larger the score value the better the quality of the matching.
476      *
477      * @return quality scores corresponding to each pair of matched points.
478      */
479     @Override
480     public double[] getQualityScores() {
481         return qualityScores;
482     }
483 
484     /**
485      * Sets quality scores corresponding to each pair of matched points.
486      * The larger the score value the better the quality of the matching.
487      *
488      * @param qualityScores quality scores corresponding to each pair of matched
489      *                      points.
490      * @throws LockedException          if robust estimator is locked because an
491      *                                  estimation is already in progress.
492      * @throws IllegalArgumentException if provided quality scores length is
493      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
494      */
495     @Override
496     public void setQualityScores(final double[] qualityScores) throws LockedException {
497         if (isLocked()) {
498             throw new LockedException();
499         }
500         internalSetQualityScores(qualityScores);
501     }
502 
503     /**
504      * Indicates if estimator is ready to start the metric 2D transformation
505      * estimation.
506      * This is true when input data (i.e. lists of matched points and quality
507      * scores) are provided and a minimum of MINIMUM_SIZE points are available.
508      *
509      * @return true if estimator is ready, false otherwise.
510      */
511     @Override
512     public boolean isReady() {
513         return super.isReady() && qualityScores != null && qualityScores.length == inputPoints.size();
514     }
515 
516     /**
517      * Indicates whether inliers must be computed and kept.
518      *
519      * @return true if inliers must be computed and kept, false if inliers
520      * only need to be computed but not kept.
521      */
522     public boolean isComputeAndKeepInliersEnabled() {
523         return computeAndKeepInliers;
524     }
525 
526     /**
527      * Specifies whether inliers must be computed and kept.
528      *
529      * @param computeAndKeepInliers true if inliers must be computed and kept,
530      *                              false if inliers only need to be computed but not kept.
531      * @throws LockedException if estimator is locked.
532      */
533     public void setComputeAndKeepInliersEnabled(final boolean computeAndKeepInliers) throws LockedException {
534         if (isLocked()) {
535             throw new LockedException();
536         }
537         this.computeAndKeepInliers = computeAndKeepInliers;
538     }
539 
540     /**
541      * Indicates whether residuals must be computed and kept.
542      *
543      * @return true if residuals must be computed and kept, false if residuals
544      * only need to be computed but not kept.
545      */
546     public boolean isComputeAndKeepResidualsEnabled() {
547         return computeAndKeepResiduals;
548     }
549 
550     /**
551      * Specifies whether residuals must be computed and kept.
552      *
553      * @param computeAndKeepResiduals true if residuals must be computed and
554      *                                kept, false if residuals only need to be computed but not kept.
555      * @throws LockedException if estimator is locked.
556      */
557     public void setComputeAndKeepResidualsEnabled(final boolean computeAndKeepResiduals) throws LockedException {
558         if (isLocked()) {
559             throw new LockedException();
560         }
561         this.computeAndKeepResiduals = computeAndKeepResiduals;
562     }
563 
564     /**
565      * Estimates a metric 2D transformation using a robust estimator and
566      * the best set of matched 2D point correspondences found using the robust
567      * estimator.
568      *
569      * @return a metric 2D transformation.
570      * @throws LockedException          if robust estimator is locked because an
571      *                                  estimation is already in progress.
572      * @throws NotReadyException        if provided input data is not enough to start
573      *                                  the estimation.
574      * @throws RobustEstimatorException if estimation fails for any reason
575      *                                  (i.e. numerical instability, no solution available, etc).
576      */
577     @Override
578     public MetricTransformation2D estimate() throws LockedException, NotReadyException, RobustEstimatorException {
579         if (isLocked()) {
580             throw new LockedException();
581         }
582         if (!isReady()) {
583             throw new NotReadyException();
584         }
585 
586         final var innerEstimator = new PROSACRobustEstimator<>(
587                 new PROSACRobustEstimatorListener<MetricTransformation2D>() {
588 
589                     // point to be reused when computing residuals
590                     private final Point2D testPoint = Point2D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
591 
592                     private final MetricTransformation2DEstimator nonRobustEstimator =
593                             new MetricTransformation2DEstimator(isWeakMinimumSizeAllowed());
594 
595                     private final List<Point2D> subsetInputPoints = new ArrayList<>();
596                     private final List<Point2D> subsetOutputPoints = new ArrayList<>();
597 
598                     @Override
599                     public double getThreshold() {
600                         return threshold;
601                     }
602 
603                     @Override
604                     public int getTotalSamples() {
605                         return inputPoints.size();
606                     }
607 
608                     @Override
609                     public int getSubsetSize() {
610                         return nonRobustEstimator.getMinimumPoints();
611                     }
612 
613                     @Override
614                     public void estimatePreliminarSolutions(
615                             final int[] samplesIndices, final List<MetricTransformation2D> solutions) {
616                         subsetInputPoints.clear();
617                         subsetOutputPoints.clear();
618                         for (final var samplesIndex : samplesIndices) {
619                             subsetInputPoints.add(inputPoints.get(samplesIndex));
620                             subsetOutputPoints.add(outputPoints.get(samplesIndex));
621                         }
622 
623                         try {
624                             nonRobustEstimator.setPoints(subsetInputPoints, subsetOutputPoints);
625                             solutions.add(nonRobustEstimator.estimate());
626                         } catch (final Exception e) {
627                             // if points are coincident, no solution is added
628                         }
629                     }
630 
631                     @Override
632                     public double computeResidual(final MetricTransformation2D currentEstimation, final int i) {
633                         final var inputPoint = inputPoints.get(i);
634                         final var outputPoint = outputPoints.get(i);
635 
636                         // transform input point and store result in mTestPoint
637                         currentEstimation.transform(inputPoint, testPoint);
638 
639                         return outputPoint.distanceTo(testPoint);
640                     }
641 
642                     @Override
643                     public boolean isReady() {
644                         return PROSACMetricTransformation2DRobustEstimator.this.isReady();
645                     }
646 
647                     @Override
648                     public void onEstimateStart(final RobustEstimator<MetricTransformation2D> estimator) {
649                         if (listener != null) {
650                             listener.onEstimateStart(PROSACMetricTransformation2DRobustEstimator.this);
651                         }
652                     }
653 
654                     @Override
655                     public void onEstimateEnd(final RobustEstimator<MetricTransformation2D> estimator) {
656                         if (listener != null) {
657                             listener.onEstimateEnd(PROSACMetricTransformation2DRobustEstimator.this);
658                         }
659                     }
660 
661                     @Override
662                     public void onEstimateNextIteration(
663                             final RobustEstimator<MetricTransformation2D> estimator, final int iteration) {
664                         if (listener != null) {
665                             listener.onEstimateNextIteration(
666                                     PROSACMetricTransformation2DRobustEstimator.this, iteration);
667                         }
668                     }
669 
670                     @Override
671                     public void onEstimateProgressChange(
672                             final RobustEstimator<MetricTransformation2D> estimator, final float progress) {
673                         if (listener != null) {
674                             listener.onEstimateProgressChange(
675                                     PROSACMetricTransformation2DRobustEstimator.this, progress);
676                         }
677                     }
678 
679                     @Override
680                     public double[] getQualityScores() {
681                         return qualityScores;
682                     }
683                 });
684 
685         try {
686             locked = true;
687             inliersData = null;
688             innerEstimator.setComputeAndKeepInliersEnabled(computeAndKeepInliers || refineResult);
689             innerEstimator.setComputeAndKeepResidualsEnabled(computeAndKeepResiduals || refineResult);
690             innerEstimator.setConfidence(confidence);
691             innerEstimator.setMaxIterations(maxIterations);
692             innerEstimator.setProgressDelta(progressDelta);
693             final var transformation = innerEstimator.estimate();
694             inliersData = innerEstimator.getInliersData();
695             return attemptRefine(transformation);
696         } catch (final com.irurueta.numerical.LockedException e) {
697             throw new LockedException(e);
698         } catch (final com.irurueta.numerical.NotReadyException e) {
699             throw new NotReadyException(e);
700         } finally {
701             locked = false;
702         }
703     }
704 
705     /**
706      * Returns method being used for robust estimation.
707      *
708      * @return method being used for robust estimation.
709      */
710     @Override
711     public RobustEstimatorMethod getMethod() {
712         return RobustEstimatorMethod.PROSAC;
713     }
714 
715     /**
716      * Gets standard deviation used for Levenberg-Marquardt fitting during
717      * refinement.
718      * Returned value gives an indication of how much variance each residual
719      * has.
720      * Typically, this value is related to the threshold used on each robust
721      * estimation, since residuals of found inliers are within the range of
722      * such threshold.
723      *
724      * @return standard deviation used for refinement.
725      */
726     @Override
727     protected double getRefinementStandardDeviation() {
728         return threshold;
729     }
730 
731     /**
732      * Sets quality scores corresponding to each pair of matched points.
733      * This method is used internally and does not check whether instance is
734      * locked or not.
735      *
736      * @param qualityScores quality scores to be set.
737      * @throws IllegalArgumentException if provided quality scores length is
738      *                                  smaller than MINIMUM_SIZE.
739      */
740     private void internalSetQualityScores(final double[] qualityScores) {
741         if (qualityScores.length < getMinimumPoints()) {
742             throw new IllegalArgumentException();
743         }
744 
745         this.qualityScores = qualityScores;
746     }
747 }