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.geometry.AffineTransformation2D;
19 import com.irurueta.geometry.Line2D;
20 import com.irurueta.geometry.refiners.LineCorrespondenceAffineTransformation2DRefiner;
21 import com.irurueta.numerical.robust.RobustEstimatorMethod;
22
23 import java.util.List;
24
25 /**
26 * This is an abstract class for algorithms to robustly find the best affine
27 * 2D transformation for collections of matching 2D lines.
28 * Implementations of this class should be able to detect and discard outliers
29 * in order to find the best solution.
30 */
31 public abstract class LineCorrespondenceAffineTransformation2DRobustEstimator
32 extends AffineTransformation2DRobustEstimator {
33
34 /**
35 * Default robust estimator method when none is provided.
36 */
37 public static final RobustEstimatorMethod DEFAULT_ROBUST_METHOD = RobustEstimatorMethod.PROMEDS;
38
39 /**
40 * List of lines to be used to estimate an affine 2D transformation.
41 * Each line in the list of input lines must be matched with the
42 * corresponding line in the list of output lines located at the same
43 * position. Hence, both input lines and output lines must have the
44 * same size, and their size must be greater or equal than MINIMUM_SIZE.
45 */
46 protected List<Line2D> inputLines;
47
48 /**
49 * List of lines to be used to estimate an affine 2D transformation.
50 * Each point in the list of output lines must be matched with the
51 * corresponding line in the list of input lines located at the same
52 * position. Hence, both input lines and output lines must have the
53 * same size, and their size must be greater or equal than MINIMUM_SIZE.
54 */
55 protected List<Line2D> outputLines;
56
57 /**
58 * Constructor.
59 */
60 protected LineCorrespondenceAffineTransformation2DRobustEstimator() {
61 super();
62 }
63
64 /**
65 * Constructor with lists of lines to be used to estimate an affine 2D
66 * transformation.
67 * Lines in the list located at the same position are considered to be
68 * matched. Hence, both lists must have the same size, and their size must
69 * be greater or equal than MINIMUM_SIZE.
70 *
71 * @param inputLines list of input lines to be used to estimate an affine 2D
72 * transformation.
73 * @param outputLines list of output lines ot be used to estimate an affine
74 * 2D transformation.
75 * @throws IllegalArgumentException if provided lists of points don't have
76 * the same size or their size is smaller than MINIMUM_SIZE.
77 */
78 protected LineCorrespondenceAffineTransformation2DRobustEstimator(
79 final List<Line2D> inputLines, final List<Line2D> outputLines) {
80 super();
81 internalSetLines(inputLines, outputLines);
82 }
83
84 /**
85 * Constructor.
86 *
87 * @param listener listener to be notified of events such as when estimation
88 * stars, ends or its progress significantly changes.
89 */
90 protected LineCorrespondenceAffineTransformation2DRobustEstimator(
91 final AffineTransformation2DRobustEstimatorListener listener) {
92 super(listener);
93 }
94
95 /**
96 * Constructor with listener and lists of lines to be used to estimate
97 * affine 2D transformation.
98 * Lines 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 listener listener to be notified of events such as when estimation
103 * starts, ends or its progress significantly changes.
104 * @param inputLines list of input lines to be used to estimate an affine
105 * 2D transformation.
106 * @param outputLines list of output lines to be used to estimate an affine
107 * 2D transformation.
108 * @throws IllegalArgumentException if provided lists of lines don't have
109 * the same size or their size is smaller than MINIMUM_SIZE.
110 */
111 protected LineCorrespondenceAffineTransformation2DRobustEstimator(
112 final AffineTransformation2DRobustEstimatorListener listener,
113 final List<Line2D> inputLines, final List<Line2D> outputLines) {
114 super(listener);
115 internalSetLines(inputLines, outputLines);
116 }
117
118 /**
119 * Returns list of input lines to be used to estimate an affine 2D
120 * transformation.
121 * Each line in the list of input lines must be matched with the
122 * corresponding line in the list of output lines located at the same
123 * position. Hence, both input lines and output lines must have the same
124 * size, and their size must be greater or equal than MINIMUM_SIZE.
125 *
126 * @return list of input lines to be used to estimate an affine 2D
127 * transformation.
128 */
129 public List<Line2D> getInputLines() {
130 return inputLines;
131 }
132
133 /**
134 * Returns list of output lines to be used to estimate an affine 2D
135 * transformation.
136 * Each line in the list of output lines must be matched with the
137 * corresponding line in the list of input lines located at the same
138 * position. Hence, both input lines and output lines must have the same
139 * size, and their size must be greater or equal than MINIMUM_SIZE.
140 *
141 * @return list of output lines to be used to estimate an affine 2D
142 * transformation.
143 */
144 public List<Line2D> getOutputLines() {
145 return outputLines;
146 }
147
148 /**
149 * Sets lists of lines to be used to estimate an affine 2D transformation.
150 * Lines in the list located at the same position are considered to be
151 * matched. Hence, both lists must have the same size, and their size must
152 * be greater or equal than MINIMUM_SIZE.
153 *
154 * @param inputLines list of input lines to be used to estimate an affine
155 * 2D transformation.
156 * @param outputLines list of output lines to be used to estimate an affine
157 * 2D transformation.
158 * @throws IllegalArgumentException if provided lists of lines don't have
159 * the same size or their size is smaller than MINIMUM_SIZE.
160 * @throws LockedException if estimator is locked because a computation is
161 * already in progress.
162 */
163 public final void setLines(final List<Line2D> inputLines, final List<Line2D> outputLines) throws LockedException {
164 if (isLocked()) {
165 throw new LockedException();
166 }
167 internalSetLines(inputLines, outputLines);
168 }
169
170 /**
171 * Indicates if estimator is ready to start the affine 2D transformation
172 * estimation.
173 * This is true when input data (i.e. lists of matched lines) are provided
174 * and a minimum of MINIMUM_SIZE lines are available.
175 *
176 * @return true if estimator is ready, false otherwise.
177 */
178 public boolean isReady() {
179 return inputLines != null && outputLines != null && inputLines.size() == outputLines.size()
180 && inputLines.size() >= MINIMUM_SIZE;
181 }
182
183 /**
184 * Returns quality scores corresponding to each pair of matched lines.
185 * The larger the score value the better the quality of the matching.
186 * This implementation always returns null.
187 * Subclasses using quality scores must implement proper behaviour.
188 *
189 * @return quality scores corresponding to each pair of matched points.
190 */
191 public double[] getQualityScores() {
192 return null;
193 }
194
195 /**
196 * Sets quality scores corresponding to each pair of matched lines.
197 * The larger the score value the better the quality of the matching.
198 * This implementation makes no action.
199 * Subclasses using quality scores must implement proper behaviour.
200 *
201 * @param qualityScores quality scores corresponding to each pair of matched
202 * points.
203 * @throws LockedException if robust estimator is locked because an
204 * estimation is already in progress.
205 * @throws IllegalArgumentException if provided quality scores length is
206 * smaller than MINIMUM_SIZE (i.e. 3 samples).
207 */
208 public void setQualityScores(final double[] qualityScores) throws LockedException {
209 }
210
211 /**
212 * Creates an affine 2D transformation estimator based on 2D line
213 * correspondences and using provided robust estimator method.
214 *
215 * @param method method of a robust estimator algorithm to estimate the
216 * best affine 2D transformation.
217 * @return an instance of affine 2D transformation estimator.
218 */
219 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(final RobustEstimatorMethod method) {
220 return switch (method) {
221 case LMEDS -> new LMedSLineCorrespondenceAffineTransformation2DRobustEstimator();
222 case MSAC -> new MSACLineCorrespondenceAffineTransformation2DRobustEstimator();
223 case PROSAC -> new PROSACLineCorrespondenceAffineTransformation2DRobustEstimator();
224 case PROMEDS -> new PROMedSLineCorrespondenceAffineTransformation2DRobustEstimator();
225 default -> new RANSACLineCorrespondenceAffineTransformation2DRobustEstimator();
226 };
227 }
228
229 /**
230 * Creates an affine 2D transformation estimator based on 2D line
231 * correspondences and using provided robust estimator method.
232 *
233 * @param inputLines list of input lines to be used to estimate an
234 * affine 2D transformation.
235 * @param outputLines list of output lines to be used to estimate an
236 * affine 2D transformation.
237 * @param method method of a robust estimator algorithm to estimate the
238 * best affine 2D transformation.
239 * @return an instance of affine 2D transformation estimator.
240 * @throws IllegalArgumentException if provided lists of lines don't have
241 * the same size or their size is smaller than MINIMUM_SIZE.
242 */
243 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
244 final List<Line2D> inputLines, final List<Line2D> outputLines, final RobustEstimatorMethod method) {
245 return switch (method) {
246 case LMEDS -> new LMedSLineCorrespondenceAffineTransformation2DRobustEstimator(inputLines, outputLines);
247 case MSAC -> new MSACLineCorrespondenceAffineTransformation2DRobustEstimator(inputLines, outputLines);
248 case PROSAC -> new PROSACLineCorrespondenceAffineTransformation2DRobustEstimator(inputLines, outputLines);
249 case PROMEDS -> new PROMedSLineCorrespondenceAffineTransformation2DRobustEstimator(inputLines, outputLines);
250 default -> new RANSACLineCorrespondenceAffineTransformation2DRobustEstimator(inputLines, outputLines);
251 };
252 }
253
254 /**
255 * Creates an affine 2D transformation estimator based on 2D line
256 * correspondences and using provided robust estimator method.
257 *
258 * @param listener listener to be notified of events such as when estimation
259 * starts, ends or its progress significantly changes.
260 * @param method method of a robust estimator algorithm to estimate the best
261 * affine 2D transformation.
262 * @return an instance of affine 2D transformation estimator.
263 */
264 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
265 final AffineTransformation2DRobustEstimatorListener listener, final RobustEstimatorMethod method) {
266 return switch (method) {
267 case LMEDS -> new LMedSLineCorrespondenceAffineTransformation2DRobustEstimator(listener);
268 case MSAC -> new MSACLineCorrespondenceAffineTransformation2DRobustEstimator(listener);
269 case PROSAC -> new PROSACLineCorrespondenceAffineTransformation2DRobustEstimator(listener);
270 case PROMEDS -> new PROMedSLineCorrespondenceAffineTransformation2DRobustEstimator(listener);
271 default -> new RANSACLineCorrespondenceAffineTransformation2DRobustEstimator(listener);
272 };
273 }
274
275 /**
276 * Creates an affine 2D transformation estimator based on 2D line
277 * correspondences and using provided robust estimator method.
278 *
279 * @param listener listener to be notified of events such as when estimation
280 * starts, ends or its progress significantly changes.
281 * @param inputLines list of input lines to be used to estimate an affine
282 * 2D transformation.
283 * @param outputLines list of output lines to be used to estimate an affine
284 * 2D transformation.
285 * @param method method of a robust estimator algorithm to estimate the best
286 * affine 2D transformation.
287 * @return an instance of affine 2D transformation estimator.
288 * @throws IllegalArgumentException if provided lists of lines don't have
289 * the same size or their size is smaller than MINIMUM_SIZE.
290 */
291 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
292 final AffineTransformation2DRobustEstimatorListener listener,
293 final List<Line2D> inputLines, final List<Line2D> outputLines, final RobustEstimatorMethod method) {
294 return switch (method) {
295 case LMEDS -> new LMedSLineCorrespondenceAffineTransformation2DRobustEstimator(
296 listener, inputLines, outputLines);
297 case MSAC -> new MSACLineCorrespondenceAffineTransformation2DRobustEstimator(
298 listener, inputLines, outputLines);
299 case PROSAC -> new PROSACLineCorrespondenceAffineTransformation2DRobustEstimator(
300 listener, inputLines, outputLines);
301 case PROMEDS -> new PROMedSLineCorrespondenceAffineTransformation2DRobustEstimator(
302 listener, inputLines, outputLines);
303 default -> new RANSACLineCorrespondenceAffineTransformation2DRobustEstimator(
304 listener, inputLines, outputLines);
305 };
306 }
307
308 /**
309 * Creates an affine 2D transformation estimator based on 2D line
310 * correspondences and using provided robust estimator method.
311 *
312 * @param qualityScores quality scores corresponding to each pair of matched
313 * points.
314 * @param method method of a robust estimator algorithm to estimate the best
315 * affine 2D transformation.
316 * @return an instance of affine 2D transformation estimator.
317 */
318 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
319 final double[] qualityScores, final RobustEstimatorMethod method) {
320 return switch (method) {
321 case LMEDS -> new LMedSLineCorrespondenceAffineTransformation2DRobustEstimator();
322 case MSAC -> new MSACLineCorrespondenceAffineTransformation2DRobustEstimator();
323 case PROSAC -> new PROSACLineCorrespondenceAffineTransformation2DRobustEstimator(qualityScores);
324 case PROMEDS -> new PROMedSLineCorrespondenceAffineTransformation2DRobustEstimator(qualityScores);
325 default -> new RANSACLineCorrespondenceAffineTransformation2DRobustEstimator();
326 };
327 }
328
329 /**
330 * Creates an affine 2D transformation estimator based on 2D line
331 * correspondences and using provided robust estimator method.
332 *
333 * @param inputLines list of input lines to be used to estimate an
334 * affine 2D transformation.
335 * @param outputLines list of output lines to be used to estimate an
336 * affine 2D transformation.
337 * @param qualityScores quality scores corresponding to each pair of matched
338 * lines.
339 * @param method method of a robust estimator algorithm to estimate the best
340 * affine 2D transformation.
341 * @return an instance of affine 2D transformation estimator.
342 * @throws IllegalArgumentException if provided lists of lines don't have
343 * the same size or their size is smaller than MINIMUM_SIZE.
344 */
345 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
346 final List<Line2D> inputLines, final List<Line2D> outputLines, final double[] qualityScores,
347 final RobustEstimatorMethod method) {
348 return switch (method) {
349 case LMEDS -> new LMedSLineCorrespondenceAffineTransformation2DRobustEstimator(inputLines, outputLines);
350 case MSAC -> new MSACLineCorrespondenceAffineTransformation2DRobustEstimator(inputLines, outputLines);
351 case PROSAC -> new PROSACLineCorrespondenceAffineTransformation2DRobustEstimator(
352 inputLines, outputLines, qualityScores);
353 case PROMEDS -> new PROMedSLineCorrespondenceAffineTransformation2DRobustEstimator(
354 inputLines, outputLines, qualityScores);
355 default -> new RANSACLineCorrespondenceAffineTransformation2DRobustEstimator(inputLines, outputLines);
356 };
357 }
358
359 /**
360 * Creates an affine 2D transformation estimator based on 2D line
361 * correspondences and using provided robust estimator method.
362 *
363 * @param listener listener to be notified of events such as when estimation
364 * starts, ends or its progress significantly changes.
365 * @param qualityScores quality scores corresponding to each pair of matched
366 * lines.
367 * @param method method of a robust estimator algorithm to estimate the best
368 * affine 2D transformation.
369 * @return an instance of affine 2D transformation estimator.
370 */
371 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
372 final AffineTransformation2DRobustEstimatorListener listener, final double[] qualityScores,
373 final RobustEstimatorMethod method) {
374 return switch (method) {
375 case LMEDS -> new LMedSLineCorrespondenceAffineTransformation2DRobustEstimator(listener);
376 case MSAC -> new MSACLineCorrespondenceAffineTransformation2DRobustEstimator(listener);
377 case PROSAC -> new PROSACLineCorrespondenceAffineTransformation2DRobustEstimator(listener, qualityScores);
378 case PROMEDS -> new PROMedSLineCorrespondenceAffineTransformation2DRobustEstimator(listener, qualityScores);
379 default -> new RANSACLineCorrespondenceAffineTransformation2DRobustEstimator(listener);
380 };
381 }
382
383 /**
384 * Creates an affine 2D transformation estimator based on 2D line
385 * correspondences and using provided robust estimator method.
386 *
387 * @param listener listener to be notified of events such as when estimation
388 * starts, ends or its progress significantly changes.
389 * @param inputLines list of input lines to be used to estimate an affine
390 * 2D transformation.
391 * @param outputLines list of output lines to be used to estimate an affine
392 * 2D transformation.
393 * @param qualityScores quality scores corresponding to each pair of matched
394 * lines.
395 * @param method method of a robust estimator algorithm to estimate the best
396 * affine 2D transformation.
397 * @return an instance of affine 2D transformation estimator.
398 * @throws IllegalArgumentException if provided lists of lines don't have
399 * the same size or their size is smaller than MINIMUM_SIZE.
400 */
401 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
402 final AffineTransformation2DRobustEstimatorListener listener,
403 final List<Line2D> inputLines, final List<Line2D> outputLines, final double[] qualityScores,
404 final RobustEstimatorMethod method) {
405 return switch (method) {
406 case LMEDS -> new LMedSLineCorrespondenceAffineTransformation2DRobustEstimator(
407 listener, inputLines, outputLines);
408 case MSAC -> new MSACLineCorrespondenceAffineTransformation2DRobustEstimator(
409 listener, inputLines, outputLines);
410 case PROSAC -> new PROSACLineCorrespondenceAffineTransformation2DRobustEstimator(
411 listener, inputLines, outputLines, qualityScores);
412 case PROMEDS -> new PROMedSLineCorrespondenceAffineTransformation2DRobustEstimator(
413 listener, inputLines, outputLines, qualityScores);
414 default -> new RANSACLineCorrespondenceAffineTransformation2DRobustEstimator(
415 listener, inputLines, outputLines);
416 };
417 }
418
419 /**
420 * Creates an affine 2D transformation estimator based on 2D line
421 * correspondences and using default robust estimator method.
422 *
423 * @return an instance of affine 2D transformation estimator.
424 */
425 public static LineCorrespondenceAffineTransformation2DRobustEstimator create() {
426 return create(DEFAULT_ROBUST_METHOD);
427 }
428
429 /**
430 * Creates an affine 2D transformation estimator based on 2D line
431 * correspondences and using default robust estimator method.
432 *
433 * @param inputLines list of input lines to be used to estimate an
434 * affine 2D transformation.
435 * @param outputLines list of output lines to be used to estimate an
436 * affine 2D transformation.
437 * @return an instance of affine 2D transformation estimator.
438 * @throws IllegalArgumentException if provided lists of lines don't have
439 * the same size or their size is smaller than MINIMUM_SIZE.
440 */
441 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
442 final List<Line2D> inputLines, final List<Line2D> outputLines) {
443 return create(inputLines, outputLines, DEFAULT_ROBUST_METHOD);
444 }
445
446 /**
447 * Creates an affine 2D transformation estimator based on 2D line
448 * correspondences and using default robust estimator method.
449 *
450 * @param listener listener to be notified of events such as when estimation
451 * starts, ends or its progress significantly changes.
452 * @return an instance of affine 2D transformation estimator.
453 */
454 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
455 final AffineTransformation2DRobustEstimatorListener listener) {
456 return create(listener, DEFAULT_ROBUST_METHOD);
457 }
458
459 /**
460 * Creates an affine 2D transformation estimator based on 2D line
461 * correspondences and using default robust estimator method.
462 *
463 * @param listener listener to be notified of events such as when estimation
464 * starts, ends or its progress significantly changes.
465 * @param inputLines list of input lines to be used to estimate an affine
466 * 2D transformation.
467 * @param outputLines list of output lines to be used to estimate an affine
468 * 2D transformation.
469 * @return an instance of affine 2D transformation estimator.
470 * @throws IllegalArgumentException if provided lists of lines don't have
471 * the same size or their size is smaller than MINIMUM_SIZE.
472 */
473 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
474 final AffineTransformation2DRobustEstimatorListener listener,
475 final List<Line2D> inputLines, final List<Line2D> outputLines) {
476 return create(listener, inputLines, outputLines, DEFAULT_ROBUST_METHOD);
477 }
478
479 /**
480 * Creates an affine 2D transformation estimator based on 2D line
481 * correspondences and using default robust estimator method.
482 *
483 * @param qualityScores quality scores corresponding to each pair of matched
484 * points.
485 * @return an instance of affine 2D transformation estimator.
486 */
487 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(final double[] qualityScores) {
488 return create(qualityScores, DEFAULT_ROBUST_METHOD);
489 }
490
491 /**
492 * Creates an affine 2D transformation estimator based on 2D line
493 * correspondences and using default robust estimator method.
494 *
495 * @param inputLines list of input lines to be used to estimate an affine
496 * 2D transformation.
497 * @param outputLines list of output lines to be used to estimate an affine
498 * 2D transformation.
499 * @param qualityScores quality scores corresponding to each pair of matched
500 * points.
501 * @return an instance of affine 2D transformation estimator.
502 * @throws IllegalArgumentException if provided lists of lines don't have
503 * the same size or their size is smaller than MINIMUM_SIZE.
504 */
505 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
506 final List<Line2D> inputLines, final List<Line2D> outputLines, final double[] qualityScores) {
507 return create(inputLines, outputLines, qualityScores, DEFAULT_ROBUST_METHOD);
508 }
509
510 /**
511 * Creates an affine 2D transformation estimator based on 2D line
512 * correspondences and using default robust estimator method.
513 *
514 * @param listener listener to be notified of events such as when estimation
515 * starts, ends or its progress significantly changes.
516 * @param qualityScores quality scores corresponding to each pair of matched
517 * points.
518 * @return an instance of affine 2D transformation estimator.
519 */
520 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
521 final AffineTransformation2DRobustEstimatorListener listener, final double[] qualityScores) {
522 return create(listener, qualityScores, DEFAULT_ROBUST_METHOD);
523 }
524
525 /**
526 * Creates an affine 2D transformation estimator based on 2D line
527 * correspondences and using default robust estimator method.
528 *
529 * @param listener listener to be notified of events such as when estimation
530 * starts, ends or its progress significantly changes.
531 * @param inputLines list of input lines to be used to estimate an affine
532 * 2D transformation.
533 * @param outputLines list of output lines to be used to estimate an affine
534 * 2D transformation.
535 * @param qualityScores quality scores corresponding to each pair of matched
536 * lines.
537 * @return an instance of affine 2D transformation estimator.
538 * @throws IllegalArgumentException if provided lists of lines don't have
539 * the same size or their size is smaller than MINIMUM_SIZE.
540 */
541 public static LineCorrespondenceAffineTransformation2DRobustEstimator create(
542 final AffineTransformation2DRobustEstimatorListener listener,
543 final List<Line2D> inputLines, final List<Line2D> outputLines, final double[] qualityScores) {
544 return create(listener, inputLines, outputLines, qualityScores, DEFAULT_ROBUST_METHOD);
545 }
546
547 /**
548 * Internal method to set lists of lines to be used to estimate an affine
549 * 2D transformation.
550 * This method does not check whether estimator is locked or not.
551 *
552 * @param inputLines list of input lines to be used to estimate an affine
553 * 2D transformation.
554 * @param outputLines list of output lines to be used to estimate an affine
555 * 2D transformation.
556 * @throws IllegalArgumentException if provided lists of lines don't have
557 * the same size or their size is smaller than MINIMUM_SIZE.
558 */
559 private void internalSetLines(final List<Line2D> inputLines, final List<Line2D> outputLines) {
560 if (inputLines.size() < MINIMUM_SIZE) {
561 throw new IllegalArgumentException();
562 }
563 if (inputLines.size() != outputLines.size()) {
564 throw new IllegalArgumentException();
565 }
566 this.inputLines = inputLines;
567 this.outputLines = outputLines;
568 }
569
570 /**
571 * Computes residual by comparing two lines algebraically by doing the
572 * dot product of their parameters.
573 * A residual of 0 indicates that dot product was 1 or -1 and lines were
574 * equal.
575 * A residual of 1 indicates that dot product was 0 and lines were
576 * orthogonal.
577 * If dot product was -1, then although their director vectors are opposed,
578 * lines are considered equal, since sign changes are not taken into account.
579 *
580 * @param line originally sampled output line.
581 * @param transformedLine estimated output line obtained after using
582 * estimated transformation.
583 * @return computed residual.
584 */
585 protected static double getResidual(final Line2D line, final Line2D transformedLine) {
586 line.normalize();
587 transformedLine.normalize();
588
589 final var dotProduct = Math.abs(line.getA() * transformedLine.getA() + line.getB() * transformedLine.getB()
590 + line.getC() * transformedLine.getC());
591 return 1.0 - dotProduct;
592 }
593
594 /**
595 * Attempts to refine provided solution if refinement is requested.
596 * This method returns a refined solution of the same provided solution
597 * if refinement is not requested or has failed.
598 * If refinement is enabled, and it is requested to keep covariance, this
599 * method will also keep covariance of refined transformation.
600 *
601 * @param transformation transformation estimated by a robust estimator
602 * without refinement.
603 * @return solution after refinement (if requested) or the provided
604 * non-refined solution if not requested or refinement failed.
605 */
606 @SuppressWarnings("DuplicatedCode")
607 protected AffineTransformation2D attemptRefine(final AffineTransformation2D transformation) {
608 if (refineResult) {
609 final var refiner = new LineCorrespondenceAffineTransformation2DRefiner(transformation, keepCovariance,
610 getInliersData(), inputLines, outputLines, getRefinementStandardDeviation());
611
612 try {
613 final var result = new AffineTransformation2D();
614 final var improved = refiner.refine(result);
615
616 if (keepCovariance) {
617 // keep covariance
618 covariance = refiner.getCovariance();
619 }
620
621 return improved ? result : transformation;
622 } catch (final Exception e) {
623 // refinement failed, so we return input value
624 return transformation;
625 }
626 } else {
627 return transformation;
628 }
629 }
630 }