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.ar.epipolar;
17  
18  import com.irurueta.geometry.Point2D;
19  import com.irurueta.geometry.estimators.LockedException;
20  import com.irurueta.geometry.estimators.NotReadyException;
21  
22  import java.util.List;
23  
24  /**
25   * Fixes matched pairs of points so that they perfectly follow a given epipolar
26   * geometry.
27   * When matching points typically the matching precision is about 1 pixel,
28   * however this makes that matched points under a given epipolar geometry (i.e.
29   * fundamental or essential matrix), do not lie perfectly on the corresponding
30   * epipolar plane or epipolar lines.
31   * The consequence is that triangularization of these matches will fail or
32   * produce inaccurate results.
33   * By fixing matched points using a corrector following a given epipolar
34   * geometry, this effect is alleviated.
35   * This is an abstract class, subclasses will implement different methods to
36   * fix matched points coordinates.
37   */
38  public abstract class Corrector {
39  
40      /**
41       * Default corrector type.
42       */
43      public static final CorrectorType DEFAULT_TYPE = CorrectorType.SAMPSON_CORRECTOR;
44  
45      /**
46       * Default amount of progress variation before notifying a change in
47       * estimation progress. By default, this is set to 5%.
48       */
49      public static final float DEFAULT_PROGRESS_DELTA = 0.05f;
50  
51      /**
52       * Minimum allowed value for progress delta.
53       */
54      public static final float MIN_PROGRESS_DELTA = 0.0f;
55  
56      /**
57       * Maximum allowed value for progress delta.
58       */
59      public static final float MAX_PROGRESS_DELTA = 1.0f;
60  
61      /**
62       * A fundamental matrix defining an epipolar geometry.
63       */
64      protected FundamentalMatrix fundamentalMatrix;
65  
66      /**
67       * List of points on left view to be corrected.
68       */
69      protected List<Point2D> leftPoints;
70  
71      /**
72       * List of points on right view to be corrected.
73       */
74      protected List<Point2D> rightPoints;
75  
76      /**
77       * List of points on left view after correction.
78       */
79      protected List<Point2D> leftCorrectedPoints;
80  
81      /**
82       * List of points on right view after correction.
83       */
84      protected List<Point2D> rightCorrectedPoints;
85  
86      /**
87       * Listener to notify start, stop and progress events.
88       */
89      protected CorrectorListener listener;
90  
91      /**
92       * Indicates whether this instance is locked or not while doing computations.
93       */
94      protected boolean locked;
95  
96      /**
97       * Amount of progress variation before notifying a progress change during
98       * estimation.
99       */
100     protected float progressDelta;
101 
102     /**
103      * Constructor.
104      */
105     protected Corrector() {
106         progressDelta = DEFAULT_PROGRESS_DELTA;
107     }
108 
109     /**
110      * Constructor.
111      *
112      * @param fundamentalMatrix fundamental matrix to be set.
113      */
114     protected Corrector(final FundamentalMatrix fundamentalMatrix) {
115         this();
116         internalSetFundamentalMatrix(fundamentalMatrix);
117     }
118 
119     /**
120      * Constructor.
121      *
122      * @param leftPoints  points to be corrected on left view.
123      * @param rightPoints points to be corrected on right view.
124      * @throws IllegalArgumentException if provided lists of points don't have
125      *                                  the same size.
126      */
127     protected Corrector(final List<Point2D> leftPoints, final List<Point2D> rightPoints) {
128         this();
129         internalSetLeftAndRightPoints(leftPoints, rightPoints);
130     }
131 
132     /**
133      * Constructor.
134      *
135      * @param leftPoints        points to be corrected on left view.
136      * @param rightPoints       points to be corrected on right view.
137      * @param fundamentalMatrix fundamental matrix to be set.
138      * @throws IllegalArgumentException if provided lists of points don't have
139      *                                  the same size.
140      */
141     protected Corrector(final List<Point2D> leftPoints, final List<Point2D> rightPoints,
142                         final FundamentalMatrix fundamentalMatrix) {
143         this();
144         internalSetLeftAndRightPoints(leftPoints, rightPoints);
145         internalSetFundamentalMatrix(fundamentalMatrix);
146     }
147 
148     /**
149      * Constructor.
150      *
151      * @param listener listener to handle events generated by this class.
152      */
153     protected Corrector(final CorrectorListener listener) {
154         this();
155         this.listener = listener;
156     }
157 
158     /**
159      * Constructor.
160      *
161      * @param fundamentalMatrix fundamental matrix to be set.
162      * @param listener          listener to handle events generated by this class.
163      */
164     protected Corrector(final FundamentalMatrix fundamentalMatrix, final CorrectorListener listener) {
165         this(fundamentalMatrix);
166         this.listener = listener;
167     }
168 
169     /**
170      * Constructor.
171      *
172      * @param leftPoints  points to be corrected on left view.
173      * @param rightPoints points to be corrected on right view.
174      * @param listener    listener to handle events generated by this class.
175      * @throws IllegalArgumentException if provided lists of points don't have
176      *                                  the same size.
177      */
178     protected Corrector(final List<Point2D> leftPoints, final List<Point2D> rightPoints,
179                         final CorrectorListener listener) {
180         this(leftPoints, rightPoints);
181         this.listener = listener;
182     }
183 
184     /**
185      * Constructor.
186      *
187      * @param leftPoints        points to be corrected on left view.
188      * @param rightPoints       points to be corrected on right view.
189      * @param fundamentalMatrix fundamental matrix to be set.
190      * @param listener          listener to handle events generated by this class.
191      * @throws IllegalArgumentException if provided lists of points don't have
192      *                                  the same size.
193      */
194     protected Corrector(final List<Point2D> leftPoints, final List<Point2D> rightPoints,
195                         final FundamentalMatrix fundamentalMatrix, final CorrectorListener listener) {
196         this(leftPoints, rightPoints, fundamentalMatrix);
197         this.listener = listener;
198     }
199 
200     /**
201      * Sets the fundamental matrix defining the epipolar geometry.
202      *
203      * @param fundamentalMatrix fundamental matrix to be set.
204      * @throws LockedException if this instance is locked.
205      */
206     public final void setFundamentalMatrix(final FundamentalMatrix fundamentalMatrix) throws LockedException {
207         if (isLocked()) {
208             throw new LockedException();
209         }
210         internalSetFundamentalMatrix(fundamentalMatrix);
211     }
212 
213     /**
214      * Returns fundamental matrix defining epipolar geometry.
215      *
216      * @return fundamental matrix defining epipolar geometry.
217      */
218     public FundamentalMatrix getFundamentalMatrix() {
219         return fundamentalMatrix;
220     }
221 
222     /**
223      * Returns list of points to be corrected on left view.
224      *
225      * @return list of points to be corrected on left view.
226      */
227     public List<Point2D> getLeftPoints() {
228         return leftPoints;
229     }
230 
231     /**
232      * Returns list of points to be corrected on right view.
233      *
234      * @return list of points to be corrected on right view.
235      */
236     public List<Point2D> getRightPoints() {
237         return rightPoints;
238     }
239 
240     /**
241      * Sets lists of points to be corrected on left and right views.
242      *
243      * @param leftPoints  points to be corrected on left view.
244      * @param rightPoints points to be corrected on right view.
245      * @throws IllegalArgumentException if provided lists of points don't have
246      *                                  the same size.
247      * @throws LockedException          if instance is locked doing computations.
248      */
249     public void setLeftAndRightPoints(final List<Point2D> leftPoints, final List<Point2D> rightPoints)
250             throws LockedException {
251         if (isLocked()) {
252             throw new LockedException();
253         }
254         internalSetLeftAndRightPoints(leftPoints, rightPoints);
255     }
256 
257     /**
258      * Sets lists of points to be corrected on left and right views and
259      * fundamental matrix defining epipolar geometry.
260      *
261      * @param leftPoints        points to be corrected on left view.
262      * @param rightPoints       points to be corrected on right view.
263      * @param fundamentalMatrix fundamental matrix to be set.
264      * @throws IllegalArgumentException if provided lists of points don't have
265      *                                  the same size.
266      * @throws LockedException          if instance is locked doing computations.
267      */
268     public void setPointsAndFundamentalMatrix(
269             final List<Point2D> leftPoints, final List<Point2D> rightPoints, final FundamentalMatrix fundamentalMatrix)
270             throws LockedException {
271         if (isLocked()) {
272             throw new LockedException();
273         }
274         internalSetFundamentalMatrix(fundamentalMatrix);
275         internalSetLeftAndRightPoints(leftPoints, rightPoints);
276     }
277 
278     /**
279      * Returns list of points on left view obtained after correction.
280      *
281      * @return list of points on left view obtained after correction.
282      */
283     public List<Point2D> getLeftCorrectedPoints() {
284         return leftCorrectedPoints;
285     }
286 
287     /**
288      * Returns list of points on right view obtained after correction.
289      *
290      * @return list of points on right view obtained after correction.
291      */
292     public List<Point2D> getRightCorrectedPoints() {
293         return rightCorrectedPoints;
294     }
295 
296     /**
297      * Returns boolean indicating whether this instance is locked or not doing
298      * computations.
299      *
300      * @return true if instance is locked, false otherwise.
301      */
302     public boolean isLocked() {
303         return locked;
304     }
305 
306     /**
307      * Returns amount of progress variation before notifying a progress change
308      * during estimation.
309      *
310      * @return amount of progress variation before notifying a progress change
311      * during estimation.
312      */
313     public float getProgressDelta() {
314         return progressDelta;
315     }
316 
317     /**
318      * Sets amount of progress variation before notifying a progress change
319      * during estimation.
320      *
321      * @param progressDelta amount of progress variation before notifying a
322      *                      progress change during estimation.
323      * @throws IllegalArgumentException if progress delta is less than zero or
324      *                                  greater than 1.
325      * @throws LockedException          if this estimator is locked because an estimation
326      *                                  is being computed.
327      */
328     public void setProgressDelta(final float progressDelta) throws LockedException {
329         if (isLocked()) {
330             throw new LockedException();
331         }
332         if (progressDelta < MIN_PROGRESS_DELTA || progressDelta > MAX_PROGRESS_DELTA) {
333             throw new IllegalArgumentException();
334         }
335         this.progressDelta = progressDelta;
336     }
337 
338     /**
339      * Returns listener to handle events generated by this class.
340      *
341      * @return listener to handle events generated by this class.
342      */
343     public CorrectorListener getListener() {
344         return listener;
345     }
346 
347     /**
348      * Sets listener to handle events generated by this class.
349      *
350      * @param listener listener to handle events generated by this class.
351      * @throws LockedException if this estimator is locked because an estimation
352      *                         is being computed.
353      */
354     public void setListener(final CorrectorListener listener) throws LockedException {
355         if (isLocked()) {
356             throw new LockedException();
357         }
358         this.listener = listener;
359     }
360 
361     /**
362      * Indicates whether provided lists of points to be corrected on left and
363      * right views are valid.
364      * Lists are valid when both have the same size.
365      *
366      * @param leftPoints  points to be corrected on left view.
367      * @param rightPoints points to be corrected on right view.
368      * @return true if lists of points are valid, false otherwise.
369      */
370     public static boolean areValidPoints(final List<Point2D> leftPoints, final List<Point2D> rightPoints) {
371         return leftPoints != null && rightPoints != null && leftPoints.size() == rightPoints.size();
372     }
373 
374     /**
375      * Indicates whether this instance is ready to correct provided left
376      * and right points using provided fundamental matrix.
377      *
378      * @return true if ready, false otherwise.
379      */
380     public boolean isReady() {
381         return areValidPoints(leftPoints, rightPoints) && fundamentalMatrix != null
382                 && fundamentalMatrix.isInternalMatrixAvailable();
383     }
384 
385     /**
386      * Corrects the lists of provided matched points to be corrected.
387      *
388      * @throws NotReadyException   if this instance is not ready (either points or
389      *                             fundamental matrix has not been provided yet).
390      * @throws LockedException     if this instance is locked doing computations.
391      * @throws CorrectionException if correction fails.
392      */
393     public abstract void correct() throws NotReadyException, LockedException, CorrectionException;
394 
395     /**
396      * Gets type of correction being used.
397      *
398      * @return type of correction.
399      */
400     public abstract CorrectorType getType();
401 
402     /**
403      * Creates an instance of a corrector using provided type.
404      *
405      * @param type a corrector type.
406      * @return an instance of a corrector.
407      */
408     public static Corrector create(final CorrectorType type) {
409         return create((CorrectorListener) null, type);
410     }
411 
412     /**
413      * Creates an instance of a corrector using provided fundamental matrix
414      * and provided type.
415      *
416      * @param fundamentalMatrix fundamental matrix defining the epipolar
417      *                          geometry.
418      * @param type              a corrector type.
419      * @return an instance of a corrector.
420      */
421     public static Corrector create(final FundamentalMatrix fundamentalMatrix, final CorrectorType type) {
422         return create(fundamentalMatrix, null, type);
423     }
424 
425     /**
426      * Creates an instance of a corrector using provided left and right points
427      * to be corrected and provided type.
428      *
429      * @param leftPoints  matched points on left view to be corrected.
430      * @param rightPoints matched points on right view to be corrected.
431      * @param type        a corrector type.
432      * @return an instance of a corrector.
433      */
434     public static Corrector create(
435             final List<Point2D> leftPoints, final List<Point2D> rightPoints, final CorrectorType type) {
436         return create(leftPoints, rightPoints, (CorrectorListener) null, type);
437     }
438 
439     /**
440      * Creates an instance of a corrector using provided left and right points
441      * to be corrected, provided fundamental matrix and provided type.
442      *
443      * @param leftPoints        matched points on left view to be corrected.
444      * @param rightPoints       matched points on right view to be corrected.
445      * @param fundamentalMatrix fundamental matrix defining the epipolar
446      *                          geometry.
447      * @param type              a corrector type.
448      * @return an instance of a corrector.
449      */
450     public static Corrector create(
451             final List<Point2D> leftPoints, final List<Point2D> rightPoints,
452             final FundamentalMatrix fundamentalMatrix, final CorrectorType type) {
453         return create(leftPoints, rightPoints, fundamentalMatrix, null, type);
454     }
455 
456     /**
457      * Creates an instance of a corrector using provided type.
458      *
459      * @param listener listener to handle events generated by this class.
460      * @param type     a corrector type.
461      * @return an instance of a corrector.
462      */
463     public static Corrector create(final CorrectorListener listener, final CorrectorType type) {
464         return type == CorrectorType.SAMPSON_CORRECTOR
465                 ? new SampsonCorrector(listener) : new GoldStandardCorrector(listener);
466     }
467 
468     /**
469      * Creates an instance of a corrector using provided fundamental matrix
470      * and provided type.
471      *
472      * @param fundamentalMatrix fundamental matrix defining the epipolar
473      *                          geometry.
474      * @param listener          listener to handle events generated by this class.
475      * @param type              a corrector type.
476      * @return an instance of a corrector.
477      */
478     public static Corrector create(
479             final FundamentalMatrix fundamentalMatrix, final CorrectorListener listener, final CorrectorType type) {
480         return type == CorrectorType.SAMPSON_CORRECTOR
481                 ? new SampsonCorrector(fundamentalMatrix, listener)
482                 : new GoldStandardCorrector(fundamentalMatrix, listener);
483     }
484 
485     /**
486      * Creates an instance of a corrector using provided left and right points
487      * to be corrected and provided type.
488      *
489      * @param leftPoints  matched points on left view to be corrected.
490      * @param rightPoints matched points on right view to be corrected.
491      * @param listener    listener to handle events generated by this class.
492      * @param type        a corrector type.
493      * @return an instance of a corrector.
494      */
495     public static Corrector create(
496             final List<Point2D> leftPoints, final List<Point2D> rightPoints, final CorrectorListener listener,
497             final CorrectorType type) {
498         return type == CorrectorType.SAMPSON_CORRECTOR
499                 ? new SampsonCorrector(leftPoints, rightPoints, listener)
500                 : new GoldStandardCorrector(leftPoints, rightPoints, listener);
501     }
502 
503     /**
504      * Creates an instance of a corrector using provided left and right points
505      * to be corrected, provided fundamental matrix and provided type.
506      *
507      * @param leftPoints        matched points on left view to be corrected.
508      * @param rightPoints       matched points on right view to be corrected.
509      * @param fundamentalMatrix fundamental matrix defining the epipolar
510      *                          geometry.
511      * @param listener          listener to handle events generated by this class.
512      * @param type              a corrector type.
513      * @return an instance of a corrector.
514      */
515     public static Corrector create(
516             final List<Point2D> leftPoints, final List<Point2D> rightPoints, final FundamentalMatrix fundamentalMatrix,
517             final CorrectorListener listener, final CorrectorType type) {
518         return type == CorrectorType.SAMPSON_CORRECTOR
519                 ? new SampsonCorrector(leftPoints, rightPoints, fundamentalMatrix, listener)
520                 : new GoldStandardCorrector(leftPoints, rightPoints, fundamentalMatrix, listener);
521     }
522 
523     /**
524      * Creates an instance of a corrector using default type.
525      *
526      * @param listener listener to handle events generated by this class.
527      * @return an instance of a corrector.
528      */
529     public static Corrector create(final CorrectorListener listener) {
530         return create(listener, DEFAULT_TYPE);
531     }
532 
533     /**
534      * Creates an instance of a corrector using provided fundamental matrix
535      * and default type.
536      *
537      * @param fundamentalMatrix fundamental matrix defining the epipolar
538      *                          geometry.
539      * @param listener          listener to handle events generated by this class.
540      * @return an instance of a corrector.
541      */
542     public static Corrector create(final FundamentalMatrix fundamentalMatrix, final CorrectorListener listener) {
543         return create(fundamentalMatrix, listener, DEFAULT_TYPE);
544     }
545 
546     /**
547      * Creates an instance of a corrector using provided left and right points
548      * to be corrected and provided type.
549      *
550      * @param leftPoints  matched points on left view to be corrected.
551      * @param rightPoints matched points on right view to be corrected.
552      * @param listener    listener to handle events generated by this class.
553      * @return an instance of a corrector.
554      */
555     public static Corrector create(
556             final List<Point2D> leftPoints, final List<Point2D> rightPoints, final CorrectorListener listener) {
557         return create(leftPoints, rightPoints, listener, DEFAULT_TYPE);
558     }
559 
560     /**
561      * Creates an instance of a corrector using provided left and right points
562      * to be corrected, provided fundamental matrix and provided type.
563      *
564      * @param leftPoints        matched points on left view to be corrected.
565      * @param rightPoints       matched points on right view to be corrected.
566      * @param fundamentalMatrix fundamental matrix defining the epipolar
567      *                          geometry.
568      * @param listener          listener to handle events generated by this class.
569      * @return an instance of a corrector.
570      */
571     public static Corrector create(
572             final List<Point2D> leftPoints, final List<Point2D> rightPoints, final FundamentalMatrix fundamentalMatrix,
573             final CorrectorListener listener) {
574         return create(leftPoints, rightPoints, fundamentalMatrix, listener, DEFAULT_TYPE);
575     }
576 
577     /**
578      * Creates an instance of a corrector using default type.
579      *
580      * @return an instance of a corrector.
581      */
582     public static Corrector create() {
583         return create(DEFAULT_TYPE);
584     }
585 
586     /**
587      * Creates an instance of a corrector using provided fundamental matrix
588      * and default type.
589      *
590      * @param fundamentalMatrix fundamental matrix defining the epipolar
591      *                          geometry.
592      * @return an instance of a corrector.
593      */
594     public static Corrector create(final FundamentalMatrix fundamentalMatrix) {
595         return create(fundamentalMatrix, DEFAULT_TYPE);
596     }
597 
598     /**
599      * Creates an instance of a corrector using provided left and right points
600      * to be corrected and default type.
601      *
602      * @param leftPoints  matched points on left view to be corrected.
603      * @param rightPoints matched points on right view to be corrected.
604      * @return an instance of a corrector.
605      */
606     public static Corrector create(final List<Point2D> leftPoints, final List<Point2D> rightPoints) {
607         return create(leftPoints, rightPoints, DEFAULT_TYPE);
608     }
609 
610     /**
611      * Creates an instance of a corrector using provided left and right points
612      * to be corrected, provided fundamental matrix and default type.
613      *
614      * @param leftPoints        matched points on left view to be corrected.
615      * @param rightPoints       matched points on right view to be corrected.
616      * @param fundamentalMatrix fundamental matrix defining the epipolar
617      *                          geometry.
618      * @return an instance of a corrector.
619      */
620     public static Corrector create(
621             final List<Point2D> leftPoints, final List<Point2D> rightPoints,
622             final FundamentalMatrix fundamentalMatrix) {
623         return create(leftPoints, rightPoints, fundamentalMatrix, DEFAULT_TYPE);
624     }
625 
626     /**
627      * Internal method to set fundamental matrix defining the epipolar geometry.
628      *
629      * @param fundamentalMatrix fundamental matrix to be set.
630      */
631     private void internalSetFundamentalMatrix(final FundamentalMatrix fundamentalMatrix) {
632         this.fundamentalMatrix = fundamentalMatrix;
633     }
634 
635     /**
636      * Internal method to set lists of points to be corrected on left and right
637      * views.
638      *
639      * @param leftPoints  points to be corrected on left view.
640      * @param rightPoints points to be corrected on right view.
641      * @throws IllegalArgumentException if provided lists of points don't have
642      *                                  the same size.
643      */
644     private void internalSetLeftAndRightPoints(
645             final List<Point2D> leftPoints, final List<Point2D> rightPoints) {
646         if (!areValidPoints(leftPoints, rightPoints)) {
647             throw new IllegalArgumentException();
648         }
649 
650         this.leftPoints = leftPoints;
651         this.rightPoints = rightPoints;
652     }
653 }