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.ar.calibration.estimators;
17  
18  import com.irurueta.ar.calibration.RadialDistortion;
19  import com.irurueta.geometry.InhomogeneousPoint2D;
20  import com.irurueta.geometry.PinholeCameraIntrinsicParameters;
21  import com.irurueta.geometry.Point2D;
22  import com.irurueta.geometry.estimators.LockedException;
23  import com.irurueta.geometry.estimators.NotReadyException;
24  import com.irurueta.numerical.robust.RobustEstimatorException;
25  import com.irurueta.numerical.robust.RobustEstimatorMethod;
26  
27  import java.util.List;
28  
29  /**
30   * This is an abstract class for algorithms to robustly find the best
31   * RadialDistortion for provided collections of matched distorted and
32   * undistorted 2D points.
33   * Implementations of this class should be able to detect and discard outliers
34   * in order to find the best solution
35   */
36  public abstract class RadialDistortionRobustEstimator {
37      /**
38       * Default robust estimator method when none is provided.
39       */
40      public static final RobustEstimatorMethod DEFAULT_ROBUST_METHOD = RobustEstimatorMethod.PROMEDS;
41  
42      /**
43       * Default amount of progress variation before notifying a change in
44       * estimation progress. By default, this is set to 5%.
45       */
46      public static final float DEFAULT_PROGRESS_DELTA = 0.05f;
47  
48      /**
49       * Minimum allowed value for progress delta.
50       */
51      public static final float MIN_PROGRESS_DELTA = 0.0f;
52  
53      /**
54       * Maximum allowed value for progress delta.
55       */
56      public static final float MAX_PROGRESS_DELTA = 1.0f;
57  
58      /**
59       * Constant defining default confidence of the estimated result, which is
60       * 99%. This means that with a probability of 99% estimation will be
61       * accurate because chosen sub-samples will be inliers.
62       */
63      public static final double DEFAULT_CONFIDENCE = 0.99;
64  
65      /**
66       * Default maximum allowed number of iterations.
67       */
68      public static final int DEFAULT_MAX_ITERATIONS = 5000;
69  
70      /**
71       * Minimum allowed confidence value.
72       */
73      public static final double MIN_CONFIDENCE = 0.0;
74  
75      /**
76       * Maximum allowed confidence value.
77       */
78      public static final double MAX_CONFIDENCE = 1.0;
79  
80      /**
81       * Minimum allowed number of iterations.
82       */
83      public static final int MIN_ITERATIONS = 1;
84  
85      /**
86       * Minimum number of required point correspondences to estimate a radial
87       * distortion.
88       */
89      public static final int MIN_NUMBER_OF_POINTS = 2;
90  
91      /**
92       * Default number of radial distortion parameters.
93       */
94      public static final int DEFAULT_NUM_K_PARAMS = 2;
95  
96      /**
97       * Minimum number of radial distortion parameters.
98       */
99      public static final int MIN_K_PARAMS = 1;
100 
101     /**
102      * Defines default focal length if none is defined.
103      */
104     public static final double DEFAULT_FOCAL_LENGTH = 1.0;
105 
106     /**
107      * Defines default skewness if none is defined.
108      */
109     public static final double DEFAULT_SKEW = 0.0;
110 
111     /**
112      * List of distorted points (aka measured points).
113      */
114     protected List<Point2D> distortedPoints;
115 
116     /**
117      * List of undistorted points (aka ideal points).
118      */
119     protected List<Point2D> undistortedPoints;
120 
121     /**
122      * Distortion center.
123      */
124     protected Point2D distortionCenter;
125 
126     /**
127      * Horizontal focal length expressed in pixels.
128      */
129     protected double horizontalFocalLength;
130 
131     /**
132      * Vertical focal length expressed in pixels.
133      */
134     protected double verticalFocalLength;
135 
136     /**
137      * Skew in pixels.
138      */
139     protected double skew;
140 
141     /**
142      * Number of radial distortion parameters to estimate.
143      */
144     protected int numKParams;
145 
146     /**
147      * Listener to be notified of events such as when estimation starts, ends
148      * or its progress significantly changes.
149      */
150     protected RadialDistortionRobustEstimatorListener listener;
151 
152     /**
153      * Indicates if this estimator is locked because an estimation is being
154      * computed.
155      */
156     protected volatile boolean locked;
157 
158     /**
159      * Amount of progress variation before notifying a progress change during
160      * estimation.
161      */
162     protected float progressDelta;
163 
164     /**
165      * Amount of confidence expressed as a value between 0.0 and 1.0 (which is
166      * equivalent to 100%). The amount of confidence indicates the probability
167      * that the estimated result is correct. Usually this value will be close
168      * to 1.0, but not exactly 1.0.
169      */
170     protected double confidence;
171 
172     /**
173      * Maximum allowed number of iterations. When the maximum number of
174      * iterations is exceeded, result will not be available, however an
175      * approximate result will be available for retrieval.
176      */
177     protected int maxIterations;
178 
179     /**
180      * Constructor.
181      */
182     protected RadialDistortionRobustEstimator() {
183         progressDelta = DEFAULT_PROGRESS_DELTA;
184         confidence = DEFAULT_CONFIDENCE;
185         maxIterations = DEFAULT_MAX_ITERATIONS;
186         horizontalFocalLength = verticalFocalLength = DEFAULT_FOCAL_LENGTH;
187         skew = DEFAULT_SKEW;
188         numKParams = DEFAULT_NUM_K_PARAMS;
189     }
190 
191     /**
192      * Constructor.
193      *
194      * @param listener listener to be notified of events such as when
195      *                 estimation starts, ends or its progress significantly changes.
196      */
197     protected RadialDistortionRobustEstimator(final RadialDistortionRobustEstimatorListener listener) {
198         this();
199         this.listener = listener;
200     }
201 
202     /**
203      * Constructor.
204      *
205      * @param distortedPoints   list of distorted points. Distorted points are
206      *                          obtained after radial distortion is applied to an undistorted point.
207      * @param undistortedPoints list of undistorted points.
208      * @throws IllegalArgumentException if provided lists of points don't have
209      *                                  the same size or their size is smaller than MIN_NUMBER_OF_POINTS.
210      */
211     protected RadialDistortionRobustEstimator(
212             final List<Point2D> distortedPoints, final List<Point2D> undistortedPoints) {
213         this();
214         internalSetPoints(distortedPoints, undistortedPoints);
215     }
216 
217     /**
218      * Constructor.
219      *
220      * @param distortedPoints   list of distorted points. Distorted points are
221      *                          obtained after radial distortion is applied to an undistorted point.
222      * @param undistortedPoints list of undistorted points.
223      * @param listener          listener to be notified of events such as when
224      *                          estimation starts, ends or its progress significantly changes.
225      * @throws IllegalArgumentException if provided lists of points don't have
226      *                                  the same size or their size is smaller than MIN_NUMBER_OF_POINTS.
227      */
228     protected RadialDistortionRobustEstimator(
229             final List<Point2D> distortedPoints,
230             final List<Point2D> undistortedPoints,
231             final RadialDistortionRobustEstimatorListener listener) {
232         this(listener);
233         internalSetPoints(distortedPoints, undistortedPoints);
234     }
235 
236     /**
237      * Constructor.
238      *
239      * @param distortedPoints   list of distorted points. Distorted points are
240      *                          obtained after radial distortion is applied to an undistorted point.
241      * @param undistortedPoints list of undistorted points.
242      * @param distortionCenter  radial distortion center. If null it is assumed
243      *                          to be the origin of coordinates, otherwise this is typically equal to
244      *                          the camera principal point.
245      * @throws IllegalArgumentException if provided lists of points don't have
246      *                                  the same size or their size is smaller than MIN_NUMBER_OF_POINTS.
247      */
248     protected RadialDistortionRobustEstimator(
249             final List<Point2D> distortedPoints,
250             final List<Point2D> undistortedPoints,
251             final Point2D distortionCenter) {
252         this(distortedPoints, undistortedPoints);
253         this.distortionCenter = distortionCenter;
254     }
255 
256     /**
257      * Constructor.
258      *
259      * @param distortedPoints   list of distorted points. Distorted points are
260      *                          obtained after radial distortion is applied to an undistorted point.
261      * @param undistortedPoints list of undistorted points.
262      * @param distortionCenter  radial distortion center. If null it is assumed
263      *                          to be the origin of coordinates, otherwise this is typically equal to
264      *                          the camera principal point.
265      * @param listener          listener to be notified of events such as when
266      *                          estimation starts, ends or its progress significantly changes.
267      * @throws IllegalArgumentException if provided lists of points don't have
268      *                                  the same size or their size is smaller than MIN_NUMBER_OF_POINTS.
269      */
270     protected RadialDistortionRobustEstimator(
271             final List<Point2D> distortedPoints,
272             final List<Point2D> undistortedPoints,
273             final Point2D distortionCenter,
274             final RadialDistortionRobustEstimatorListener listener) {
275         this(distortedPoints, undistortedPoints, listener);
276         this.distortionCenter = distortionCenter;
277     }
278 
279     /**
280      * Returns reference to listener to be notified of events such as when
281      * estimation starts, ends or its progress significantly changes.
282      *
283      * @return listener to be notified of events.
284      */
285     public RadialDistortionRobustEstimatorListener getListener() {
286         return listener;
287     }
288 
289     /**
290      * Sets listener to be notified of events such as when estimation starts,
291      * ends or its progress significantly changes.
292      *
293      * @param listener listener to be notified of events.
294      * @throws LockedException if robust estimator is locked.
295      */
296     public void setListener(final RadialDistortionRobustEstimatorListener listener) throws LockedException {
297         if (isLocked()) {
298             throw new LockedException();
299         }
300         this.listener = listener;
301     }
302 
303     /**
304      * Indicates whether listener has been provided and is available for
305      * retrieval.
306      *
307      * @return true if available, false otherwise.
308      */
309     public boolean isListenerAvailable() {
310         return listener != null;
311     }
312 
313     /**
314      * Indicates if this instance is locked because estimation is being computed.
315      *
316      * @return true if locked, false otherwise.
317      */
318     public boolean isLocked() {
319         return locked;
320     }
321 
322     /**
323      * Returns amount of progress variation before notifying a progress change
324      * during estimation.
325      *
326      * @return amount of progress variation before notifying a progress change
327      * during estimation.
328      */
329     public float getProgressDelta() {
330         return progressDelta;
331     }
332 
333     /**
334      * Sets amount of progress variation before notifying a progress change
335      * during estimation.
336      *
337      * @param progressDelta amount of progress variation before notifying a
338      *                      progress change during estimation.
339      * @throws IllegalArgumentException if progress delta is less than zero or
340      *                                  greater than 1.
341      * @throws LockedException          if this estimator is locked because an estimation
342      *                                  is being computed.
343      */
344     public void setProgressDelta(final float progressDelta) throws LockedException {
345         if (isLocked()) {
346             throw new LockedException();
347         }
348         if (progressDelta < MIN_PROGRESS_DELTA || progressDelta > MAX_PROGRESS_DELTA) {
349             throw new IllegalArgumentException();
350         }
351         this.progressDelta = progressDelta;
352     }
353 
354     /**
355      * Returns amount of confidence expressed as a value between 0.0 and 1.0
356      * (which is equivalent to 100%). The amount of confidence indicates the
357      * probability that the estimated result is correct. Usually this value will
358      * be close to 1.0, but not exactly 1.0.
359      *
360      * @return amount of confidence as a value between 0.0 and 1.0.
361      */
362     public double getConfidence() {
363         return confidence;
364     }
365 
366     /**
367      * Sets amount of confidence expressed as a value between 0.0 and 1.0 (which
368      * is equivalent to 100%). The amount of confidence indicates the
369      * probability that the estimated result is correct. Usually this value will
370      * be close to 1.0, but not exactly 1.0.
371      *
372      * @param confidence confidence to be set as a value between 0.0 and 1.0.
373      * @throws IllegalArgumentException if provided value is not between 0.0 and
374      *                                  1.0.
375      * @throws LockedException          if this estimator is locked because an estimator
376      *                                  is being computed.
377      */
378     public void setConfidence(final double confidence) throws LockedException {
379         if (isLocked()) {
380             throw new LockedException();
381         }
382         if (confidence < MIN_CONFIDENCE || confidence > MAX_CONFIDENCE) {
383             throw new IllegalArgumentException();
384         }
385         this.confidence = confidence;
386     }
387 
388     /**
389      * Returns maximum allowed number of iterations. If maximum allowed number
390      * of iterations is achieved without converging to a result when calling
391      * estimate(), a RobustEstimatorException will be raised.
392      *
393      * @return maximum allowed number of iterations.
394      */
395     public int getMaxIterations() {
396         return maxIterations;
397     }
398 
399     /**
400      * Sets maximum allowed number of iterations. When the maximum number of
401      * iterations is exceeded, result will not be available, however an
402      * approximate result will be available for retrieval
403      *
404      * @param maxIterations maximum allowed number of iterations to be set.
405      * @throws IllegalArgumentException if provided value is less than 1.
406      * @throws LockedException          if this estimator is locked because an estimation
407      *                                  is being computed.
408      */
409     public void setMaxIterations(final int maxIterations) throws LockedException {
410         if (isLocked()) {
411             throw new LockedException();
412         }
413         if (maxIterations < MIN_ITERATIONS) {
414             throw new IllegalArgumentException();
415         }
416         this.maxIterations = maxIterations;
417     }
418 
419     /**
420      * Sets list of corresponding distorted and undistorted points.
421      *
422      * @param distortedPoints   list of distorted points. Distorted points are
423      *                          obtained after radial distortion is applied to an undistorted point.
424      * @param undistortedPoints list of undistorted points.
425      * @throws LockedException          if estimator is locked.
426      * @throws IllegalArgumentException if provided lists of points don't have
427      *                                  the same size.
428      */
429     public void setPoints(final List<Point2D> distortedPoints, final List<Point2D> undistortedPoints)
430             throws LockedException {
431         if (isLocked()) {
432             throw new LockedException();
433         }
434 
435         internalSetPoints(distortedPoints, undistortedPoints);
436     }
437 
438     /**
439      * Returns list of distorted points. Distorted points are obtained after
440      * radial distortion is applied to an undistorted point.
441      *
442      * @return list of distorted points.
443      */
444     public List<Point2D> getDistortedPoints() {
445         return distortedPoints;
446     }
447 
448     /**
449      * Returns list of undistorted points.
450      *
451      * @return list of undistorted points.
452      */
453     public List<Point2D> getUndistortedPoints() {
454         return undistortedPoints;
455     }
456 
457     /**
458      * Returns distortion center. This is usually equal to the principal point
459      * of an estimated camera. If not set it is assumed to be at the origin of
460      * coordinates (0,0).
461      *
462      * @return distortion center or null.
463      */
464     public Point2D getDistortionCenter() {
465         return distortionCenter;
466     }
467 
468     /**
469      * Sets distortion center. This is usually equal to the principal point of
470      * an estimated camera. If not set it is assumed to be at the origin of
471      * coordinates (0,0).
472      *
473      * @param distortionCenter distortion center, or null if set at origin of
474      *                         coordinates.
475      * @throws LockedException if estimator is locked.
476      */
477     public void setDistortionCenter(final Point2D distortionCenter) throws LockedException {
478         if (isLocked()) {
479             throw new LockedException();
480         }
481 
482         this.distortionCenter = distortionCenter;
483     }
484 
485     /**
486      * Returns horizontal focal length expressed in pixels.
487      *
488      * @return horizontal focal length expressed in pixels.
489      */
490     public double getHorizontalFocalLength() {
491         return horizontalFocalLength;
492     }
493 
494     /**
495      * Sets horizontal focal length expressed in pixels.
496      *
497      * @param horizontalFocalLength horizontal focal length expressed in pixels.
498      * @throws LockedException if estimator is locked.
499      */
500     public void setHorizontalFocalLength(final double horizontalFocalLength) throws LockedException {
501         if (isLocked()) {
502             throw new LockedException();
503         }
504 
505         this.horizontalFocalLength = horizontalFocalLength;
506     }
507 
508     /**
509      * Returns vertical focal length expressed in pixels.
510      *
511      * @return vertical focal length expressed in pixels.
512      */
513     public double getVerticalFocalLength() {
514         return verticalFocalLength;
515     }
516 
517     /**
518      * Sets vertical focal length expressed in pixels.
519      *
520      * @param verticalFocalLength vertical focal length expressed in pixels.
521      * @throws LockedException if estimator is locked.
522      */
523     public void setVerticalFocalLength(final double verticalFocalLength) throws LockedException {
524         if (isLocked()) {
525             throw new LockedException();
526         }
527 
528         this.verticalFocalLength = verticalFocalLength;
529     }
530 
531     /**
532      * Returns skew expressed in pixels.
533      *
534      * @return skew expressed in pixels.
535      */
536     public double getSkew() {
537         return skew;
538     }
539 
540     /**
541      * Sets skew expressed in pixels.
542      *
543      * @param skew skew expressed in pixels.
544      * @throws LockedException if estimator is locked.
545      */
546     public void setSkew(final double skew) throws LockedException {
547         if (isLocked()) {
548             throw new LockedException();
549         }
550 
551         this.skew = skew;
552     }
553 
554     /**
555      * Returns pinhole camera intrinsic parameters associated to this estimator.
556      *
557      * @return pinhole camera intrinsic parameters associated to this estimator.
558      */
559     public PinholeCameraIntrinsicParameters getIntrinsic() {
560         return new PinholeCameraIntrinsicParameters(horizontalFocalLength,
561                 verticalFocalLength,
562                 distortionCenter != null ? distortionCenter.getInhomX() : 0.0,
563                 distortionCenter != null ? distortionCenter.getInhomY() : 0.0,
564                 skew);
565     }
566 
567     /**
568      * Sets intrinsic parameters for this estimator from pinhole camera
569      * intrinsic parameters.
570      *
571      * @param intrinsic intrinsic parameters.
572      * @throws LockedException if estimator is locked.
573      */
574     public void setIntrinsic(final PinholeCameraIntrinsicParameters intrinsic)
575             throws LockedException {
576         setIntrinsic(new InhomogeneousPoint2D(
577                 intrinsic.getHorizontalPrincipalPoint(), intrinsic.getVerticalPrincipalPoint()),
578                 intrinsic.getHorizontalFocalLength(),
579                 intrinsic.getVerticalFocalLength(),
580                 intrinsic.getSkewness());
581     }
582 
583     /**
584      * Sets intrinsic parameters for this estimator.
585      *
586      * @param distortionCenter      distortion center.
587      * @param horizontalFocalLength horizontal focal length in pixels.
588      * @param verticalFocalLength   vertical focal length in pixels.
589      * @param skew                  skew in pixels.
590      * @throws LockedException if estimator is locked.
591      */
592     public void setIntrinsic(
593             final Point2D distortionCenter, final double horizontalFocalLength, final double verticalFocalLength,
594             final double skew) throws LockedException {
595         if (isLocked()) {
596             throw new LockedException();
597         }
598 
599         this.distortionCenter = distortionCenter;
600         this.horizontalFocalLength = horizontalFocalLength;
601         this.verticalFocalLength = verticalFocalLength;
602         this.skew = skew;
603     }
604 
605     /**
606      * Returns number of radial distortion parameters to estimate.
607      *
608      * @return number of radial distortion parameters to estimate.
609      */
610     public int getNumKParams() {
611         return numKParams;
612     }
613 
614     /**
615      * Sets number of radial distortion parameters to estimate.
616      *
617      * @param numKParams number of radial distortion parameters to estimate.
618      * @throws LockedException          if estimator is locked.
619      * @throws IllegalArgumentException if number of parameters is less than 1.
620      */
621     public void setNumKParams(final int numKParams) throws LockedException {
622         if (isLocked()) {
623             throw new LockedException();
624         }
625         if (numKParams < MIN_K_PARAMS) {
626             throw new IllegalArgumentException();
627         }
628 
629         this.numKParams = numKParams;
630     }
631 
632     /**
633      * Indicates if lists of corresponding points have already been provided and
634      * are available for retrieval.
635      *
636      * @return true if available, false otherwise.
637      */
638     public boolean arePointsAvailable() {
639         return distortedPoints != null && undistortedPoints != null;
640     }
641 
642     /**
643      * Indicates if this estimator is ready to start th estimation.
644      * This is true when lists of points are provided, having equal size and
645      * at least 2 points.
646      *
647      * @return true if estimator is ready, false otherwise.
648      */
649     public boolean isReady() {
650         return arePointsAvailable() && areValidPoints(distortedPoints, undistortedPoints);
651     }
652 
653     /**
654      * Returns quality scores corresponding to each point.
655      * The larger the score value the better the quality of the point measure.
656      * This implementation always returns null.
657      * Subclasses using quality scores must implement proper behaviour.
658      *
659      * @return quality scores corresponding to each point.
660      */
661     public double[] getQualityScores() {
662         return null;
663     }
664 
665     /**
666      * Sets quality scores corresponding to each point.
667      * The larger the score value the better the quality of the point sample.
668      * This implementation makes no action.
669      * Subclasses using quality scores must implement proper behaviour.
670      *
671      * @param qualityScores quality scores corresponding to each sampled point.
672      * @throws LockedException          if robust estimator is locked because an
673      *                                  estimation is already in progress.
674      * @throws IllegalArgumentException if provided quality scores length is
675      *                                  smaller than MINIMUM_SIZE (i.e. 3 samples).
676      */
677     public void setQualityScores(final double[] qualityScores) throws LockedException {
678     }
679 
680     /**
681      * Estimates a radial distortion using a robust estimator and
682      * the best set of matched 2D points found using the robust estimator.
683      *
684      * @return a radial distortion.
685      * @throws LockedException          if robust estimator is locked because an
686      *                                  estimation is already in progress.
687      * @throws NotReadyException        if provided input data is not enough to start
688      *                                  the estimation.
689      * @throws RobustEstimatorException if estimation fails for any reason
690      *                                  (i.e. numerical instability, no solution available, etc).
691      */
692     public abstract RadialDistortion estimate() throws LockedException, NotReadyException, RobustEstimatorException;
693 
694     /**
695      * Returns method being used for robust estimation.
696      *
697      * @return method being used for robust estimation.
698      */
699     public abstract RobustEstimatorMethod getMethod();
700 
701     /**
702      * Creates a radial distortion robust estimator using provided robust method.
703      *
704      * @param method method of a robust estimator algorithm to estimate the best
705      *               radial distortion.
706      * @return an instance of a radial distortion robust estimator.
707      */
708     public static RadialDistortionRobustEstimator create(
709             final RobustEstimatorMethod method) {
710         return switch (method) {
711             case LMEDS -> new LMedSRadialDistortionRobustEstimator();
712             case MSAC -> new MSACRadialDistortionRobustEstimator();
713             case PROSAC -> new PROSACRadialDistortionRobustEstimator();
714             case PROMEDS -> new PROMedSRadialDistortionRobustEstimator();
715             default -> new RANSACRadialDistortionRobustEstimator();
716         };
717     }
718 
719     /**
720      * Creates a radial distortion robust estimator using provided distorted and
721      * undistorted points, as well as the distortion center. If no distortion
722      * center is provided, it is assumed to be at the origin of coordinates.
723      *
724      * @param distortedPoints   list of distorted points. Distorted points are
725      *                          obtained after radial distortion is applied to an undistorted point.
726      * @param undistortedPoints list of undistorted points.
727      * @param qualityScores     quality scores corresponding to each point.
728      * @param distortionCenter  Distortion center. This is usually equal to the
729      *                          principal point of an estimated camera. If not set it is assumed to be at
730      *                          the origin of coordinates (0,0).
731      * @param method            method of a robust estimator algorithm to estimate the best
732      *                          radial distortion.
733      * @return an instance of a radial distortion robust estimator.
734      * @throws IllegalArgumentException if provided lists of points don't have
735      *                                  the same size.
736      */
737     public static RadialDistortionRobustEstimator create(
738             final List<Point2D> distortedPoints, final List<Point2D> undistortedPoints, final double[] qualityScores,
739             final Point2D distortionCenter, final RobustEstimatorMethod method) {
740         return switch (method) {
741             case LMEDS -> new LMedSRadialDistortionRobustEstimator(distortedPoints, undistortedPoints,
742                     distortionCenter);
743             case MSAC -> new MSACRadialDistortionRobustEstimator(distortedPoints, undistortedPoints, distortionCenter);
744             case PROSAC -> new PROSACRadialDistortionRobustEstimator(distortedPoints, undistortedPoints, qualityScores,
745                     distortionCenter);
746             case PROMEDS -> new PROMedSRadialDistortionRobustEstimator(distortedPoints, undistortedPoints,
747                     qualityScores, distortionCenter);
748             default -> new RANSACRadialDistortionRobustEstimator(distortedPoints, undistortedPoints, distortionCenter);
749         };
750     }
751 
752     /**
753      * Creates a radial distortion robust estimator using provided distorted and
754      * undistorted points, as well as the distortion center. If no distortion
755      * center is provided, it is assumed to be at the origin of coordinates.
756      *
757      * @param distortedPoints   list of distorted points. Distorted points are
758      *                          obtained after radial distortion is applied to an undistorted point.
759      * @param undistortedPoints list of undistorted points.
760      * @param distortionCenter  Distortion center. This is usually equal to the
761      *                          principal point of an estimated camera. If not set it is assumed to be at
762      *                          the origin of coordinates (0,0).
763      * @param method            method of a robust estimator algorithm to estimate the best
764      *                          radial distortion.
765      * @return an instance of a radial distortion robust estimator.
766      * @throws IllegalArgumentException if provided lists of points don't have
767      *                                  the same size.
768      */
769     public static RadialDistortionRobustEstimator create(
770             final List<Point2D> distortedPoints, final List<Point2D> undistortedPoints, final Point2D distortionCenter,
771             final RobustEstimatorMethod method) {
772         return switch (method) {
773             case LMEDS -> new LMedSRadialDistortionRobustEstimator(distortedPoints, undistortedPoints,
774                     distortionCenter);
775             case MSAC -> new MSACRadialDistortionRobustEstimator(distortedPoints, undistortedPoints, distortionCenter);
776             case PROSAC -> new PROSACRadialDistortionRobustEstimator(distortedPoints, undistortedPoints,
777                     distortionCenter);
778             case PROMEDS -> new PROMedSRadialDistortionRobustEstimator(distortedPoints, undistortedPoints,
779                     distortionCenter);
780             default -> new RANSACRadialDistortionRobustEstimator(distortedPoints, undistortedPoints, distortionCenter);
781         };
782     }
783 
784     /**
785      * Creates a radial distortion robust estimator using provided distorted and
786      * undistorted points and assuming that distortion center is at origin of
787      * coordinates.
788      *
789      * @param distortedPoints   list of distorted points. Distorted points are
790      *                          obtained after radial distortion is applied to an undistorted point.
791      * @param undistortedPoints list of undistorted points.
792      * @param qualityScores     quality scores corresponding to each point.
793      * @param method            method of a robust estimator algorithm to estimate the best
794      *                          radial distortion.
795      * @return an instance of a radial distortion robust estimator.
796      * @throws IllegalArgumentException if provided lists of points don't have
797      *                                  the same size.
798      */
799     public static RadialDistortionRobustEstimator create(
800             final List<Point2D> distortedPoints, final List<Point2D> undistortedPoints, final double[] qualityScores,
801             final RobustEstimatorMethod method) {
802         return create(distortedPoints, undistortedPoints, qualityScores, null, method);
803     }
804 
805     /**
806      * Creates a radial distortion robust estimator using provided distorted and
807      * undistorted points and assuming that distortion center is at origin of
808      * coordinates.
809      *
810      * @param distortedPoints   list of distorted points. Distorted points are
811      *                          obtained after radial distortion is applied to an undistorted point.
812      * @param undistortedPoints list of undistorted points.
813      * @param method            method of a robust estimator algorithm to estimate the best
814      *                          radial distortion.
815      * @return an instance of a radial distortion robust estimator.
816      * @throws IllegalArgumentException if provided lists of points don't have
817      *                                  the same size.
818      */
819     public static RadialDistortionRobustEstimator create(
820             final List<Point2D> distortedPoints, final List<Point2D> undistortedPoints,
821             final RobustEstimatorMethod method) {
822         return create(distortedPoints, undistortedPoints, (Point2D) null, method);
823     }
824 
825     /**
826      * Creates a radial distortion robust estimator using default robust method.
827      *
828      * @return an instance of a radial distortion robust estimator.
829      */
830     public static RadialDistortionRobustEstimator create() {
831         return create(DEFAULT_ROBUST_METHOD);
832     }
833 
834 
835     /**
836      * Creates a radial distortion robust estimator using provided distorted and
837      * undistorted points assuming that distortion center is at origin of
838      * coordinates and using default robust estimation method.
839      *
840      * @param distortedPoints   list of distorted points. Distorted points are
841      *                          obtained after radial distortion is applied to an undistorted point.
842      * @param undistortedPoints list of undistorted points.
843      * @param qualityScores     quality scores corresponding to each point
844      * @return an instance of a radial distortion robust estimator.
845      * @throws IllegalArgumentException if provided lists of points don't have
846      *                                  the same size.
847      */
848     public static RadialDistortionRobustEstimator create(
849             final List<Point2D> distortedPoints, final List<Point2D> undistortedPoints, final double[] qualityScores) {
850         return create(distortedPoints, undistortedPoints, qualityScores, DEFAULT_ROBUST_METHOD);
851     }
852 
853     /**
854      * Creates a radial distortion robust estimator using provided distorted and
855      * undistorted points assuming that distortion center is at origin of
856      * coordinates and using default robust estimation method.
857      *
858      * @param distortedPoints   list of distorted points. Distorted points are
859      *                          obtained after radial distortion is applied to an undistorted point.
860      * @param undistortedPoints list of undistorted points.
861      * @return an instance of a radial distortion robust estimator.
862      * @throws IllegalArgumentException if provided lists of points don't have
863      *                                  the same size.
864      */
865     public static RadialDistortionRobustEstimator create(
866             final List<Point2D> distortedPoints, final List<Point2D> undistortedPoints) {
867         return create(distortedPoints, undistortedPoints, DEFAULT_ROBUST_METHOD);
868     }
869 
870     /**
871      * Creates a radial distortion robust estimator using provided distorted and
872      * undistorted points, as well as the distortion center using the default
873      * robust estimation method. If no distortion center is provided, it is
874      * assumed to be at the origin of coordinates.
875      *
876      * @param distortedPoints   list of distorted points. Distorted points are
877      *                          obtained after radial distortion is applied to an undistorted point.
878      * @param undistortedPoints list of undistorted points.
879      * @param qualityScores     quality scores corresponding to each point.
880      * @param distortionCenter  Distortion center. This is usually equal to the
881      *                          principal point of an estimated camera. If not set it is assumed to be at
882      *                          the origin of coordinates (0,0).
883      * @return an instance of a radial distortion robust estimator.
884      * @throws IllegalArgumentException if provided lists of points don't have
885      *                                  the same size.
886      */
887     public static RadialDistortionRobustEstimator create(
888             final List<Point2D> distortedPoints, final List<Point2D> undistortedPoints, final double[] qualityScores,
889             final Point2D distortionCenter) {
890         return create(distortedPoints, undistortedPoints, qualityScores, distortionCenter, DEFAULT_ROBUST_METHOD);
891     }
892 
893     /**
894      * Creates a radial distortion robust estimator using provided distorted and
895      * undistorted points, as well as the distortion center using the default
896      * robust estimation method. If no distortion center is provided, it is
897      * assumed to be at the origin of coordinates.
898      *
899      * @param distortedPoints   list of distorted points. Distorted points are
900      *                          obtained after radial distortion is applied to an undistorted point.
901      * @param undistortedPoints list of undistorted points.
902      * @param distortionCenter  Distortion center. This is usually equal to the
903      *                          principal point of an estimated camera. If not set it is assumed to be at
904      *                          the origin of coordinates (0,0).
905      * @return an instance of a radial distortion robust estimator.
906      * @throws IllegalArgumentException if provided lists of points don't have
907      *                                  the same size.
908      */
909     public static RadialDistortionRobustEstimator create(
910             final List<Point2D> distortedPoints, final List<Point2D> undistortedPoints,
911             final Point2D distortionCenter) {
912         return create(distortedPoints, undistortedPoints, distortionCenter, DEFAULT_ROBUST_METHOD);
913     }
914 
915     /**
916      * Indicates if lists of corresponding distorted and undistorted points are
917      * valid.
918      * Lists are considered valid if they have the same number of points and
919      * both have more than the required minimum of correspondences (which is 2).
920      *
921      * @param distortedPoints   list of distorted points. Distorted points are
922      *                          obtained after radial distortion is applied to an undistorted point.
923      * @param undistortedPoints list of undistorted points.
924      * @return true if lists of points are valid, false otherwise.
925      */
926     public static boolean areValidPoints(final List<Point2D> distortedPoints, final List<Point2D> undistortedPoints) {
927         if (distortedPoints == null || undistortedPoints == null) {
928             return false;
929         }
930         return distortedPoints.size() == undistortedPoints.size() && distortedPoints.size() >= MIN_NUMBER_OF_POINTS;
931     }
932 
933     /**
934      * Sets list of corresponding distorted and undistorted points.
935      * This method does not check whether estimator is locked.
936      *
937      * @param distortedPoints   list of distorted points. Distorted points are
938      *                          obtained after radial distortion is applied to an undistorted point.
939      * @param undistortedPoints list of undistorted points.
940      * @throws IllegalArgumentException if provided lists of points don't have
941      *                                  the same size.
942      */
943     private void internalSetPoints(final List<Point2D> distortedPoints, final List<Point2D> undistortedPoints) {
944 
945         if (distortedPoints == null || undistortedPoints == null) {
946             throw new IllegalArgumentException();
947         }
948 
949         if (!areValidPoints(distortedPoints, undistortedPoints)) {
950             throw new IllegalArgumentException();
951         }
952 
953         this.distortedPoints = distortedPoints;
954         this.undistortedPoints = undistortedPoints;
955     }
956 }