View Javadoc
1   /*
2    * Copyright (C) 2015 Alberto Irurueta Carro (alberto@irurueta.com)
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.irurueta.geometry.estimators;
17  
18  import com.irurueta.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 }