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 }