View Javadoc
1   /*
2    * Copyright (C) 2015 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.AffineTransformation3D;
19  import com.irurueta.geometry.Point3D;
20  import com.irurueta.geometry.refiners.PointCorrespondenceAffineTransformation3DRefiner;
21  import com.irurueta.numerical.robust.RobustEstimatorMethod;
22  
23  import java.util.List;
24  
25  /**
26   * This is an abstract class for algorithms to robustly find the best affine
27   * 3D transformation for collections of matching 3D points.
28   * Implementations of this class should be able to detect and discard outliers
29   * in order to find the best solution.
30   */
31  public abstract class PointCorrespondenceAffineTransformation3DRobustEstimator
32          extends AffineTransformation3DRobustEstimator {
33  
34      /**
35       * Default robust estimator method when none is provided.
36       */
37      public static final RobustEstimatorMethod DEFAULT_ROBUST_METHOD = RobustEstimatorMethod.PROMEDS;
38  
39      /**
40       * List of points to be used to estimate an affine 3D transformation.
41       * Each point in the list of input points must be matched with the
42       * corresponding point in the list of output points located at the same
43       * position. Hence, both input points and output points must have the same
44       * size, and their size must be greater or equal than MINIMUM_SIZE.
45       */
46      protected List<Point3D> inputPoints;
47  
48      /**
49       * List of points to be used to estimate an affine 3D transformation.
50       * Each point in the list of output points must be matched with the
51       * corresponding point in the list of input points located at the same
52       * position. Hence, both input points and output points must have the same
53       * size, and their size must be greater or equal than MINIMUM_SIZE.
54       */
55      protected List<Point3D> outputPoints;
56  
57      /**
58       * Constructor.
59       */
60      protected PointCorrespondenceAffineTransformation3DRobustEstimator() {
61          super();
62      }
63  
64      /**
65       * Constructor with lists of points to be used to estimate an affine 3D
66       * transformation.
67       * Points in the list located at the same position are considered to be
68       * matched. Hence, both lists must have the same size, and their size must
69       * be greater or equal than MINIMUM_SIZE.
70       *
71       * @param inputPoints  list of input points to be used to estimate an
72       *                     affine 3D transformation.
73       * @param outputPoints list of output points to be used to estimate an
74       *                     affine 3D transformation.
75       * @throws IllegalArgumentException if provided lists of points don't have
76       *                                  the same size or their size is smaller than MINIMUM_SIZE.
77       */
78      protected PointCorrespondenceAffineTransformation3DRobustEstimator(
79              final List<Point3D> inputPoints, final List<Point3D> outputPoints) {
80          super();
81          internalSetPoints(inputPoints, outputPoints);
82      }
83  
84      /**
85       * Constructor.
86       *
87       * @param listener listener to be notified of events such as when estimation
88       *                 stars, ends or its progress significantly changes.
89       */
90      protected PointCorrespondenceAffineTransformation3DRobustEstimator(
91              final AffineTransformation3DRobustEstimatorListener listener) {
92          super(listener);
93      }
94  
95      /**
96       * Constructor with listener and lists of points to be used to estimate an
97       * affine 3D 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 listener     listener to be notified of events such as when estimation
103      *                     starts, ends or its progress significantly changes.
104      * @param inputPoints  list of input points to be used to estimate an
105      *                     affine 3D transformation.
106      * @param outputPoints list of output points to be used to estimate an
107      *                     affine 3D transformation.
108      * @throws IllegalArgumentException if provided lists of points don't have
109      *                                  the same size or their size is smaller than MINIMUM_SIZE.
110      */
111     protected PointCorrespondenceAffineTransformation3DRobustEstimator(
112             final AffineTransformation3DRobustEstimatorListener listener,
113             final List<Point3D> inputPoints, final List<Point3D> outputPoints) {
114         super(listener);
115         internalSetPoints(inputPoints, outputPoints);
116     }
117 
118     /**
119      * Returns list of input points to be used to estimate an affine 3D
120      * transformation.
121      * Each point in the list of input points must be matched with the
122      * corresponding point in the list of output points located at the same
123      * position. Hence, both input points and output points must have the same
124      * size, and their size must be greater or equal than MINIMUM_SIZE.
125      *
126      * @return list of input points to be used to estimate an affine 3D
127      * transformation.
128      */
129     public List<Point3D> getInputPoints() {
130         return inputPoints;
131     }
132 
133     /**
134      * Returns list of output points to be used to estimate an affine 3D
135      * transformation.
136      * Each point in the list of output points must be matched with the
137      * corresponding point in the list of input points located at the same
138      * position. Hence, both input points and output points must have the same
139      * size, and their size must be greater or equal than MINIMUM_SIZE.
140      *
141      * @return list of output points to be used to estimate an affine 2D
142      * transformation.
143      */
144     public List<Point3D> getOutputPoints() {
145         return outputPoints;
146     }
147 
148     /**
149      * Sets lists of points to be used to estimate an affine 3D transformation.
150      * Points in the list located at the same position are considered to be
151      * matched. Hence, both lists must have the same size, and their size must
152      * be greater or equal than MINIMUM_SIZE.
153      *
154      * @param inputPoints  list of input points to be used to estimate an
155      *                     affine 3D transformation.
156      * @param outputPoints list of output points to be used to estimate an
157      *                     affine 3D transformation.
158      * @throws IllegalArgumentException if provided lists of points don't have
159      *                                  the same size or their size is smaller than MINIMUM_SIZE.
160      * @throws LockedException          if estimator is locked because a computation is
161      *                                  already in progress.
162      */
163     public final void setPoints(final List<Point3D> inputPoints, final List<Point3D> outputPoints)
164             throws LockedException {
165         if (isLocked()) {
166             throw new LockedException();
167         }
168         internalSetPoints(inputPoints, outputPoints);
169     }
170 
171     /**
172      * Indicates if estimator is ready to start the affine 3D transformation
173      * estimation.
174      * This is true when input data (i.e. lists of matched points) are provided
175      * and a minimum of MINIMUM_SIZE points are available.
176      *
177      * @return true if estimator is ready, false otherwise.
178      */
179     public boolean isReady() {
180         return inputPoints != null && outputPoints != null && inputPoints.size() == outputPoints.size()
181                 && inputPoints.size() >= MINIMUM_SIZE;
182     }
183 
184     /**
185      * Returns quality scores corresponding to each pair of matched points.
186      * The larger the score value the better the quality of the matching.
187      * This implementation always returns null.
188      * Subclasses using quality scores must implement proper behaviour.
189      *
190      * @return quality scores corresponding to each pair of matched points.
191      */
192     public double[] getQualityScores() {
193         return null;
194     }
195 
196     /**
197      * Sets quality scores corresponding to each pair of matched points.
198      * The larger the score value the better the quality of the matching.
199      * This implementation makes no action.
200      * Subclasses using quality scores must implement proper behaviour.
201      *
202      * @param qualityScores quality scores corresponding to each pair of matched
203      *                      points.
204      * @throws LockedException          if robust estimator is locked because an
205      *                                  estimation is already in progress.
206      * @throws IllegalArgumentException if provided quality scores length is
207      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
208      */
209     public void setQualityScores(final double[] qualityScores) throws LockedException {
210     }
211 
212     /**
213      * Creates an affine 3D transformation estimator based on 2D point
214      * correspondences and using provided robust estimator method.
215      *
216      * @param method method of a robust estimator algorithm to estimate
217      *               the best affine 3D transformation.
218      * @return an instance of affine 3D transformation estimator.
219      */
220     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(final RobustEstimatorMethod method) {
221         return switch (method) {
222             case LMEDS -> new LMedSPointCorrespondenceAffineTransformation3DRobustEstimator();
223             case MSAC -> new MSACPointCorrespondenceAffineTransformation3DRobustEstimator();
224             case PROSAC -> new PROSACPointCorrespondenceAffineTransformation3DRobustEstimator();
225             case PROMEDS -> new PROMedSPointCorrespondenceAffineTransformation3DRobustEstimator();
226             default -> new RANSACPointCorrespondenceAffineTransformation3DRobustEstimator();
227         };
228     }
229 
230     /**
231      * Creates an affine 3D transformation estimator based on 3D point
232      * correspondences and using provided robust estimator method.
233      *
234      * @param inputPoints  list of input points to be used to estimate an
235      *                     affine 3D transformation.
236      * @param outputPoints list of output points to be used to estimate an
237      *                     affine 3D transformation.
238      * @param method       method of a robust estimator algorithm to estimate
239      *                     the best affine 3D transformation.
240      * @return an instance of affine 3D transformation estimator.
241      * @throws IllegalArgumentException if provided lists of points don't have
242      *                                  the same size or their size is smaller than MINIMUM_SIZE.
243      */
244     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
245             final List<Point3D> inputPoints, final List<Point3D> outputPoints, final RobustEstimatorMethod method) {
246         return switch (method) {
247             case LMEDS -> new LMedSPointCorrespondenceAffineTransformation3DRobustEstimator(inputPoints, outputPoints);
248             case MSAC -> new MSACPointCorrespondenceAffineTransformation3DRobustEstimator(inputPoints, outputPoints);
249             case PROSAC -> new PROSACPointCorrespondenceAffineTransformation3DRobustEstimator(
250                     inputPoints, outputPoints);
251             case PROMEDS -> new PROMedSPointCorrespondenceAffineTransformation3DRobustEstimator(
252                     inputPoints, outputPoints);
253             default -> new RANSACPointCorrespondenceAffineTransformation3DRobustEstimator(inputPoints, outputPoints);
254         };
255     }
256 
257     /**
258      * Creates an affine 3D transformation estimator based on 3D point
259      * correspondences and using provided robust estimator method.
260      *
261      * @param listener listener to be notified of events such as when estimation
262      *                 starts, ends or its progress significantly changes.
263      * @param method   method of a robust estimator algorithm to estimate
264      *                 the best affine 3D transformation.
265      * @return an instance of affine 3D transformation estimator.
266      */
267     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
268             final AffineTransformation3DRobustEstimatorListener listener, final RobustEstimatorMethod method) {
269         return switch (method) {
270             case LMEDS -> new LMedSPointCorrespondenceAffineTransformation3DRobustEstimator(listener);
271             case MSAC -> new MSACPointCorrespondenceAffineTransformation3DRobustEstimator(listener);
272             case PROSAC -> new PROSACPointCorrespondenceAffineTransformation3DRobustEstimator(listener);
273             case PROMEDS -> new PROMedSPointCorrespondenceAffineTransformation3DRobustEstimator(listener);
274             default -> new RANSACPointCorrespondenceAffineTransformation3DRobustEstimator(listener);
275         };
276     }
277 
278     /**
279      * Creates an affine 3D transformation estimator based on 3D point
280      * correspondences and using provided robust estimator method.
281      *
282      * @param listener     listener to be notified of events such as when estimation
283      *                     starts, ends or its progress significantly changes.
284      * @param inputPoints  list of input points to be used to estimate an
285      *                     affine 3D transformation.
286      * @param outputPoints list of output points to be used to estimate an
287      *                     affine 3D transformation.
288      * @param method       method of a robust estimator algorithm to estimate
289      *                     the best affine 3D transformation.
290      * @return an instance of affine 3D transformation estimator.
291      * @throws IllegalArgumentException if provided lists of points don't have
292      *                                  the same size or their size is smaller than MINIMUM_SIZE.
293      */
294     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
295             final AffineTransformation3DRobustEstimatorListener listener, final List<Point3D> inputPoints,
296             final List<Point3D> outputPoints, final RobustEstimatorMethod method) {
297         return switch (method) {
298             case LMEDS -> new LMedSPointCorrespondenceAffineTransformation3DRobustEstimator(
299                     listener, inputPoints, outputPoints);
300             case MSAC -> new MSACPointCorrespondenceAffineTransformation3DRobustEstimator(
301                     listener, inputPoints, outputPoints);
302             case PROSAC -> new PROSACPointCorrespondenceAffineTransformation3DRobustEstimator(
303                     listener, inputPoints, outputPoints);
304             case PROMEDS -> new PROMedSPointCorrespondenceAffineTransformation3DRobustEstimator(
305                     listener, inputPoints, outputPoints);
306             default -> new RANSACPointCorrespondenceAffineTransformation3DRobustEstimator(
307                     listener, inputPoints, outputPoints);
308         };
309     }
310 
311     /**
312      * Creates an affine 3D transformation estimator based on 3D point
313      * correspondences and using provided robust estimator method.
314      *
315      * @param qualityScores quality scores corresponding to each pair of matched
316      *                      points.
317      * @param method        method of a robust estimator algorithm to estimate
318      *                      the best affine 3D transformation.
319      * @return an instance of affine 3D transformation estimator.
320      */
321     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
322             final double[] qualityScores, final RobustEstimatorMethod method) {
323         return switch (method) {
324             case LMEDS -> new LMedSPointCorrespondenceAffineTransformation3DRobustEstimator();
325             case MSAC -> new MSACPointCorrespondenceAffineTransformation3DRobustEstimator();
326             case PROSAC -> new PROSACPointCorrespondenceAffineTransformation3DRobustEstimator(qualityScores);
327             case PROMEDS -> new PROMedSPointCorrespondenceAffineTransformation3DRobustEstimator(qualityScores);
328             default -> new RANSACPointCorrespondenceAffineTransformation3DRobustEstimator();
329         };
330     }
331 
332     /**
333      * Creates an affine 3D transformation estimator based on 3D point
334      * correspondences and using provided robust estimator method.
335      *
336      * @param inputPoints   list of input points to be used to estimate an
337      *                      affine 3D transformation.
338      * @param outputPoints  list of output points to be used to estimate an
339      *                      affine 3D transformation.
340      * @param qualityScores quality scores corresponding to each pair of matched
341      *                      points.
342      * @param method        method of a robust estimator algorithm to estimate
343      *                      the best affine 3D transformation.
344      * @return an instance of affine 3D transformation estimator.
345      * @throws IllegalArgumentException if provided lists of points don't have
346      *                                  the same size or their size is smaller than MINIMUM_SIZE.
347      */
348     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
349             final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores,
350             final RobustEstimatorMethod method) {
351         return switch (method) {
352             case LMEDS -> new LMedSPointCorrespondenceAffineTransformation3DRobustEstimator(inputPoints, outputPoints);
353             case MSAC -> new MSACPointCorrespondenceAffineTransformation3DRobustEstimator(inputPoints, outputPoints);
354             case PROSAC -> new PROSACPointCorrespondenceAffineTransformation3DRobustEstimator(
355                     inputPoints, outputPoints, qualityScores);
356             case PROMEDS -> new PROMedSPointCorrespondenceAffineTransformation3DRobustEstimator(
357                     inputPoints, outputPoints, qualityScores);
358             default -> new RANSACPointCorrespondenceAffineTransformation3DRobustEstimator(inputPoints, outputPoints);
359         };
360     }
361 
362     /**
363      * Creates an affine 3D transformation estimator based on 3D point
364      * correspondences and using provided robust estimator method.
365      *
366      * @param listener      listener to be notified of events such as when estimation
367      *                      starts, ends or its progress significantly changes.
368      * @param qualityScores quality scores corresponding to each pair of matched
369      *                      points.
370      * @param method        method of a robust estimator algorithm to estimate
371      *                      the best affine 3D transformation.
372      * @return an instance of affine 3D transformation estimator.
373      */
374     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
375             final AffineTransformation3DRobustEstimatorListener listener, final double[] qualityScores,
376             final RobustEstimatorMethod method) {
377         return switch (method) {
378             case LMEDS -> new LMedSPointCorrespondenceAffineTransformation3DRobustEstimator(listener);
379             case MSAC -> new MSACPointCorrespondenceAffineTransformation3DRobustEstimator(listener);
380             case PROSAC -> new PROSACPointCorrespondenceAffineTransformation3DRobustEstimator(listener, qualityScores);
381             case PROMEDS -> new PROMedSPointCorrespondenceAffineTransformation3DRobustEstimator(
382                     listener, qualityScores);
383             default -> new RANSACPointCorrespondenceAffineTransformation3DRobustEstimator(listener);
384         };
385     }
386 
387     /**
388      * Creates an affine 3D transformation estimator based on 3D point
389      * correspondences and using provided robust estimator method.
390      *
391      * @param listener      listener to be notified of events such as when estimation
392      *                      starts, ends or its progress significantly changes.
393      * @param inputPoints   list of input points to be used to estimate an
394      *                      affine 3D transformation.
395      * @param outputPoints  list of output points to be used to estimate an
396      *                      affine 3D transformation.
397      * @param qualityScores quality scores corresponding to each pair of matched
398      *                      points.
399      * @param method        method of a robust estimator algorithm to estimate
400      *                      the best affine 3D transformation.
401      * @return an instance of affine 3D transformation estimator.
402      * @throws IllegalArgumentException if provided lists of points don't have
403      *                                  the same size or their size is smaller than MINIMUM_SIZE.
404      */
405     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
406             final AffineTransformation3DRobustEstimatorListener listener,
407             final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores,
408             final RobustEstimatorMethod method) {
409         return switch (method) {
410             case LMEDS -> new LMedSPointCorrespondenceAffineTransformation3DRobustEstimator(
411                     listener, inputPoints, outputPoints);
412             case MSAC -> new MSACPointCorrespondenceAffineTransformation3DRobustEstimator(
413                     listener, inputPoints, outputPoints);
414             case PROSAC -> new PROSACPointCorrespondenceAffineTransformation3DRobustEstimator(
415                     listener, inputPoints, outputPoints, qualityScores);
416             case PROMEDS -> new PROMedSPointCorrespondenceAffineTransformation3DRobustEstimator(
417                     listener, inputPoints, outputPoints, qualityScores);
418             default -> new RANSACPointCorrespondenceAffineTransformation3DRobustEstimator(
419                     listener, inputPoints, outputPoints);
420         };
421     }
422 
423     /**
424      * Creates an affine 3D transformation estimator based on 3D point
425      * correspondences and using default robust estimator method.
426      *
427      * @return an instance of affine 3D transformation estimator.
428      */
429     public static PointCorrespondenceAffineTransformation3DRobustEstimator create() {
430         return create(DEFAULT_ROBUST_METHOD);
431     }
432 
433     /**
434      * Creates an affine 3D transformation estimator based on 3D point
435      * correspondences and using default robust estimator method.
436      *
437      * @param inputPoints  list of input points to be used to estimate an
438      *                     affine 3D transformation.
439      * @param outputPoints list of output points to be used to estimate an
440      *                     affine 3D transformation.
441      * @return an instance of affine 3D transformation estimator.
442      * @throws IllegalArgumentException if provided lists of points don't have
443      *                                  the same size or their size is smaller than MINIMUM_SIZE.
444      */
445     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
446             final List<Point3D> inputPoints, final List<Point3D> outputPoints) {
447         return create(inputPoints, outputPoints, DEFAULT_ROBUST_METHOD);
448     }
449 
450     /**
451      * Creates an affine 3D transformation estimator based on 3D point
452      * correspondences and using default robust estimator method.
453      *
454      * @param listener listener to be notified of events such as when estimation
455      *                 starts, ends or its progress significantly changes.
456      * @return an instance of affine 3D transformation estimator.
457      */
458     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
459             final AffineTransformation3DRobustEstimatorListener listener) {
460         return create(listener, DEFAULT_ROBUST_METHOD);
461     }
462 
463     /**
464      * Creates an affine 3D transformation estimator based on 3D point
465      * correspondences and using default robust estimator method.
466      *
467      * @param listener     listener to be notified of events such as when estimation
468      *                     starts, ends or its progress significantly changes.
469      * @param inputPoints  list of input points to be used to estimate an
470      *                     affine 3D transformation.
471      * @param outputPoints list of output points to be used to estimate an
472      *                     affine 3D transformation.
473      * @return an instance of affine 3D transformation estimator.
474      * @throws IllegalArgumentException if provided lists of points don't have
475      *                                  the same size or their size is smaller than MINIMUM_SIZE.
476      */
477     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
478             final AffineTransformation3DRobustEstimatorListener listener,
479             final List<Point3D> inputPoints, final List<Point3D> outputPoints) {
480         return create(listener, inputPoints, outputPoints, DEFAULT_ROBUST_METHOD);
481     }
482 
483     /**
484      * Creates an affine 3D transformation estimator based on 3D point
485      * correspondences and using default robust estimator method.
486      *
487      * @param qualityScores quality scores corresponding to each pair of matched
488      *                      points.
489      * @return an instance of affine 3D transformation estimator.
490      */
491     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(final double[] qualityScores) {
492         return create(qualityScores, DEFAULT_ROBUST_METHOD);
493     }
494 
495     /**
496      * Creates an affine 3D transformation estimator based on 3D point
497      * correspondences and using default robust estimator method.
498      *
499      * @param inputPoints   list of input points to be used to estimate an
500      *                      affine 3D transformation.
501      * @param outputPoints  list of output points to be used to estimate an
502      *                      affine 3D transformation.
503      * @param qualityScores quality scores corresponding to each pair of matched
504      *                      points.
505      * @return an instance of affine 3D transformation estimator.
506      * @throws IllegalArgumentException if provided lists of points don't have
507      *                                  the same size or their size is smaller than MINIMUM_SIZE.
508      */
509     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
510             final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores) {
511         return create(inputPoints, outputPoints, qualityScores, DEFAULT_ROBUST_METHOD);
512     }
513 
514     /**
515      * Creates an affine 3D transformation estimator based on 3D point
516      * correspondences and using default robust estimator method.
517      *
518      * @param listener      listener to be notified of events such as when estimation
519      *                      starts, ends or its progress significantly changes.
520      * @param qualityScores quality scores corresponding to each pair of matched
521      *                      points.
522      * @return an instance of affine 3D transformation estimator.
523      */
524     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
525             final AffineTransformation3DRobustEstimatorListener listener, final double[] qualityScores) {
526         return create(listener, qualityScores, DEFAULT_ROBUST_METHOD);
527     }
528 
529     /**
530      * Creates an affine 3D transformation estimator based on 3D point
531      * correspondences and using default robust estimator method.
532      *
533      * @param listener      listener to be notified of events such as when estimation
534      *                      starts, ends or its progress significantly changes.
535      * @param inputPoints   list of input points to be used to estimate an
536      *                      affine 3D transformation.
537      * @param outputPoints  list of output points to be used to estimate an
538      *                      affine 3D transformation.
539      * @param qualityScores quality scores corresponding to each pair of matched
540      *                      points.
541      * @return an instance of affine 3D transformation estimator.
542      * @throws IllegalArgumentException if provided lists of points don't have
543      *                                  the same size or their size is smaller than MINIMUM_SIZE.
544      */
545     public static PointCorrespondenceAffineTransformation3DRobustEstimator create(
546             final AffineTransformation3DRobustEstimatorListener listener, final List<Point3D> inputPoints,
547             List<Point3D> outputPoints, final double[] qualityScores) {
548         return create(listener, inputPoints, outputPoints, qualityScores, DEFAULT_ROBUST_METHOD);
549     }
550 
551     /**
552      * Attempts to refine provided solution if refinement is requested.
553      * This method returns a refined solution of the same provided solution
554      * if refinement is not requested or has failed.
555      * If refinement is enabled, and it is requested to keep covariance, this
556      * method will also keep covariance of refined transformation.
557      *
558      * @param transformation transformation estimated by a robust estimator
559      *                       without refinement.
560      * @return solution after refinement (if requested) or the provided
561      * non-refined solution if not requested or refinement failed.
562      */
563     @SuppressWarnings("DuplicatedCode")
564     protected AffineTransformation3D attemptRefine(final AffineTransformation3D transformation) {
565         if (refineResult) {
566             final var refiner = new PointCorrespondenceAffineTransformation3DRefiner(transformation, keepCovariance,
567                     getInliersData(), inputPoints, outputPoints, getRefinementStandardDeviation());
568 
569             try {
570                 final var result = new AffineTransformation3D();
571                 final var improved = refiner.refine(result);
572 
573                 if (keepCovariance) {
574                     // keep covariance
575                     covariance = refiner.getCovariance();
576                 }
577 
578                 return improved ? result : transformation;
579             } catch (final Exception e) {
580                 // refinement failed, so we return input value
581                 return transformation;
582             }
583         } else {
584             return transformation;
585         }
586     }
587 
588     /**
589      * Internal method to set lists of points to be used to estimate an affine
590      * 3D transformation.
591      * This method does not check whether estimator is locked or not.
592      *
593      * @param inputPoints  list of input points to be used to estimate an
594      *                     affine 3D transformation.
595      * @param outputPoints list of output points to be used to estimate an
596      *                     affine 3D transformation.
597      * @throws IllegalArgumentException if provided lists of points don't have
598      *                                  the same size or their size is smaller than MINIMUM_SIZE.
599      */
600     private void internalSetPoints(final List<Point3D> inputPoints, final List<Point3D> outputPoints) {
601         if (inputPoints.size() < MINIMUM_SIZE) {
602             throw new IllegalArgumentException();
603         }
604         if (inputPoints.size() != outputPoints.size()) {
605             throw new IllegalArgumentException();
606         }
607         this.inputPoints = inputPoints;
608         this.outputPoints = outputPoints;
609     }
610 }