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.EuclideanTransformation2D;
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 Euclidean 2D transformation for provided collections of
32   * matched 2D points using PROSAC algorithm.
33   */
34  public class PROSACEuclideanTransformation2DRobustEstimator extends EuclideanTransformation2DRobustEstimator {
35  
36      /**
37       * Constant defining default threshold to determine whether points are
38       * inliers or not.
39       * By default, 1.0 is considered a good value for cases where measures are
40       * done on pixels, since typically the minimum resolution is 1 pixel.
41       */
42      public static final double DEFAULT_THRESHOLD = 1.0;
43  
44      /**
45       * Minimum value that can be set as threshold.
46       * Threshold must be strictly greater than 0.0.
47       */
48      public static final double MIN_THRESHOLD = 0.0;
49  
50      /**
51       * Indicates that by default inliers will only be computed but not kept.
52       */
53      public static final boolean DEFAULT_COMPUTE_AND_KEEP_INLIERS = false;
54  
55      /**
56       * Indicates that by default residuals will only be computed but not kept.
57       */
58      public static final boolean DEFAULT_COMPUTE_AND_KEEP_RESIDUALS = false;
59  
60      /**
61       * Threshold to determine whether points are inliers or not when testing
62       * possible estimation solutions.
63       * The threshold refers to the amount of error (i.e. distance) a possible
64       * solution has on a matched pair of points.
65       */
66      private double threshold;
67  
68      /**
69       * Quality scores corresponding to each pair of matched points.
70       * The larger the score value the better the quality of the matching.
71       */
72      private double[] qualityScores;
73  
74      /**
75       * Indicates whether inliers must be computed and kept.
76       */
77      private boolean computeAndKeepInliers;
78  
79      /**
80       * Indicates whether residuals must be computed and kept.
81       */
82      private boolean computeAndKeepResiduals;
83  
84      /**
85       * Constructor.
86       */
87      public PROSACEuclideanTransformation2DRobustEstimator() {
88          super();
89          threshold = DEFAULT_THRESHOLD;
90          computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
91          computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
92      }
93  
94      /**
95       * Constructor with lists of points to be used to estimate an Euclidean 2D
96       * transformation.
97       * Points in the list located at the same position are considered to be
98       * matched. Hence, both lists must have the same size, and their size must
99       * be greater or equal than MINIMUM_SIZE.
100      *
101      * @param inputPoints  list of input points to be used to estimate an
102      *                     Euclidean 2D transformation.
103      * @param outputPoints list of output points to be used to estimate an
104      *                     Euclidean 2D transformation.
105      * @throws IllegalArgumentException if provided lists of points don't have
106      *                                  the same size or their size is smaller than MINIMUM_SIZE.
107      */
108     public PROSACEuclideanTransformation2DRobustEstimator(
109             final List<Point2D> inputPoints, final List<Point2D> outputPoints) {
110         super(inputPoints, outputPoints);
111         threshold = DEFAULT_THRESHOLD;
112         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
113         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
114     }
115 
116     /**
117      * Constructor.
118      *
119      * @param listener listener to be notified of events such as when estimation
120      *                 starts, ends or its progress significantly changes.
121      */
122     public PROSACEuclideanTransformation2DRobustEstimator(
123             final EuclideanTransformation2DRobustEstimatorListener 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 an
132      * Euclidean 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 an
140      *                     Euclidean 2D transformation.
141      * @param outputPoints list of output points to be used to estimate an
142      *                     Euclidean 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 PROSACEuclideanTransformation2DRobustEstimator(
147             final EuclideanTransformation2DRobustEstimatorListener 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 PROSACEuclideanTransformation2DRobustEstimator(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 an Euclidean 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 an
179      *                      Euclidean 2D transformation.
180      * @param outputPoints  list of output points to be used to estimate an
181      *                      Euclidean 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 PROSACEuclideanTransformation2DRobustEstimator(
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 PROSACEuclideanTransformation2DRobustEstimator(
213             final EuclideanTransformation2DRobustEstimatorListener 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 an
223      * Euclidean 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 an
231      *                      affine 2D transformation.
232      * @param outputPoints  list of output points to be used to estimate an
233      *                      affine 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 PROSACEuclideanTransformation2DRobustEstimator(
240             final EuclideanTransformation2DRobustEstimatorListener 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 3 points, false requires 4.
258      */
259     public PROSACEuclideanTransformation2DRobustEstimator(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 an Euclidean 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 an
274      *                               Euclidean 2D transformation.
275      * @param outputPoints           list of output points to be used to estimate an
276      *                               Euclidean 2D transformation.
277      * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
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 PROSACEuclideanTransformation2DRobustEstimator(
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 3 points, false requires 4.
295      */
296     public PROSACEuclideanTransformation2DRobustEstimator(
297             final EuclideanTransformation2DRobustEstimatorListener 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 an
306      * Euclidean 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 an
314      *                               Euclidean 2D transformation.
315      * @param outputPoints           list of output points to be used to estimate an
316      *                               Euclidean 2D transformation.
317      * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
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 PROSACEuclideanTransformation2DRobustEstimator(
322             final EuclideanTransformation2DRobustEstimatorListener 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 3 points, false requires 4.
336      * @throws IllegalArgumentException if provided quality scores length is
337      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
338      */
339     public PROSACEuclideanTransformation2DRobustEstimator(
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 an Euclidean 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 an
356      *                               Euclidean 2D transformation.
357      * @param outputPoints           list of output points to be used to estimate an
358      *                               Euclidean 2D transformation.
359      * @param qualityScores          quality scores corresponding to each pair of matched
360      *                               points.
361      * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
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 PROSACEuclideanTransformation2DRobustEstimator(
367             final List<Point2D> inputPoints, final List<Point2D> outputPoints,
368             final double[] qualityScores, 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 3 points, false requires 4.
389      * @throws IllegalArgumentException if provided quality scores length is
390      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
391      */
392     public PROSACEuclideanTransformation2DRobustEstimator(
393             final EuclideanTransformation2DRobustEstimatorListener listener,
394             final double[] qualityScores, 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 an
404      * Euclidean 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 an
412      *                               affine 2D transformation.
413      * @param outputPoints           list of output points to be used to estimate an
414      *                               affine 2D transformation.
415      * @param qualityScores          quality scores corresponding to each pair of matched
416      *                               points.
417      * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
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 PROSACEuclideanTransformation2DRobustEstimator(
422             final EuclideanTransformation2DRobustEstimatorListener listener, final List<Point2D> inputPoints,
423             final List<Point2D> outputPoints, final double[] qualityScores, final boolean weakMinimumSizeAllowed) {
424         super(listener, inputPoints, outputPoints, weakMinimumSizeAllowed);
425 
426         if (qualityScores.length != inputPoints.size()) {
427             throw new IllegalArgumentException();
428         }
429 
430         threshold = DEFAULT_THRESHOLD;
431         internalSetQualityScores(qualityScores);
432         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
433         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
434     }
435 
436     /**
437      * Returns threshold to determine whether points are inliers or not when
438      * testing possible estimation solutions.
439      * The threshold refers to the amount of error (i.e. Euclidean distance) a
440      * possible solution has on a matched pair of points.
441      *
442      * @return threshold to determine whether points are inliers or not when
443      * testing possible estimation solutions.
444      */
445     public double getThreshold() {
446         return threshold;
447     }
448 
449     /**
450      * Sets threshold to determine whether points are inliers or not when
451      * testing possible estimation solutions.
452      * The threshold refers to the amount of error (i.e. Euclidean distance) a
453      * possible solution has on a matched pair of points.
454      *
455      * @param threshold threshold to determine whether points are inliers or not
456      *                  when testing possible estimation solutions.
457      * @throws IllegalArgumentException if provided values is equal or less than
458      *                                  zero.
459      * @throws LockedException          if robust estimator is locked because an
460      *                                  estimation is already in progress.
461      */
462     public void setThreshold(final double threshold) throws LockedException {
463         if (isLocked()) {
464             throw new LockedException();
465         }
466         if (threshold <= MIN_THRESHOLD) {
467             throw new IllegalArgumentException();
468         }
469         this.threshold = threshold;
470     }
471 
472     /**
473      * Returns quality scores corresponding to each pair of matched points.
474      * The larger the score value the better the quality of the matching.
475      *
476      * @return quality scores corresponding to each pair of matched points.
477      */
478     @Override
479     public double[] getQualityScores() {
480         return qualityScores;
481     }
482 
483     /**
484      * Sets quality scores corresponding to each pair of matched points.
485      * The larger the score value the better the quality of the matching.
486      *
487      * @param qualityScores quality scores corresponding to each pair of matched
488      *                      points.
489      * @throws LockedException          if robust estimator is locked because an
490      *                                  estimation is already in progress.
491      * @throws IllegalArgumentException if provided quality scores length is
492      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
493      */
494     @Override
495     public void setQualityScores(final double[] qualityScores) throws LockedException {
496         if (isLocked()) {
497             throw new LockedException();
498         }
499         internalSetQualityScores(qualityScores);
500     }
501 
502     /**
503      * Indicates if estimator is ready to start the Euclidean 2D transformation
504      * estimation.
505      * This is true when input data (i.e. lists of matched points and quality
506      * scores) are provided and a minimum of MINIMUM_SIZE points are available.
507      *
508      * @return true if estimator is ready, false otherwise.
509      */
510     @Override
511     public boolean isReady() {
512         return super.isReady() && qualityScores != null && qualityScores.length == inputPoints.size();
513     }
514 
515     /**
516      * Indicates whether inliers must be computed and kept.
517      *
518      * @return true if inliers must be computed and kept, false if inliers
519      * only need to be computed but not kept.
520      */
521     public boolean isComputeAndKeepInliersEnabled() {
522         return computeAndKeepInliers;
523     }
524 
525     /**
526      * Specifies whether inliers must be computed and kept.
527      *
528      * @param computeAndKeepInliers true if inliers must be computed and kept,
529      *                              false if inliers only need to be computed but not kept.
530      * @throws LockedException if estimator is locked.
531      */
532     public void setComputeAndKeepInliersEnabled(final boolean computeAndKeepInliers) throws LockedException {
533         if (isLocked()) {
534             throw new LockedException();
535         }
536         this.computeAndKeepInliers = computeAndKeepInliers;
537     }
538 
539     /**
540      * Indicates whether residuals must be computed and kept.
541      *
542      * @return true if residuals must be computed and kept, false if residuals
543      * only need to be computed but not kept.
544      */
545     public boolean isComputeAndKeepResidualsEnabled() {
546         return computeAndKeepResiduals;
547     }
548 
549     /**
550      * Specifies whether residuals must be computed and kept.
551      *
552      * @param computeAndKeepResiduals true if residuals must be computed and
553      *                                kept, false if residuals only need to be computed but not kept.
554      * @throws LockedException if estimator is locked.
555      */
556     public void setComputeAndKeepResidualsEnabled(final boolean computeAndKeepResiduals) throws LockedException {
557         if (isLocked()) {
558             throw new LockedException();
559         }
560         this.computeAndKeepResiduals = computeAndKeepResiduals;
561     }
562 
563     /**
564      * Estimates an Euclidean 2D transformation using a robust estimator and
565      * the best set of matched 2D point correspondences found using the robust
566      * estimator.
567      *
568      * @return an Euclidean 2D transformation.
569      * @throws LockedException          if robust estimator is locked because an
570      *                                  estimation is already in progress.
571      * @throws NotReadyException        if provided input data is not enough to start
572      *                                  the estimation.
573      * @throws RobustEstimatorException if estimation fails for any reason
574      *                                  (i.e. numerical instability, no solution available, etc).
575      */
576     @SuppressWarnings("DuplicatedCode")
577     @Override
578     public EuclideanTransformation2D 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<EuclideanTransformation2D>() {
588 
589                     // point to be reused when computing residuals
590                     private final Point2D testPoint = Point2D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
591 
592                     private final EuclideanTransformation2DEstimator nonRobustEstimator =
593                             new EuclideanTransformation2DEstimator(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                     @SuppressWarnings("DuplicatedCode")
614                     @Override
615                     public void estimatePreliminarSolutions(
616                             final int[] samplesIndices, final List<EuclideanTransformation2D> solutions) {
617                         subsetInputPoints.clear();
618                         subsetOutputPoints.clear();
619                         for (final var samplesIndex : samplesIndices) {
620                             subsetInputPoints.add(inputPoints.get(samplesIndex));
621                             subsetOutputPoints.add(outputPoints.get(samplesIndex));
622                         }
623 
624                         try {
625                             nonRobustEstimator.setPoints(subsetInputPoints, subsetOutputPoints);
626                             solutions.add(nonRobustEstimator.estimate());
627                         } catch (final Exception e) {
628                             // if points are coincident, no solution is added
629                         }
630                     }
631 
632                     @Override
633                     public double computeResidual(final EuclideanTransformation2D currentEstimation, final int i) {
634                         final var inputPoint = inputPoints.get(i);
635                         final var outputPoint = outputPoints.get(i);
636 
637                         // transform input point and store result in mTestPoint
638                         currentEstimation.transform(inputPoint, testPoint);
639 
640                         return outputPoint.distanceTo(testPoint);
641                     }
642 
643                     @Override
644                     public boolean isReady() {
645                         return PROSACEuclideanTransformation2DRobustEstimator.this.isReady();
646                     }
647 
648                     @Override
649                     public void onEstimateStart(final RobustEstimator<EuclideanTransformation2D> estimator) {
650                         if (listener != null) {
651                             listener.onEstimateStart(PROSACEuclideanTransformation2DRobustEstimator.this);
652                         }
653                     }
654 
655                     @Override
656                     public void onEstimateEnd(final RobustEstimator<EuclideanTransformation2D> estimator) {
657                         if (listener != null) {
658                             listener.onEstimateEnd(PROSACEuclideanTransformation2DRobustEstimator.this);
659                         }
660                     }
661 
662                     @Override
663                     public void onEstimateNextIteration(
664                             final RobustEstimator<EuclideanTransformation2D> estimator, final int iteration) {
665                         if (listener != null) {
666                             listener.onEstimateNextIteration(
667                                     PROSACEuclideanTransformation2DRobustEstimator.this, iteration);
668                         }
669                     }
670 
671                     @Override
672                     public void onEstimateProgressChange(
673                             final RobustEstimator<EuclideanTransformation2D> estimator, final float progress) {
674                         if (listener != null) {
675                             listener.onEstimateProgressChange(
676                                     PROSACEuclideanTransformation2DRobustEstimator.this, progress);
677                         }
678                     }
679 
680                     @Override
681                     public double[] getQualityScores() {
682                         return qualityScores;
683                     }
684                 });
685 
686         try {
687             locked = true;
688             inliersData = null;
689             innerEstimator.setComputeAndKeepInliersEnabled(computeAndKeepInliers || refineResult);
690             innerEstimator.setComputeAndKeepResidualsEnabled(computeAndKeepResiduals || refineResult);
691             innerEstimator.setConfidence(confidence);
692             innerEstimator.setMaxIterations(maxIterations);
693             innerEstimator.setProgressDelta(progressDelta);
694             final var transformation = innerEstimator.estimate();
695             inliersData = innerEstimator.getInliersData();
696             return attemptRefine(transformation);
697         } catch (final com.irurueta.numerical.LockedException e) {
698             throw new LockedException(e);
699         } catch (final com.irurueta.numerical.NotReadyException e) {
700             throw new NotReadyException(e);
701         } finally {
702             locked = false;
703         }
704     }
705 
706     /**
707      * Returns method being used for robust estimation.
708      *
709      * @return method being used for robust estimation.
710      */
711     @Override
712     public RobustEstimatorMethod getMethod() {
713         return RobustEstimatorMethod.PROSAC;
714     }
715 
716     /**
717      * Gets standard deviation used for Levenberg-Marquardt fitting during
718      * refinement.
719      * Returned value gives an indication of how much variance each residual
720      * has.
721      * Typically, this value is related to the threshold used on each robust
722      * estimation, since residuals of found inliers are within the range of
723      * such threshold.
724      *
725      * @return standard deviation used for refinement.
726      */
727     @Override
728     protected double getRefinementStandardDeviation() {
729         return threshold;
730     }
731 
732     /**
733      * Sets quality scores corresponding to each pair of matched points.
734      * This method is used internally and does not check whether instance is
735      * locked or not.
736      *
737      * @param qualityScores quality scores to be set.
738      * @throws IllegalArgumentException if provided quality scores length is
739      *                                  smaller than MINIMUM_SIZE.
740      */
741     private void internalSetQualityScores(final double[] qualityScores) {
742         if (qualityScores.length < getMinimumPoints()) {
743             throw new IllegalArgumentException();
744         }
745 
746         this.qualityScores = qualityScores;
747     }
748 }