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.EuclideanTransformation3D;
20  import com.irurueta.geometry.Point3D;
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 3D transformation for provided collections of
32   * matched 3D points using PROSAC algorithm.
33   */
34  @SuppressWarnings("DuplicatedCode")
35  public class PROSACEuclideanTransformation3DRobustEstimator extends EuclideanTransformation3DRobustEstimator {
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 PROSACEuclideanTransformation3DRobustEstimator() {
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 an Euclidean 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 an
103      *                     Euclidean 3D transformation.
104      * @param outputPoints list of output points to be used to estimate an
105      *                     Euclidean 3D 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 PROSACEuclideanTransformation3DRobustEstimator(
110             final List<Point3D> inputPoints, final List<Point3D> 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 PROSACEuclideanTransformation3DRobustEstimator(
124             final EuclideanTransformation3DRobustEstimatorListener listener) {
125         super(listener);
126         threshold = DEFAULT_THRESHOLD;
127         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
128         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
129     }
130 
131     /**
132      * Constructor with listener and lists of points to be used to estimate an
133      * Euclidean 3D transformation.
134      * Points in the list located at the same position are considered to be
135      * matched. Hence, both lists must have the same size, and their size must
136      * be greater or equal than MINIMUM_SIZE.
137      *
138      * @param listener     listener to be notified of events such as when estimation
139      *                     stars, ends or its progress significantly changes.
140      * @param inputPoints  list of input points to be used to estimate an
141      *                     affine 3D transformation.
142      * @param outputPoints list of output points to be used to estimate an
143      *                     affine 3D transformation.
144      * @throws IllegalArgumentException if provided lists of points don't have
145      *                                  the same size or their size is smaller than MINIMUM_SIZE.
146      */
147     public PROSACEuclideanTransformation3DRobustEstimator(
148             final EuclideanTransformation3DRobustEstimatorListener listener,
149             final List<Point3D> inputPoints, final List<Point3D> outputPoints) {
150         super(listener, inputPoints, outputPoints);
151         threshold = DEFAULT_THRESHOLD;
152         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
153         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
154     }
155 
156     /**
157      * Constructor.
158      *
159      * @param qualityScores quality scores corresponding to each pair of matched
160      *                      points.
161      * @throws IllegalArgumentException if provided quality scores length is
162      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
163      */
164     public PROSACEuclideanTransformation3DRobustEstimator(final double[] qualityScores) {
165         super();
166         threshold = DEFAULT_THRESHOLD;
167         internalSetQualityScores(qualityScores);
168         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
169         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
170     }
171 
172     /**
173      * Constructor with lists of points to be used to estimate an Euclidean 3D
174      * transformation.
175      * Points in the list located at the same position are considered to be
176      * matched. Hence, both lists must have the same size, and their size must
177      * be greater or equal than MINIMUM_SIZE.
178      *
179      * @param inputPoints   list of input points to be used to estimate an
180      *                      affine 3D transformation.
181      * @param outputPoints  list of output points to be used to estimate an
182      *                      affine 3D transformation.
183      * @param qualityScores quality scores corresponding to each pair of matched
184      *                      points.
185      * @throws IllegalArgumentException if provided lists of points and array
186      *                                  of quality scores don't have the same size or their size is smaller than
187      *                                  MINIMUM_SIZE.
188      */
189     public PROSACEuclideanTransformation3DRobustEstimator(
190             final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores) {
191         super(inputPoints, outputPoints);
192 
193         if (qualityScores.length != inputPoints.size()) {
194             throw new IllegalArgumentException();
195         }
196 
197         threshold = DEFAULT_THRESHOLD;
198         internalSetQualityScores(qualityScores);
199         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
200         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
201     }
202 
203     /**
204      * Constructor.
205      *
206      * @param listener      listener to be notified of events such as when estimation
207      *                      starts, ends or its progress significantly changes.
208      * @param qualityScores quality scores corresponding to each pair of matched
209      *                      points.
210      * @throws IllegalArgumentException if provided quality scores length is
211      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
212      */
213     public PROSACEuclideanTransformation3DRobustEstimator(
214             final EuclideanTransformation3DRobustEstimatorListener listener, final double[] qualityScores) {
215         super(listener);
216         threshold = DEFAULT_THRESHOLD;
217         internalSetQualityScores(qualityScores);
218         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
219         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
220     }
221 
222     /**
223      * Constructor with listener and lists of points to be used to estimate an
224      * Euclidean 3D transformation.
225      * Points in the list located at the same position are considered to be
226      * matched. Hence, both lists must have the same size, and their size must
227      * be greater or equal than MINIMUM_SIZE.
228      *
229      * @param listener      listener to be notified of events such as when estimation
230      *                      stars, ends or its progress significantly changes.
231      * @param inputPoints   list of input points to be used to estimate an
232      *                      affine 3D transformation.
233      * @param outputPoints  list of output points to be used to estimate an
234      *                      affine 3D transformation.
235      * @param qualityScores quality scores corresponding to each pair of matched
236      *                      points.
237      * @throws IllegalArgumentException if provided lists of points don't have
238      *                                  the same size or their size is smaller than MINIMUM_SIZE.
239      */
240     public PROSACEuclideanTransformation3DRobustEstimator(
241             final EuclideanTransformation3DRobustEstimatorListener listener,
242             final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores) {
243         super(listener, inputPoints, outputPoints);
244 
245         if (qualityScores.length != inputPoints.size()) {
246             throw new IllegalArgumentException();
247         }
248 
249         threshold = DEFAULT_THRESHOLD;
250         internalSetQualityScores(qualityScores);
251         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
252         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
253     }
254 
255     /**
256      * Constructor.
257      *
258      * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
259      */
260     public PROSACEuclideanTransformation3DRobustEstimator(
261             final boolean weakMinimumSizeAllowed) {
262         super(weakMinimumSizeAllowed);
263         threshold = DEFAULT_THRESHOLD;
264         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
265         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
266     }
267 
268     /**
269      * Constructor with lists of points to be used to estimate an Euclidean 2D
270      * transformation.
271      * Points in the list located at the same position are considered to be
272      * matched. Hence, both lists must have the same size, and their size must
273      * be greater or equal than MINIMUM_SIZE.
274      *
275      * @param inputPoints            list of input points to be used to estimate an
276      *                               Euclidean 3D transformation.
277      * @param outputPoints           list of output points to be used to estimate an
278      *                               Euclidean 3D transformation.
279      * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
280      * @throws IllegalArgumentException if provided lists of points don't have
281      *                                  the same size or their size is smaller than MINIMUM_SIZE.
282      */
283     public PROSACEuclideanTransformation3DRobustEstimator(
284             final List<Point3D> inputPoints, final List<Point3D> outputPoints, final boolean weakMinimumSizeAllowed) {
285         super(inputPoints, outputPoints, weakMinimumSizeAllowed);
286         threshold = DEFAULT_THRESHOLD;
287         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
288         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
289     }
290 
291     /**
292      * Constructor.
293      *
294      * @param listener               listener to be notified of events such as when estimation
295      *                               starts, ends or its progress significantly changes.
296      * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
297      */
298     public PROSACEuclideanTransformation3DRobustEstimator(
299             final EuclideanTransformation3DRobustEstimatorListener listener, final boolean weakMinimumSizeAllowed) {
300         super(listener, weakMinimumSizeAllowed);
301         threshold = DEFAULT_THRESHOLD;
302         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
303         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
304     }
305 
306     /**
307      * Constructor with listener and lists of points to be used to estimate an
308      * Euclidean 3D transformation.
309      * Points in the list located at the same position are considered to be
310      * matched. Hence, both lists must have the same size, and their size must
311      * be greater or equal than MINIMUM_SIZE.
312      *
313      * @param listener               listener to be notified of events such as when estimation
314      *                               stars, ends or its progress significantly changes.
315      * @param inputPoints            list of input points to be used to estimate an
316      *                               affine 3D transformation.
317      * @param outputPoints           list of output points to be used to estimate an
318      *                               affine 3D transformation.
319      * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
320      * @throws IllegalArgumentException if provided lists of points don't have
321      *                                  the same size or their size is smaller than MINIMUM_SIZE.
322      */
323     public PROSACEuclideanTransformation3DRobustEstimator(
324             final EuclideanTransformation3DRobustEstimatorListener listener,
325             final List<Point3D> inputPoints, final List<Point3D> outputPoints, final boolean weakMinimumSizeAllowed) {
326         super(listener, inputPoints, outputPoints, weakMinimumSizeAllowed);
327         threshold = DEFAULT_THRESHOLD;
328         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
329         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
330     }
331 
332     /**
333      * Constructor.
334      *
335      * @param qualityScores          quality scores corresponding to each pair of matched
336      *                               points.
337      * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
338      * @throws IllegalArgumentException if provided quality scores length is
339      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
340      */
341     public PROSACEuclideanTransformation3DRobustEstimator(
342             final double[] qualityScores, final boolean weakMinimumSizeAllowed) {
343         super(weakMinimumSizeAllowed);
344         threshold = DEFAULT_THRESHOLD;
345         internalSetQualityScores(qualityScores);
346         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
347         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
348     }
349 
350     /**
351      * Constructor with lists of points to be used to estimate an Euclidean 3D
352      * transformation.
353      * Points in the list located at the same position are considered to be
354      * matched. Hence, both lists must have the same size, and their size must
355      * be greater or equal than MINIMUM_SIZE.
356      *
357      * @param inputPoints            list of input points to be used to estimate an
358      *                               affine 3D transformation.
359      * @param outputPoints           list of output points to be used to estimate an
360      *                               affine 3D transformation.
361      * @param qualityScores          quality scores corresponding to each pair of matched
362      *                               points.
363      * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
364      * @throws IllegalArgumentException if provided lists of points and array
365      *                                  of quality scores don't have the same size or their size is smaller than
366      *                                  MINIMUM_SIZE.
367      */
368     public PROSACEuclideanTransformation3DRobustEstimator(
369             final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores,
370             final boolean weakMinimumSizeAllowed) {
371         super(inputPoints, outputPoints, weakMinimumSizeAllowed);
372 
373         if (qualityScores.length != inputPoints.size()) {
374             throw new IllegalArgumentException();
375         }
376 
377         threshold = DEFAULT_THRESHOLD;
378         internalSetQualityScores(qualityScores);
379         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
380         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
381     }
382 
383     /**
384      * Constructor.
385      *
386      * @param listener               listener to be notified of events such as when estimation
387      *                               starts, ends or its progress significantly changes.
388      * @param qualityScores          quality scores corresponding to each pair of matched
389      *                               points.
390      * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
391      * @throws IllegalArgumentException if provided quality scores length is
392      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
393      */
394     public PROSACEuclideanTransformation3DRobustEstimator(
395             final EuclideanTransformation3DRobustEstimatorListener listener, final double[] qualityScores,
396             final boolean weakMinimumSizeAllowed) {
397         super(listener, weakMinimumSizeAllowed);
398         threshold = DEFAULT_THRESHOLD;
399         internalSetQualityScores(qualityScores);
400         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
401         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
402     }
403 
404     /**
405      * Constructor with listener and lists of points to be used to estimate an
406      * Euclidean 3D transformation.
407      * Points in the list located at the same position are considered to be
408      * matched. Hence, both lists must have the same size, and their size must
409      * be greater or equal than MINIMUM_SIZE.
410      *
411      * @param listener               listener to be notified of events such as when estimation
412      *                               stars, ends or its progress significantly changes.
413      * @param inputPoints            list of input points to be used to estimate an
414      *                               affine 3D transformation.
415      * @param outputPoints           list of output points to be used to estimate an
416      *                               affine 3D transformation.
417      * @param qualityScores          quality scores corresponding to each pair of matched
418      *                               points.
419      * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
420      * @throws IllegalArgumentException if provided lists of points don't have
421      *                                  the same size or their size is smaller than MINIMUM_SIZE.
422      */
423     public PROSACEuclideanTransformation3DRobustEstimator(
424             final EuclideanTransformation3DRobustEstimatorListener listener, final List<Point3D> inputPoints,
425             final List<Point3D> outputPoints, final double[] qualityScores, final boolean weakMinimumSizeAllowed) {
426         super(listener, inputPoints, outputPoints, weakMinimumSizeAllowed);
427 
428         if (qualityScores.length != inputPoints.size()) {
429             throw new IllegalArgumentException();
430         }
431 
432         threshold = DEFAULT_THRESHOLD;
433         internalSetQualityScores(qualityScores);
434         computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
435         computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
436     }
437 
438     /**
439      * Returns threshold to determine whether points are inliers or not when
440      * testing possible estimation solutions.
441      * The threshold refers to the amount of error (i.e. Euclidean distance) a
442      * possible solution has on a matched pair of points.
443      *
444      * @return threshold to determine whether points are inliers or not when
445      * testing possible estimation solutions.
446      */
447     public double getThreshold() {
448         return threshold;
449     }
450 
451     /**
452      * Sets threshold to determine whether points are inliers or not when
453      * testing possible estimation solutions.
454      * The threshold refers to the amount of error (i.e. Euclidean distance) a
455      * possible solution has on a matched pair of points.
456      *
457      * @param threshold threshold to determine whether points are inliers or not
458      *                  when testing possible estimation solutions.
459      * @throws IllegalArgumentException if provided values is equal or less than
460      *                                  zero.
461      * @throws LockedException          if robust estimator is locked because an
462      *                                  estimation is already in progress.
463      */
464     public void setThreshold(final double threshold) throws LockedException {
465         if (isLocked()) {
466             throw new LockedException();
467         }
468         if (threshold <= MIN_THRESHOLD) {
469             throw new IllegalArgumentException();
470         }
471         this.threshold = threshold;
472     }
473 
474     /**
475      * Returns quality scores corresponding to each pair of matched points.
476      * The larger the score value the better the quality of the matching.
477      *
478      * @return quality scores corresponding to each pair of matched points.
479      */
480     @Override
481     public double[] getQualityScores() {
482         return qualityScores;
483     }
484 
485     /**
486      * Sets quality scores corresponding to each pair of matched points.
487      * The larger the score value the better the quality of the matching.
488      *
489      * @param qualityScores quality scores corresponding to each pair of matched
490      *                      points.
491      * @throws LockedException          if robust estimator is locked because an
492      *                                  estimation is already in progress.
493      * @throws IllegalArgumentException if provided quality scores length is
494      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
495      */
496     @Override
497     public void setQualityScores(final double[] qualityScores) throws LockedException {
498         if (isLocked()) {
499             throw new LockedException();
500         }
501         internalSetQualityScores(qualityScores);
502     }
503 
504     /**
505      * Indicates if estimator is ready to start the Euclidean 3D transformation
506      * estimation.
507      * This is true when input data (i.e. lists of matched points and quality
508      * scores) are provided and a minimum of MINIMUM_SIZE points are available.
509      *
510      * @return true if estimator is ready, false otherwise.
511      */
512     @Override
513     public boolean isReady() {
514         return super.isReady() && qualityScores != null && qualityScores.length == inputPoints.size();
515     }
516 
517     /**
518      * Indicates whether inliers must be computed and kept.
519      *
520      * @return true if inliers must be computed and kept, false if inliers
521      * only need to be computed but not kept.
522      */
523     public boolean isComputeAndKeepInliersEnabled() {
524         return computeAndKeepInliers;
525     }
526 
527     /**
528      * Specifies whether inliers must be computed and kept.
529      *
530      * @param computeAndKeepInliers true if inliers must be computed and kept,
531      *                              false if inliers only need to be computed but not kept.
532      * @throws LockedException if estimator is locked.
533      */
534     public void setComputeAndKeepInliersEnabled(final boolean computeAndKeepInliers) throws LockedException {
535         if (isLocked()) {
536             throw new LockedException();
537         }
538         this.computeAndKeepInliers = computeAndKeepInliers;
539     }
540 
541     /**
542      * Indicates whether residuals must be computed and kept.
543      *
544      * @return true if residuals must be computed and kept, false if residuals
545      * only need to be computed but not kept.
546      */
547     public boolean isComputeAndKeepResidualsEnabled() {
548         return computeAndKeepResiduals;
549     }
550 
551     /**
552      * Specifies whether residuals must be computed and kept.
553      *
554      * @param computeAndKeepResiduals true if residuals must be computed and
555      *                                kept, false if residuals only need to be computed but not kept.
556      * @throws LockedException if estimator is locked.
557      */
558     public void setComputeAndKeepResidualsEnabled(final boolean computeAndKeepResiduals) throws LockedException {
559         if (isLocked()) {
560             throw new LockedException();
561         }
562         this.computeAndKeepResiduals = computeAndKeepResiduals;
563     }
564 
565     /**
566      * Estimates an Euclidean 3D transformation using a robust estimator and
567      * the best set of matched 3D point correspondences found using the robust
568      * estimator.
569      *
570      * @return an affine 3D transformation.
571      * @throws LockedException          if robust estimator is locked because an
572      *                                  estimation is already in progress.
573      * @throws NotReadyException        if provided input data is not enough to start
574      *                                  the estimation.
575      * @throws RobustEstimatorException if estimation fails for any reason
576      *                                  (i.e. numerical instability, no solution available, etc).
577      */
578     @Override
579     public EuclideanTransformation3D estimate() throws LockedException, NotReadyException, RobustEstimatorException {
580         if (isLocked()) {
581             throw new LockedException();
582         }
583         if (!isReady()) {
584             throw new NotReadyException();
585         }
586 
587         final var innerEstimator = new PROSACRobustEstimator<>(
588                 new PROSACRobustEstimatorListener<EuclideanTransformation3D>() {
589 
590                     // point to be reused when computing residuals
591                     private final Point3D testPoint = Point3D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
592 
593                     private final EuclideanTransformation3DEstimator nonRobustEstimator =
594                             new EuclideanTransformation3DEstimator(isWeakMinimumSizeAllowed());
595 
596                     private final List<Point3D> subsetInputPoints = new ArrayList<>();
597                     private final List<Point3D> subsetOutputPoints = new ArrayList<>();
598 
599                     @Override
600                     public double getThreshold() {
601                         return threshold;
602                     }
603 
604                     @Override
605                     public int getTotalSamples() {
606                         return inputPoints.size();
607                     }
608 
609                     @Override
610                     public int getSubsetSize() {
611                         return nonRobustEstimator.getMinimumPoints();
612                     }
613 
614                     @Override
615                     public void estimatePreliminarSolutions(
616                             final int[] samplesIndices, final List<EuclideanTransformation3D> 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 EuclideanTransformation3D 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 PROSACEuclideanTransformation3DRobustEstimator.this.isReady();
646                     }
647 
648                     @Override
649                     public void onEstimateStart(final RobustEstimator<EuclideanTransformation3D> estimator) {
650                         if (listener != null) {
651                             listener.onEstimateStart(PROSACEuclideanTransformation3DRobustEstimator.this);
652                         }
653                     }
654 
655                     @Override
656                     public void onEstimateEnd(final RobustEstimator<EuclideanTransformation3D> estimator) {
657                         if (listener != null) {
658                             listener.onEstimateEnd(PROSACEuclideanTransformation3DRobustEstimator.this);
659                         }
660                     }
661 
662                     @Override
663                     public void onEstimateNextIteration(
664                             final RobustEstimator<EuclideanTransformation3D> estimator, final int iteration) {
665                         if (listener != null) {
666                             listener.onEstimateNextIteration(
667                                     PROSACEuclideanTransformation3DRobustEstimator.this, iteration);
668                         }
669                     }
670 
671                     @Override
672                     public void onEstimateProgressChange(
673                             final RobustEstimator<EuclideanTransformation3D> estimator, final float progress) {
674                         if (listener != null) {
675                             listener.onEstimateProgressChange(
676                                     PROSACEuclideanTransformation3DRobustEstimator.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 }