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