1 /*
2 * Copyright (C) 2017 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.geometry.CoordinatesType;
19 import com.irurueta.geometry.EuclideanTransformation3D;
20 import com.irurueta.geometry.Point3D;
21 import com.irurueta.numerical.robust.PROSACRobustEstimator;
22 import com.irurueta.numerical.robust.PROSACRobustEstimatorListener;
23 import com.irurueta.numerical.robust.RobustEstimator;
24 import com.irurueta.numerical.robust.RobustEstimatorException;
25 import com.irurueta.numerical.robust.RobustEstimatorMethod;
26
27 import java.util.ArrayList;
28 import java.util.List;
29
30 /**
31 * Finds the best Euclidean 3D transformation for provided collections of
32 * matched 3D points using PROSAC algorithm.
33 */
34 @SuppressWarnings("DuplicatedCode")
35 public class PROSACEuclideanTransformation3DRobustEstimator extends EuclideanTransformation3DRobustEstimator {
36
37 /**
38 * Constant defining default threshold to determine whether points are
39 * inliers or not.
40 * By default, 1.0 is considered a good value for cases where measures are
41 * done on pixels, since typically the minimum resolution is 1 pixel.
42 */
43 public static final double DEFAULT_THRESHOLD = 1.0;
44
45 /**
46 * Minimum value that can be set as threshold.
47 * Threshold must be strictly greater than 0.0.
48 */
49 public static final double MIN_THRESHOLD = 0.0;
50
51 /**
52 * Indicates that by default inliers will only be computed but not kept.
53 */
54 public static final boolean DEFAULT_COMPUTE_AND_KEEP_INLIERS = false;
55
56 /**
57 * Indicates that by default residuals will only be computed but not kept.
58 */
59 public static final boolean DEFAULT_COMPUTE_AND_KEEP_RESIDUALS = false;
60
61 /**
62 * Threshold to determine whether points are inliers or not when testing
63 * possible estimation solutions.
64 * The threshold refers to the amount of error (i.e. distance) a possible
65 * solution has on a matched pair of points.
66 */
67 private double threshold;
68
69 /**
70 * Quality scores corresponding to each pair of matched points.
71 * The larger the score value the better the quality of the matching.
72 */
73 private double[] qualityScores;
74
75 /**
76 * Indicates whether inliers must be computed and kept.
77 */
78 private boolean computeAndKeepInliers;
79
80 /**
81 * Indicates whether residuals must be computed and kept.
82 */
83 private boolean computeAndKeepResiduals;
84
85 /**
86 * Constructor.
87 */
88 public PROSACEuclideanTransformation3DRobustEstimator() {
89 super();
90 threshold = DEFAULT_THRESHOLD;
91 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
92 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
93 }
94
95 /**
96 * Constructor with lists of points to be used to estimate an Euclidean 2D
97 * transformation.
98 * Points in the list located at the same position are considered to be
99 * matched. Hence, both lists must have the same size, and their size must
100 * be greater or equal than MINIMUM_SIZE.
101 *
102 * @param inputPoints list of input points to be used to estimate an
103 * Euclidean 3D transformation.
104 * @param outputPoints list of output points to be used to estimate an
105 * Euclidean 3D transformation.
106 * @throws IllegalArgumentException if provided lists of points don't have
107 * the same size or their size is smaller than MINIMUM_SIZE.
108 */
109 public PROSACEuclideanTransformation3DRobustEstimator(
110 final List<Point3D> inputPoints, final List<Point3D> outputPoints) {
111 super(inputPoints, outputPoints);
112 threshold = DEFAULT_THRESHOLD;
113 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
114 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
115 }
116
117 /**
118 * Constructor.
119 *
120 * @param listener listener to be notified of events such as when estimation
121 * starts, ends or its progress significantly changes.
122 */
123 public PROSACEuclideanTransformation3DRobustEstimator(
124 final EuclideanTransformation3DRobustEstimatorListener listener) {
125 super(listener);
126 threshold = DEFAULT_THRESHOLD;
127 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
128 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
129 }
130
131 /**
132 * Constructor with listener and lists of points to be used to estimate an
133 * Euclidean 3D transformation.
134 * Points in the list located at the same position are considered to be
135 * matched. Hence, both lists must have the same size, and their size must
136 * be greater or equal than MINIMUM_SIZE.
137 *
138 * @param listener listener to be notified of events such as when estimation
139 * stars, ends or its progress significantly changes.
140 * @param inputPoints list of input points to be used to estimate an
141 * affine 3D transformation.
142 * @param outputPoints list of output points to be used to estimate an
143 * affine 3D transformation.
144 * @throws IllegalArgumentException if provided lists of points don't have
145 * the same size or their size is smaller than MINIMUM_SIZE.
146 */
147 public PROSACEuclideanTransformation3DRobustEstimator(
148 final EuclideanTransformation3DRobustEstimatorListener listener,
149 final List<Point3D> inputPoints, final List<Point3D> outputPoints) {
150 super(listener, inputPoints, outputPoints);
151 threshold = DEFAULT_THRESHOLD;
152 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
153 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
154 }
155
156 /**
157 * Constructor.
158 *
159 * @param qualityScores quality scores corresponding to each pair of matched
160 * points.
161 * @throws IllegalArgumentException if provided quality scores length is
162 * smaller than MINIMUM_SIZE (i.e. 3 samples).
163 */
164 public PROSACEuclideanTransformation3DRobustEstimator(final double[] qualityScores) {
165 super();
166 threshold = DEFAULT_THRESHOLD;
167 internalSetQualityScores(qualityScores);
168 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
169 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
170 }
171
172 /**
173 * Constructor with lists of points to be used to estimate an Euclidean 3D
174 * transformation.
175 * Points in the list located at the same position are considered to be
176 * matched. Hence, both lists must have the same size, and their size must
177 * be greater or equal than MINIMUM_SIZE.
178 *
179 * @param inputPoints list of input points to be used to estimate an
180 * affine 3D transformation.
181 * @param outputPoints list of output points to be used to estimate an
182 * affine 3D transformation.
183 * @param qualityScores quality scores corresponding to each pair of matched
184 * points.
185 * @throws IllegalArgumentException if provided lists of points and array
186 * of quality scores don't have the same size or their size is smaller than
187 * MINIMUM_SIZE.
188 */
189 public PROSACEuclideanTransformation3DRobustEstimator(
190 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores) {
191 super(inputPoints, outputPoints);
192
193 if (qualityScores.length != inputPoints.size()) {
194 throw new IllegalArgumentException();
195 }
196
197 threshold = DEFAULT_THRESHOLD;
198 internalSetQualityScores(qualityScores);
199 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
200 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
201 }
202
203 /**
204 * Constructor.
205 *
206 * @param listener listener to be notified of events such as when estimation
207 * starts, ends or its progress significantly changes.
208 * @param qualityScores quality scores corresponding to each pair of matched
209 * points.
210 * @throws IllegalArgumentException if provided quality scores length is
211 * smaller than MINIMUM_SIZE (i.e. 3 samples).
212 */
213 public PROSACEuclideanTransformation3DRobustEstimator(
214 final EuclideanTransformation3DRobustEstimatorListener listener, final double[] qualityScores) {
215 super(listener);
216 threshold = DEFAULT_THRESHOLD;
217 internalSetQualityScores(qualityScores);
218 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
219 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
220 }
221
222 /**
223 * Constructor with listener and lists of points to be used to estimate an
224 * Euclidean 3D transformation.
225 * Points in the list located at the same position are considered to be
226 * matched. Hence, both lists must have the same size, and their size must
227 * be greater or equal than MINIMUM_SIZE.
228 *
229 * @param listener listener to be notified of events such as when estimation
230 * stars, ends or its progress significantly changes.
231 * @param inputPoints list of input points to be used to estimate an
232 * affine 3D transformation.
233 * @param outputPoints list of output points to be used to estimate an
234 * affine 3D transformation.
235 * @param qualityScores quality scores corresponding to each pair of matched
236 * points.
237 * @throws IllegalArgumentException if provided lists of points don't have
238 * the same size or their size is smaller than MINIMUM_SIZE.
239 */
240 public PROSACEuclideanTransformation3DRobustEstimator(
241 final EuclideanTransformation3DRobustEstimatorListener listener,
242 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores) {
243 super(listener, inputPoints, outputPoints);
244
245 if (qualityScores.length != inputPoints.size()) {
246 throw new IllegalArgumentException();
247 }
248
249 threshold = DEFAULT_THRESHOLD;
250 internalSetQualityScores(qualityScores);
251 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
252 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
253 }
254
255 /**
256 * Constructor.
257 *
258 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
259 */
260 public PROSACEuclideanTransformation3DRobustEstimator(
261 final boolean weakMinimumSizeAllowed) {
262 super(weakMinimumSizeAllowed);
263 threshold = DEFAULT_THRESHOLD;
264 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
265 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
266 }
267
268 /**
269 * Constructor with lists of points to be used to estimate an Euclidean 2D
270 * transformation.
271 * Points in the list located at the same position are considered to be
272 * matched. Hence, both lists must have the same size, and their size must
273 * be greater or equal than MINIMUM_SIZE.
274 *
275 * @param inputPoints list of input points to be used to estimate an
276 * Euclidean 3D transformation.
277 * @param outputPoints list of output points to be used to estimate an
278 * Euclidean 3D transformation.
279 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
280 * @throws IllegalArgumentException if provided lists of points don't have
281 * the same size or their size is smaller than MINIMUM_SIZE.
282 */
283 public PROSACEuclideanTransformation3DRobustEstimator(
284 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final boolean weakMinimumSizeAllowed) {
285 super(inputPoints, outputPoints, weakMinimumSizeAllowed);
286 threshold = DEFAULT_THRESHOLD;
287 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
288 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
289 }
290
291 /**
292 * Constructor.
293 *
294 * @param listener listener to be notified of events such as when estimation
295 * starts, ends or its progress significantly changes.
296 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
297 */
298 public PROSACEuclideanTransformation3DRobustEstimator(
299 final EuclideanTransformation3DRobustEstimatorListener listener, final boolean weakMinimumSizeAllowed) {
300 super(listener, weakMinimumSizeAllowed);
301 threshold = DEFAULT_THRESHOLD;
302 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
303 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
304 }
305
306 /**
307 * Constructor with listener and lists of points to be used to estimate an
308 * Euclidean 3D transformation.
309 * Points in the list located at the same position are considered to be
310 * matched. Hence, both lists must have the same size, and their size must
311 * be greater or equal than MINIMUM_SIZE.
312 *
313 * @param listener listener to be notified of events such as when estimation
314 * stars, ends or its progress significantly changes.
315 * @param inputPoints list of input points to be used to estimate an
316 * affine 3D transformation.
317 * @param outputPoints list of output points to be used to estimate an
318 * affine 3D transformation.
319 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
320 * @throws IllegalArgumentException if provided lists of points don't have
321 * the same size or their size is smaller than MINIMUM_SIZE.
322 */
323 public PROSACEuclideanTransformation3DRobustEstimator(
324 final EuclideanTransformation3DRobustEstimatorListener listener,
325 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final boolean weakMinimumSizeAllowed) {
326 super(listener, inputPoints, outputPoints, weakMinimumSizeAllowed);
327 threshold = DEFAULT_THRESHOLD;
328 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
329 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
330 }
331
332 /**
333 * Constructor.
334 *
335 * @param qualityScores quality scores corresponding to each pair of matched
336 * points.
337 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
338 * @throws IllegalArgumentException if provided quality scores length is
339 * smaller than MINIMUM_SIZE (i.e. 3 samples).
340 */
341 public PROSACEuclideanTransformation3DRobustEstimator(
342 final double[] qualityScores, final boolean weakMinimumSizeAllowed) {
343 super(weakMinimumSizeAllowed);
344 threshold = DEFAULT_THRESHOLD;
345 internalSetQualityScores(qualityScores);
346 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
347 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
348 }
349
350 /**
351 * Constructor with lists of points to be used to estimate an Euclidean 3D
352 * transformation.
353 * Points in the list located at the same position are considered to be
354 * matched. Hence, both lists must have the same size, and their size must
355 * be greater or equal than MINIMUM_SIZE.
356 *
357 * @param inputPoints list of input points to be used to estimate an
358 * affine 3D transformation.
359 * @param outputPoints list of output points to be used to estimate an
360 * affine 3D transformation.
361 * @param qualityScores quality scores corresponding to each pair of matched
362 * points.
363 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
364 * @throws IllegalArgumentException if provided lists of points and array
365 * of quality scores don't have the same size or their size is smaller than
366 * MINIMUM_SIZE.
367 */
368 public PROSACEuclideanTransformation3DRobustEstimator(
369 final List<Point3D> inputPoints, final List<Point3D> outputPoints, final double[] qualityScores,
370 final boolean weakMinimumSizeAllowed) {
371 super(inputPoints, outputPoints, weakMinimumSizeAllowed);
372
373 if (qualityScores.length != inputPoints.size()) {
374 throw new IllegalArgumentException();
375 }
376
377 threshold = DEFAULT_THRESHOLD;
378 internalSetQualityScores(qualityScores);
379 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
380 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
381 }
382
383 /**
384 * Constructor.
385 *
386 * @param listener listener to be notified of events such as when estimation
387 * starts, ends or its progress significantly changes.
388 * @param qualityScores quality scores corresponding to each pair of matched
389 * points.
390 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
391 * @throws IllegalArgumentException if provided quality scores length is
392 * smaller than MINIMUM_SIZE (i.e. 3 samples).
393 */
394 public PROSACEuclideanTransformation3DRobustEstimator(
395 final EuclideanTransformation3DRobustEstimatorListener listener, final double[] qualityScores,
396 final boolean weakMinimumSizeAllowed) {
397 super(listener, weakMinimumSizeAllowed);
398 threshold = DEFAULT_THRESHOLD;
399 internalSetQualityScores(qualityScores);
400 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
401 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
402 }
403
404 /**
405 * Constructor with listener and lists of points to be used to estimate an
406 * Euclidean 3D transformation.
407 * Points in the list located at the same position are considered to be
408 * matched. Hence, both lists must have the same size, and their size must
409 * be greater or equal than MINIMUM_SIZE.
410 *
411 * @param listener listener to be notified of events such as when estimation
412 * stars, ends or its progress significantly changes.
413 * @param inputPoints list of input points to be used to estimate an
414 * affine 3D transformation.
415 * @param outputPoints list of output points to be used to estimate an
416 * affine 3D transformation.
417 * @param qualityScores quality scores corresponding to each pair of matched
418 * points.
419 * @param weakMinimumSizeAllowed true allows 3 points, false requires 4.
420 * @throws IllegalArgumentException if provided lists of points don't have
421 * the same size or their size is smaller than MINIMUM_SIZE.
422 */
423 public PROSACEuclideanTransformation3DRobustEstimator(
424 final EuclideanTransformation3DRobustEstimatorListener listener, final List<Point3D> inputPoints,
425 final List<Point3D> outputPoints, final double[] qualityScores, final boolean weakMinimumSizeAllowed) {
426 super(listener, inputPoints, outputPoints, weakMinimumSizeAllowed);
427
428 if (qualityScores.length != inputPoints.size()) {
429 throw new IllegalArgumentException();
430 }
431
432 threshold = DEFAULT_THRESHOLD;
433 internalSetQualityScores(qualityScores);
434 computeAndKeepInliers = DEFAULT_COMPUTE_AND_KEEP_INLIERS;
435 computeAndKeepResiduals = DEFAULT_COMPUTE_AND_KEEP_RESIDUALS;
436 }
437
438 /**
439 * Returns threshold to determine whether points are inliers or not when
440 * testing possible estimation solutions.
441 * The threshold refers to the amount of error (i.e. Euclidean distance) a
442 * possible solution has on a matched pair of points.
443 *
444 * @return threshold to determine whether points are inliers or not when
445 * testing possible estimation solutions.
446 */
447 public double getThreshold() {
448 return threshold;
449 }
450
451 /**
452 * Sets threshold to determine whether points are inliers or not when
453 * testing possible estimation solutions.
454 * The threshold refers to the amount of error (i.e. Euclidean distance) a
455 * possible solution has on a matched pair of points.
456 *
457 * @param threshold threshold to determine whether points are inliers or not
458 * when testing possible estimation solutions.
459 * @throws IllegalArgumentException if provided values is equal or less than
460 * zero.
461 * @throws LockedException if robust estimator is locked because an
462 * estimation is already in progress.
463 */
464 public void setThreshold(final double threshold) throws LockedException {
465 if (isLocked()) {
466 throw new LockedException();
467 }
468 if (threshold <= MIN_THRESHOLD) {
469 throw new IllegalArgumentException();
470 }
471 this.threshold = threshold;
472 }
473
474 /**
475 * Returns quality scores corresponding to each pair of matched points.
476 * The larger the score value the better the quality of the matching.
477 *
478 * @return quality scores corresponding to each pair of matched points.
479 */
480 @Override
481 public double[] getQualityScores() {
482 return qualityScores;
483 }
484
485 /**
486 * Sets quality scores corresponding to each pair of matched points.
487 * The larger the score value the better the quality of the matching.
488 *
489 * @param qualityScores quality scores corresponding to each pair of matched
490 * points.
491 * @throws LockedException if robust estimator is locked because an
492 * estimation is already in progress.
493 * @throws IllegalArgumentException if provided quality scores length is
494 * smaller than MINIMUM_SIZE (i.e. 3 samples).
495 */
496 @Override
497 public void setQualityScores(final double[] qualityScores) throws LockedException {
498 if (isLocked()) {
499 throw new LockedException();
500 }
501 internalSetQualityScores(qualityScores);
502 }
503
504 /**
505 * Indicates if estimator is ready to start the Euclidean 3D transformation
506 * estimation.
507 * This is true when input data (i.e. lists of matched points and quality
508 * scores) are provided and a minimum of MINIMUM_SIZE points are available.
509 *
510 * @return true if estimator is ready, false otherwise.
511 */
512 @Override
513 public boolean isReady() {
514 return super.isReady() && qualityScores != null && qualityScores.length == inputPoints.size();
515 }
516
517 /**
518 * Indicates whether inliers must be computed and kept.
519 *
520 * @return true if inliers must be computed and kept, false if inliers
521 * only need to be computed but not kept.
522 */
523 public boolean isComputeAndKeepInliersEnabled() {
524 return computeAndKeepInliers;
525 }
526
527 /**
528 * Specifies whether inliers must be computed and kept.
529 *
530 * @param computeAndKeepInliers true if inliers must be computed and kept,
531 * false if inliers only need to be computed but not kept.
532 * @throws LockedException if estimator is locked.
533 */
534 public void setComputeAndKeepInliersEnabled(final boolean computeAndKeepInliers) throws LockedException {
535 if (isLocked()) {
536 throw new LockedException();
537 }
538 this.computeAndKeepInliers = computeAndKeepInliers;
539 }
540
541 /**
542 * Indicates whether residuals must be computed and kept.
543 *
544 * @return true if residuals must be computed and kept, false if residuals
545 * only need to be computed but not kept.
546 */
547 public boolean isComputeAndKeepResidualsEnabled() {
548 return computeAndKeepResiduals;
549 }
550
551 /**
552 * Specifies whether residuals must be computed and kept.
553 *
554 * @param computeAndKeepResiduals true if residuals must be computed and
555 * kept, false if residuals only need to be computed but not kept.
556 * @throws LockedException if estimator is locked.
557 */
558 public void setComputeAndKeepResidualsEnabled(final boolean computeAndKeepResiduals) throws LockedException {
559 if (isLocked()) {
560 throw new LockedException();
561 }
562 this.computeAndKeepResiduals = computeAndKeepResiduals;
563 }
564
565 /**
566 * Estimates an Euclidean 3D transformation using a robust estimator and
567 * the best set of matched 3D point correspondences found using the robust
568 * estimator.
569 *
570 * @return an affine 3D transformation.
571 * @throws LockedException if robust estimator is locked because an
572 * estimation is already in progress.
573 * @throws NotReadyException if provided input data is not enough to start
574 * the estimation.
575 * @throws RobustEstimatorException if estimation fails for any reason
576 * (i.e. numerical instability, no solution available, etc).
577 */
578 @Override
579 public EuclideanTransformation3D estimate() throws LockedException, NotReadyException, RobustEstimatorException {
580 if (isLocked()) {
581 throw new LockedException();
582 }
583 if (!isReady()) {
584 throw new NotReadyException();
585 }
586
587 final var innerEstimator = new PROSACRobustEstimator<>(
588 new PROSACRobustEstimatorListener<EuclideanTransformation3D>() {
589
590 // point to be reused when computing residuals
591 private final Point3D testPoint = Point3D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
592
593 private final EuclideanTransformation3DEstimator nonRobustEstimator =
594 new EuclideanTransformation3DEstimator(isWeakMinimumSizeAllowed());
595
596 private final List<Point3D> subsetInputPoints = new ArrayList<>();
597 private final List<Point3D> subsetOutputPoints = new ArrayList<>();
598
599 @Override
600 public double getThreshold() {
601 return threshold;
602 }
603
604 @Override
605 public int getTotalSamples() {
606 return inputPoints.size();
607 }
608
609 @Override
610 public int getSubsetSize() {
611 return nonRobustEstimator.getMinimumPoints();
612 }
613
614 @Override
615 public void estimatePreliminarSolutions(
616 final int[] samplesIndices, final List<EuclideanTransformation3D> solutions) {
617 subsetInputPoints.clear();
618 subsetOutputPoints.clear();
619 for (final var samplesIndex : samplesIndices) {
620 subsetInputPoints.add(inputPoints.get(samplesIndex));
621 subsetOutputPoints.add(outputPoints.get(samplesIndex));
622 }
623
624 try {
625 nonRobustEstimator.setPoints(subsetInputPoints, subsetOutputPoints);
626 solutions.add(nonRobustEstimator.estimate());
627 } catch (final Exception e) {
628 // if points are coincident, no solution is added
629 }
630 }
631
632 @Override
633 public double computeResidual(final EuclideanTransformation3D currentEstimation, final int i) {
634 final var inputPoint = inputPoints.get(i);
635 final var outputPoint = outputPoints.get(i);
636
637 // transform input point and store result in mTestPoint
638 currentEstimation.transform(inputPoint, testPoint);
639
640 return outputPoint.distanceTo(testPoint);
641 }
642
643 @Override
644 public boolean isReady() {
645 return PROSACEuclideanTransformation3DRobustEstimator.this.isReady();
646 }
647
648 @Override
649 public void onEstimateStart(final RobustEstimator<EuclideanTransformation3D> estimator) {
650 if (listener != null) {
651 listener.onEstimateStart(PROSACEuclideanTransformation3DRobustEstimator.this);
652 }
653 }
654
655 @Override
656 public void onEstimateEnd(final RobustEstimator<EuclideanTransformation3D> estimator) {
657 if (listener != null) {
658 listener.onEstimateEnd(PROSACEuclideanTransformation3DRobustEstimator.this);
659 }
660 }
661
662 @Override
663 public void onEstimateNextIteration(
664 final RobustEstimator<EuclideanTransformation3D> estimator, final int iteration) {
665 if (listener != null) {
666 listener.onEstimateNextIteration(
667 PROSACEuclideanTransformation3DRobustEstimator.this, iteration);
668 }
669 }
670
671 @Override
672 public void onEstimateProgressChange(
673 final RobustEstimator<EuclideanTransformation3D> estimator, final float progress) {
674 if (listener != null) {
675 listener.onEstimateProgressChange(
676 PROSACEuclideanTransformation3DRobustEstimator.this, progress);
677 }
678 }
679
680 @Override
681 public double[] getQualityScores() {
682 return qualityScores;
683 }
684 });
685
686 try {
687 locked = true;
688 inliersData = null;
689 innerEstimator.setComputeAndKeepInliersEnabled(computeAndKeepInliers || refineResult);
690 innerEstimator.setComputeAndKeepResidualsEnabled(computeAndKeepResiduals || refineResult);
691 innerEstimator.setConfidence(confidence);
692 innerEstimator.setMaxIterations(maxIterations);
693 innerEstimator.setProgressDelta(progressDelta);
694 final var transformation = innerEstimator.estimate();
695 inliersData = innerEstimator.getInliersData();
696 return attemptRefine(transformation);
697 } catch (final com.irurueta.numerical.LockedException e) {
698 throw new LockedException(e);
699 } catch (final com.irurueta.numerical.NotReadyException e) {
700 throw new NotReadyException(e);
701 } finally {
702 locked = false;
703 }
704 }
705
706 /**
707 * Returns method being used for robust estimation.
708 *
709 * @return method being used for robust estimation.
710 */
711 @Override
712 public RobustEstimatorMethod getMethod() {
713 return RobustEstimatorMethod.PROSAC;
714 }
715
716 /**
717 * Gets standard deviation used for Levenberg-Marquardt fitting during
718 * refinement.
719 * Returned value gives an indication of how much variance each residual
720 * has.
721 * Typically, this value is related to the threshold used on each robust
722 * estimation, since residuals of found inliers are within the range of
723 * such threshold.
724 *
725 * @return standard deviation used for refinement.
726 */
727 @Override
728 protected double getRefinementStandardDeviation() {
729 return threshold;
730 }
731
732 /**
733 * Sets quality scores corresponding to each pair of matched points.
734 * This method is used internally and does not check whether instance is
735 * locked or not.
736 *
737 * @param qualityScores quality scores to be set.
738 * @throws IllegalArgumentException if provided quality scores length is
739 * smaller than MINIMUM_SIZE.
740 */
741 private void internalSetQualityScores(final double[] qualityScores) {
742 if (qualityScores.length < getMinimumPoints()) {
743 throw new IllegalArgumentException();
744 }
745
746 this.qualityScores = qualityScores;
747 }
748 }