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