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.algebra.AlgebraException;
19  import com.irurueta.algebra.Matrix;
20  import com.irurueta.geometry.Conic;
21  import com.irurueta.geometry.Point2D;
22  import com.irurueta.numerical.robust.RobustEstimatorException;
23  import com.irurueta.numerical.robust.RobustEstimatorMethod;
24  
25  import java.util.List;
26  
27  /**
28   * This is an abstract class for algorithms to robustly find the best conic
29   * that fits in a collection of 2D points.
30   * Implementations of this class should be able to detect and discard outliers
31   * in order to find the best solution.
32   */
33  @SuppressWarnings("DuplicatedCode")
34  public abstract class ConicRobustEstimator {
35  
36      /**
37       * Minimum number of 2D points required to estimate a Conic.
38       */
39      public static final int MINIMUM_SIZE = 5;
40  
41      /**
42       * Default amount of progress variation before notifying a change in
43       * estimation progress. By default, this is set to 5%.
44       */
45      public static final float DEFAULT_PROGRESS_DELTA = 0.05f;
46  
47      /**
48       * Minimum allowed value for progress delta.
49       */
50      public static final float MIN_PROGRESS_DELTA = 0.0f;
51  
52      /**
53       * Maximum allowed value for progress delta.
54       */
55      public static final float MAX_PROGRESS_DELTA = 1.0f;
56  
57      /**
58       * Constant defining default confidence of the estimated result, which is
59       * 99%. This means that with a probability of 99% estimation will be
60       * accurate because chosen sub-samples will be inliers.
61       */
62      public static final double DEFAULT_CONFIDENCE = 0.99;
63  
64      /**
65       * Default maximum allowed number of iterations.
66       */
67      public static final int DEFAULT_MAX_ITERATIONS = 5000;
68  
69      /**
70       * Minimum allowed confidence value.
71       */
72      public static final double MIN_CONFIDENCE = 0.0;
73  
74      /**
75       * Maximum allowed confidence value.
76       */
77      public static final double MAX_CONFIDENCE = 1.0;
78  
79      /**
80       * Minimum allowed number of iterations.
81       */
82      public static final int MIN_ITERATIONS = 1;
83  
84      /**
85       * Default robust estimator method when none is provided.
86       */
87      public static final RobustEstimatorMethod DEFAULT_ROBUST_METHOD = RobustEstimatorMethod.PROMEDS;
88  
89      /**
90       * Listener to be notified of events such as when estimation starts, ends
91       * or its progress significantly changes.
92       */
93      protected ConicRobustEstimatorListener listener;
94  
95      /**
96       * Indicates if this estimator is locked because an estimation is being
97       * computed.
98       */
99      protected volatile boolean locked;
100 
101     /**
102      * Amount of progress variation before notifying a progress change during
103      * estimation.
104      */
105     protected float progressDelta;
106 
107     /**
108      * Amount of confidence expressed as a value between 0.0 and 1.0 (which is
109      * equivalent to 100%). The amount of confidence indicates the probability
110      * that the estimated result is correct. Usually this value will be close
111      * to 1.0, but not exactly 1.0.
112      */
113     protected double confidence;
114 
115     /**
116      * Maximum allowed number of iterations. When the maximum number of
117      * iterations is exceeded, result will not be available, however an
118      * approximate result will be available for retrieval.
119      */
120     protected int maxIterations;
121 
122     /**
123      * List of points to be used to estimate a conic. Provided list must have
124      * a size greater or equal than MINIMUM_SIZE.
125      */
126     protected List<Point2D> points;
127 
128     /**
129      * Matrix representation of a 2D point to be reused when computing
130      * residuals.
131      */
132     private Matrix testPoint;
133 
134     /**
135      * Matrix representation of a conic to be reused when computing
136      * residuals.
137      */
138     private Matrix testC;
139 
140     /**
141      * Constructor.
142      */
143     protected ConicRobustEstimator() {
144         progressDelta = DEFAULT_PROGRESS_DELTA;
145         confidence = DEFAULT_CONFIDENCE;
146         maxIterations = DEFAULT_MAX_ITERATIONS;
147     }
148 
149     /**
150      * Constructor.
151      *
152      * @param listener listener to be notified of events such as when estimation
153      *                 starts, ends or its progress significantly changes.
154      */
155     protected ConicRobustEstimator(final ConicRobustEstimatorListener listener) {
156         this.listener = listener;
157         progressDelta = DEFAULT_PROGRESS_DELTA;
158         confidence = DEFAULT_CONFIDENCE;
159         maxIterations = DEFAULT_MAX_ITERATIONS;
160     }
161 
162     /**
163      * Constructor with points.
164      *
165      * @param points 2D points to estimate a conic.
166      * @throws IllegalArgumentException if provided list of points don't have
167      *                                  a size greater or equal than MINIMUM_SIZE.
168      */
169     protected ConicRobustEstimator(final List<Point2D> points) {
170         progressDelta = DEFAULT_PROGRESS_DELTA;
171         confidence = DEFAULT_CONFIDENCE;
172         maxIterations = DEFAULT_MAX_ITERATIONS;
173         internalSetPoints(points);
174     }
175 
176     /**
177      * Constructor.
178      *
179      * @param points   2D points to estimate a conic.
180      * @param listener listener to be notified of events such as when estimation
181      *                 starts, ends or its progress significantly changes.
182      * @throws IllegalArgumentException if provided list of points don't have
183      *                                  a size greater or equal than MINIMUM_SIZE.
184      */
185     protected ConicRobustEstimator(final ConicRobustEstimatorListener listener,
186                                    final List<Point2D> points) {
187         this.listener = listener;
188         progressDelta = DEFAULT_PROGRESS_DELTA;
189         confidence = DEFAULT_CONFIDENCE;
190         maxIterations = DEFAULT_MAX_ITERATIONS;
191         internalSetPoints(points);
192     }
193 
194 
195     /**
196      * Returns reference to listener to be notified of events such as when
197      * estimation starts, ends or its progress significantly changes.
198      *
199      * @return listener to be notified of events.
200      */
201     public ConicRobustEstimatorListener getListener() {
202         return listener;
203     }
204 
205     /**
206      * Sets listener to be notified of events such as when estimation starts,
207      * ends or its progress significantly changes.
208      *
209      * @param listener listener to be notified of events.
210      * @throws LockedException if robust estimator is locked.
211      */
212     public void setListener(final ConicRobustEstimatorListener listener) throws LockedException {
213         if (isLocked()) {
214             throw new LockedException();
215         }
216         this.listener = listener;
217     }
218 
219     /**
220      * Indicates whether listener has been provided and is available for
221      * retrieval.
222      *
223      * @return true if available, false otherwise.
224      */
225     public boolean isListenerAvailable() {
226         return listener != null;
227     }
228 
229     /**
230      * Indicates if this instance is locked because estimation is being computed.
231      *
232      * @return true if locked, false otherwise.
233      */
234     public boolean isLocked() {
235         return locked;
236     }
237 
238     /**
239      * Returns amount of progress variation before notifying a progress change
240      * during estimation.
241      *
242      * @return amount of progress variation before notifying a progress change
243      * during estimation.
244      */
245     public float getProgressDelta() {
246         return progressDelta;
247     }
248 
249     /**
250      * Sets amount of progress variation before notifying a progress change
251      * during estimation.
252      *
253      * @param progressDelta amount of progress variation before notifying a
254      *                      progress change during estimation.
255      * @throws IllegalArgumentException if progress delta is less than zero or
256      *                                  greater than 1.
257      * @throws LockedException          if this estimator is locked because an estimation
258      *                                  is being computed.
259      */
260     public void setProgressDelta(final float progressDelta) throws LockedException {
261         if (isLocked()) {
262             throw new LockedException();
263         }
264         if (progressDelta < MIN_PROGRESS_DELTA || progressDelta > MAX_PROGRESS_DELTA) {
265             throw new IllegalArgumentException();
266         }
267         this.progressDelta = progressDelta;
268     }
269 
270     /**
271      * Returns amount of confidence expressed as a value between 0.0 and 1.0
272      * (which is equivalent to 100%). The amount of confidence indicates the
273      * probability that the estimated result is correct. Usually this value will
274      * be close to 1.0, but not exactly 1.0.
275      *
276      * @return amount of confidence as a value between 0.0 and 1.0.
277      */
278     public double getConfidence() {
279         return confidence;
280     }
281 
282     /**
283      * Sets amount of confidence expressed as a value between 0.0 and 1.0 (which
284      * is equivalent to 100%). The amount of confidence indicates the
285      * probability that the estimated result is correct. Usually this value will
286      * be close to 1.0, but not exactly 1.0.
287      *
288      * @param confidence confidence to be set as a value between 0.0 and 1.0.
289      * @throws IllegalArgumentException if provided value is not between 0.0 and
290      *                                  1.0.
291      * @throws LockedException          if this estimator is locked because an estimator
292      *                                  is being computed.
293      */
294     public void setConfidence(final double confidence) throws LockedException {
295         if (isLocked()) {
296             throw new LockedException();
297         }
298         if (confidence < MIN_CONFIDENCE || confidence > MAX_CONFIDENCE) {
299             throw new IllegalArgumentException();
300         }
301         this.confidence = confidence;
302     }
303 
304     /**
305      * Returns maximum allowed number of iterations. If maximum allowed number
306      * of iterations is achieved without converging to a result when calling
307      * estimate(), a RobustEstimatorException will be raised.
308      *
309      * @return maximum allowed number of iterations.
310      */
311     public int getMaxIterations() {
312         return maxIterations;
313     }
314 
315     /**
316      * Sets maximum allowed number of iterations. When the maximum number of
317      * iterations is exceeded, result will not be available, however an
318      * approximate result will be available for retrieval.
319      *
320      * @param maxIterations maximum allowed number of iterations to be set.
321      * @throws IllegalArgumentException if provided value is less than 1.
322      * @throws LockedException          if this estimator is locked because an estimation
323      *                                  is being computed.
324      */
325     public void setMaxIterations(final int maxIterations) throws LockedException {
326         if (isLocked()) {
327             throw new LockedException();
328         }
329         if (maxIterations < MIN_ITERATIONS) {
330             throw new IllegalArgumentException();
331         }
332         this.maxIterations = maxIterations;
333     }
334 
335     /**
336      * Returns list of points to be used to estimate a conic.
337      * Provided list must have a size greater or equal than MINIMUM_SIZE.
338      *
339      * @return list of points to be used to estimate a conic.
340      */
341     public List<Point2D> getPoints() {
342         return points;
343     }
344 
345     /**
346      * Sets list of points to be used to estimate a conic.
347      * Provided list must have a size greater or equal than MINIMUM_SIZE.
348      *
349      * @param points list of points to be used to estimate a conic.
350      * @throws IllegalArgumentException if provided list of points don't have
351      *                                  a size greater or equal than MINIMUM_SIZE.
352      * @throws LockedException          if estimator is locked because a computation is
353      *                                  already in progress.
354      */
355     public void setPoints(final List<Point2D> points) throws LockedException {
356         if (isLocked()) {
357             throw new LockedException();
358         }
359         internalSetPoints(points);
360     }
361 
362     /**
363      * Indicates if estimator is ready to start the conic estimation.
364      * This is true when a minimum if MINIMUM_SIZE points are available.
365      *
366      * @return true if estimator is ready, false otherwise.
367      */
368     public boolean isReady() {
369         return points != null && points.size() >= MINIMUM_SIZE;
370     }
371 
372     /**
373      * Returns quality scores corresponding to each point.
374      * The larger the score value the better the quality of the point measure.
375      * This implementation always returns null.
376      * Subclasses using quality scores must implement proper behaviour.
377      *
378      * @return quality scores corresponding to each point.
379      */
380     public double[] getQualityScores() {
381         return null;
382     }
383 
384     /**
385      * Sets quality scores corresponding to each point.
386      * The larger the score value the better the quality of the matching.
387      * This implementation makes no action.
388      * Subclasses using quality scores must implement proper behaviour.
389      *
390      * @param qualityScores quality scores corresponding to each pair of matched
391      *                      points.
392      * @throws LockedException          if robust estimator is locked because an
393      *                                  estimation is already in progress.
394      * @throws IllegalArgumentException if provided quality scores length is
395      *                                  smaller than MINIMUM_SIZE (i.e. 5 samples).
396      */
397     public void setQualityScores(final double[] qualityScores) throws LockedException {
398     }
399 
400     /**
401      * Creates a conic robust estimator based on 2D point samples and using
402      * provided robust estimator method.
403      *
404      * @param method method of a robust estimator algorithm to estimate the best
405      *               conic.
406      * @return an instance of a conic robust estimator.
407      */
408     public static ConicRobustEstimator create(final RobustEstimatorMethod method) {
409         return switch (method) {
410             case LMEDS -> new LMedSConicRobustEstimator();
411             case MSAC -> new MSACConicRobustEstimator();
412             case PROSAC -> new PROSACConicRobustEstimator();
413             case PROMEDS -> new PROMedSConicRobustEstimator();
414             default -> new RANSACConicRobustEstimator();
415         };
416     }
417 
418     /**
419      * Creates a conic robust estimator based on 2D point samples and using
420      * provided points and robust estimator method.
421      *
422      * @param points 2D points to estimate a conic.
423      * @param method method of a robust estimator algorithm to estimate the best
424      *               conic.
425      * @return an instance of a conic robust estimator.
426      * @throws IllegalArgumentException if provided list of points don't have a
427      *                                  size greater or equal than MINIMUM_SIZE.
428      */
429     public static ConicRobustEstimator create(final List<Point2D> points, final RobustEstimatorMethod method) {
430         return switch (method) {
431             case LMEDS -> new LMedSConicRobustEstimator(points);
432             case MSAC -> new MSACConicRobustEstimator(points);
433             case PROSAC -> new PROSACConicRobustEstimator(points);
434             case PROMEDS -> new PROMedSConicRobustEstimator(points);
435             default -> new RANSACConicRobustEstimator(points);
436         };
437     }
438 
439     /**
440      * Creates a conic robust estimator based on 2D point samples and using
441      * provided listener.
442      *
443      * @param listener listener to be notified of events such as when estimation
444      *                 starts, ends or its progress significantly changes.
445      * @param method   method of a robust estimator algorithm to estimate the best
446      *                 conic.
447      * @return an instance of a conic robust estimator.
448      */
449     public static ConicRobustEstimator create(
450             final ConicRobustEstimatorListener listener, final RobustEstimatorMethod method) {
451         return switch (method) {
452             case LMEDS -> new LMedSConicRobustEstimator(listener);
453             case MSAC -> new MSACConicRobustEstimator(listener);
454             case PROSAC -> new PROSACConicRobustEstimator(listener);
455             case PROMEDS -> new PROMedSConicRobustEstimator(listener);
456             default -> new RANSACConicRobustEstimator(listener);
457         };
458     }
459 
460     /**
461      * Creates a conic robust estimator based on 2D point samples and using
462      * provided listener and points.
463      *
464      * @param listener listener to be notified of events such as when estimation
465      *                 starts, ends or its progress significantly changes.
466      * @param points   2D points to estimate a conic.
467      * @param method   method of a robust estimator algorithm to estimate the best
468      *                 conic.
469      * @return an instance of a conic robust estimator.
470      * @throws IllegalArgumentException if provided list of points don't have a
471      *                                  size greater or equal than MINIMUM_SIZE.
472      */
473     public static ConicRobustEstimator create(
474             final ConicRobustEstimatorListener listener, final List<Point2D> points,
475             final RobustEstimatorMethod method) {
476         return switch (method) {
477             case LMEDS -> new LMedSConicRobustEstimator(listener, points);
478             case MSAC -> new MSACConicRobustEstimator(listener, points);
479             case PROSAC -> new PROSACConicRobustEstimator(listener, points);
480             case PROMEDS -> new PROMedSConicRobustEstimator(listener, points);
481             default -> new RANSACConicRobustEstimator(listener, points);
482         };
483     }
484 
485     /**
486      * Creates a conic robust estimator based on 2D point samples and using
487      * provided robust estimator method.
488      *
489      * @param qualityScores quality scores corresponding to each provided point.
490      * @param method        method of a robust estimator algorithm to estimate the best
491      *                      conic.
492      * @return an instance of a conic robust estimator.
493      * @throws IllegalArgumentException if provided quality scores length is
494      *                                  smaller than MINIMUM_SIZE (i.e. 5 points).
495      */
496     public static ConicRobustEstimator create(final double[] qualityScores, final RobustEstimatorMethod method) {
497         return switch (method) {
498             case LMEDS -> new LMedSConicRobustEstimator();
499             case MSAC -> new MSACConicRobustEstimator();
500             case PROSAC -> new PROSACConicRobustEstimator(qualityScores);
501             case PROMEDS -> new PROMedSConicRobustEstimator(qualityScores);
502             default -> new RANSACConicRobustEstimator();
503         };
504     }
505 
506     /**
507      * Creates a conic robust estimator based on 2D point samples and using
508      * provided points and robust estimator method.
509      *
510      * @param points        2D points to estimate a conic.
511      * @param qualityScores quality scores corresponding to each provided point.
512      * @param method        method of a robust estimator algorithm to estimate the best
513      *                      conic.
514      * @return an instance of a conic robust estimator.
515      * @throws IllegalArgumentException if provided list of points don't have
516      *                                  the same size as the list of provided quality scores, or it their size
517      *                                  is not greater or equal than MINIMUM_SIZE.
518      */
519     public static ConicRobustEstimator create(
520             final List<Point2D> points, final double[] qualityScores, final RobustEstimatorMethod method) {
521         return switch (method) {
522             case LMEDS -> new LMedSConicRobustEstimator(points);
523             case MSAC -> new MSACConicRobustEstimator(points);
524             case PROSAC -> new PROSACConicRobustEstimator(points, qualityScores);
525             case PROMEDS -> new PROMedSConicRobustEstimator(points, qualityScores);
526             default -> new RANSACConicRobustEstimator(points);
527         };
528     }
529 
530     /**
531      * Creates a conic robust estimator based on 2D point samples and using
532      * provided listener.
533      *
534      * @param listener      listener to be notified of events such as when estimation
535      *                      starts, ends or its progress significantly changes.
536      * @param qualityScores quality scores corresponding to each provided point.
537      * @param method        method of a robust estimator algorithm to estimate the best
538      *                      conic.
539      * @return an instance of a conic robust estimator.
540      * @throws IllegalArgumentException if provided quality scores length is
541      *                                  smaller than MINIMUM_SIZE (i.e. 5 points).
542      */
543     public static ConicRobustEstimator create(
544             final ConicRobustEstimatorListener listener, final double[] qualityScores,
545             final RobustEstimatorMethod method) {
546         return switch (method) {
547             case LMEDS -> new LMedSConicRobustEstimator(listener);
548             case MSAC -> new MSACConicRobustEstimator(listener);
549             case PROSAC -> new PROSACConicRobustEstimator(listener, qualityScores);
550             case PROMEDS -> new PROMedSConicRobustEstimator(listener, qualityScores);
551             default -> new RANSACConicRobustEstimator(listener);
552         };
553     }
554 
555     /**
556      * Creates a conic robust estimator based on 2D point samples and using
557      * provided listener and points.
558      *
559      * @param listener      listener to be notified of events such as when estimation
560      *                      starts, ends or its progress significantly changes.
561      * @param points        2D points to estimate a conic.
562      * @param qualityScores quality scores corresponding to each provided point.
563      * @param method        method of a robust estimator algorithm to estimate the best
564      *                      conic.
565      * @return an instance of a conic robust estimator.
566      * @throws IllegalArgumentException if provided list of points don't have
567      *                                  the same size as the list of provided quality scores, or it their size
568      *                                  is not greater or equal than MINIMUM_SIZE.
569      */
570     public static ConicRobustEstimator create(
571             final ConicRobustEstimatorListener listener, final List<Point2D> points, final double[] qualityScores,
572             final RobustEstimatorMethod method) {
573         return switch (method) {
574             case LMEDS -> new LMedSConicRobustEstimator(listener, points);
575             case MSAC -> new MSACConicRobustEstimator(listener, points);
576             case PROSAC -> new PROSACConicRobustEstimator(listener, points, qualityScores);
577             case PROMEDS -> new PROMedSConicRobustEstimator(listener, points, qualityScores);
578             default -> new RANSACConicRobustEstimator(listener, points);
579         };
580     }
581 
582     /**
583      * Creates a conic robust estimator based on 2D point samples and using
584      * default robust estimator method.
585      *
586      * @return an instance of a conic robust estimator.
587      */
588     public static ConicRobustEstimator create() {
589         return create(DEFAULT_ROBUST_METHOD);
590     }
591 
592     /**
593      * Creates a conic robust estimator based on 2D point samples and using
594      * provided points and default robust estimator method.
595      *
596      * @param points 2D points to estimate a conic.
597      * @return an instance of a conic robust estimator.
598      * @throws IllegalArgumentException if provided list of points don't have a
599      *                                  size greater or equal than MINIMUM_SIZE.
600      */
601     public static ConicRobustEstimator create(final List<Point2D> points) {
602         return create(points, DEFAULT_ROBUST_METHOD);
603     }
604 
605     /**
606      * Creates a conic robust estimator based on 2D point samples and using
607      * provided listener and default robust estimator method.
608      *
609      * @param listener listener to be notified of events such as when estimation
610      *                 starts, ends or its progress significantly changes.
611      * @return an instance of a conic robust estimator.
612      */
613     public static ConicRobustEstimator create(final ConicRobustEstimatorListener listener) {
614         return create(listener, DEFAULT_ROBUST_METHOD);
615     }
616 
617     /**
618      * Creates a conic robust estimator based on 2D point samples and using
619      * provided listener and points and default robust estimator method.
620      *
621      * @param listener listener to be notified of events such as when estimation
622      *                 starts, ends or its progress significantly changes.
623      * @param points   2D points to estimate a conic.
624      * @return an instance of a conic robust estimator.
625      * @throws IllegalArgumentException if provided list of points don't have a
626      *                                  size greater or equal than MINIMUM_SIZE.
627      */
628     public static ConicRobustEstimator create(final ConicRobustEstimatorListener listener, final List<Point2D> points) {
629         return create(listener, points, DEFAULT_ROBUST_METHOD);
630     }
631 
632     /**
633      * Creates a conic robust estimator based on 2D point samples and using
634      * default robust estimator method.
635      *
636      * @param qualityScores quality scores corresponding to each provided point
637      * @return an instance of a conic robust estimator.
638      * @throws IllegalArgumentException if provided quality scores length is
639      *                                  smaller than MINIMUM_SIZE (i.e. 5 points).
640      */
641     public static ConicRobustEstimator create(final double[] qualityScores) {
642         return create(qualityScores, DEFAULT_ROBUST_METHOD);
643     }
644 
645     /**
646      * Creates a conic robust estimator based on 2D point samples and using
647      * provided points and default estimator method.
648      *
649      * @param points        2D points to estimate a conic.
650      * @param qualityScores quality scores corresponding to each provided point
651      * @return an instance of a conic robust estimator.
652      * @throws IllegalArgumentException if provided list of points don't have
653      *                                  the same size as the list of provided quality scores, or if their size
654      *                                  is not greater or equal than MINIMUM_SIZE.
655      */
656     public static ConicRobustEstimator create(final List<Point2D> points, final double[] qualityScores) {
657         return create(points, qualityScores, DEFAULT_ROBUST_METHOD);
658     }
659 
660     /**
661      * Creates a conic robust estimator based on 2D point samples and using
662      * provided listener and default estimator method.
663      *
664      * @param listener      listener to be notified of events such as when estimation
665      *                      starts, ends or its progress significantly changes.
666      * @param qualityScores quality scores corresponding to each provided point
667      * @return an instance of a conic robust estimator.
668      * @throws IllegalArgumentException if provided quality scores length is
669      *                                  smaller than MINIMUM_SIZE (i.e. 5 points).
670      */
671     public static ConicRobustEstimator create(
672             final ConicRobustEstimatorListener listener, final double[] qualityScores) {
673         return create(listener, qualityScores, DEFAULT_ROBUST_METHOD);
674     }
675 
676     /**
677      * Creates a conic robust estimator based on 2D point samples and using
678      * provided listener and points and default estimator method.
679      *
680      * @param listener      listener to be notified of events such as when estimation
681      *                      starts, ends or its progress significantly changes.
682      * @param points        2D points to estimate a conic.
683      * @param qualityScores quality scores corresponding to each provided point
684      * @return an instance of a conic robust estimator.
685      * @throws IllegalArgumentException if provided list of points don't have
686      *                                  the same size as the list of provided quality scores, or if their size
687      *                                  is not greater or equal than MINIMUM_SIZE.
688      */
689     public static ConicRobustEstimator create(
690             final ConicRobustEstimatorListener listener, final List<Point2D> points, final double[] qualityScores) {
691         return create(listener, points, qualityScores, DEFAULT_ROBUST_METHOD);
692     }
693 
694     /**
695      * Estimates a conic using a robust estimator and the best set of 2D points
696      * that fit into the locus of the estimated conic found using the robust
697      * estimator.
698      *
699      * @return a conic.
700      * @throws LockedException          if robust estimator is locked because an
701      *                                  estimation is already in progress.
702      * @throws NotReadyException        if provided input data is not enough to start
703      *                                  the estimation.
704      * @throws RobustEstimatorException if estimation fails for any reason
705      *                                  (i.e. numerical instability, no solution available, etc).
706      */
707     public abstract Conic estimate() throws LockedException, NotReadyException, RobustEstimatorException;
708 
709     /**
710      * Returns method being used for robust estimation.
711      *
712      * @return method being used for robust estimation.
713      */
714     public abstract RobustEstimatorMethod getMethod();
715 
716     /**
717      * Internal method to set lists of points to be used to estimate a conic.
718      * This method does not check whether estimator is locked or not.
719      *
720      * @param points list of points to be used to estimate a conic.
721      * @throws IllegalArgumentException if provided list of points doesn't have
722      *                                  a size greater or equal than MINIMUM_SIZE.
723      */
724     private void internalSetPoints(final List<Point2D> points) {
725         if (points.size() < MINIMUM_SIZE) {
726             throw new IllegalArgumentException();
727         }
728         this.points = points;
729     }
730 
731     /**
732      * Computes the residual between a conic and a point.
733      *
734      * @param c     a conic.
735      * @param point a 2D point.
736      * @return residual.
737      */
738     protected double residual(final Conic c, final Point2D point) {
739         c.normalize();
740         try {
741             if (testC == null) {
742                 testC = c.asMatrix();
743             } else {
744                 c.asMatrix(testC);
745             }
746 
747             if (testPoint == null) {
748                 testPoint = new Matrix(Point2D.POINT2D_HOMOGENEOUS_COORDINATES_LENGTH, 1);
749             }
750             point.normalize();
751             testPoint.setElementAt(0, 0, point.getHomX());
752             testPoint.setElementAt(1, 0, point.getHomY());
753             testPoint.setElementAt(2, 0, point.getHomW());
754             final var locusMatrix = testPoint.transposeAndReturnNew();
755             locusMatrix.multiply(testC);
756             locusMatrix.multiply(testPoint);
757             return Math.abs(locusMatrix.getElementAt(0, 0));
758         } catch (final AlgebraException e) {
759             return Double.MAX_VALUE;
760         }
761     }
762 }