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