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.CoordinatesType;
19  import com.irurueta.geometry.Line2D;
20  import com.irurueta.geometry.Point2D;
21  import com.irurueta.geometry.estimators.LockedException;
22  import com.irurueta.geometry.estimators.NotReadyException;
23  import com.irurueta.statistics.UniformRandomizer;
24  
25  /**
26   * Compares two fundamental matrices by estimating average epipolar distances.
27   * This class uses epipolar geometry to determine how similar fundamental
28   * matrices are. This is done by assuming a certain size on the retinal planes
29   * and generating random 2D points to obtain their associated epipolar lines and
30   * the distances of matched points to those epipolar lines
31   * This class simply computes the norm of the difference of both fundamental
32   * matrices. The smaller the value the more similar the fundamental matrices
33   * will be from a pure algebraic point of view.
34   */
35  public class EpipolarDistanceFundamentalMatrixComparator extends FundamentalMatrixComparator {
36  
37      /**
38       * Defines default minimum horizontal coordinate when generating random
39       * samples.
40       */
41      public static final double DEFAULT_MIN_X = 0.0;
42  
43      /**
44       * Defines default maximum horizontal coordinate when generating random
45       * samples.
46       */
47      public static final double DEFAULT_MAX_X = 640.0;
48  
49      /**
50       * Defines default minimum vertical coordinate when generating random
51       * samples.
52       */
53      public static final double DEFAULT_MIN_Y = 0.0;
54  
55      /**
56       * Defines default maximum vertical coordinate when generating random
57       * samples.
58       */
59      public static final double DEFAULT_MAX_Y = 480.0;
60  
61      /**
62       * Default number of random samples to generate to compare fundamental
63       * matrices.
64       */
65      public static final int DEFAULT_N_SAMPLES = 100;
66  
67      /**
68       * Minimum number of samples that must be generated to compare fundamental
69       * matrices.
70       */
71      public static final int MIN_N_SAMPLES = 1;
72  
73      /**
74       * Minimum disparity factor respect to retinal plane size defined by
75       * minimum and maximum samples coordinates. When computing residuals,
76       * matched samples are created along the corresponding epipolar lines with a
77       * random disparity within provided range, and then the epipolar line for
78       * the randomly generated matched sample is generated on the original view
79       * to determine the distance to such line and the original sample.
80       */
81      public static final double DEFAULT_MIN_DISPARITY_FACTOR = -0.1;
82  
83      /**
84       * Maximum disparity factor respect to retinal plane size defined by
85       * minimum and maximum samples coordinates. When computing residuals,
86       * matched samples are created along the corresponding epipolar lines with a
87       * random disparity within provided range, and then the epipolar line for
88       * the randomly generated matched sample is generated on the original view
89       * to determine the distance to such line and the original sample.
90       */
91      public static final double DEFAULT_MAX_DISPARITY_FACTOR = 0.1;
92  
93      /**
94       * Default factor to determine maximum number of iterations respect to the
95       * number of samples to compute comparison.
96       */
97      public static final double DEFAULT_MAX_ITERATIONS_FACTOR = 10.0;
98  
99      /**
100      * Minimum value for the factor to determine maximum number of iterations
101      * respect to the number of samples to compute comparison.
102      */
103     public static final double MIN_MAX_ITERATIONS_FACTOR = 1.0;
104 
105     /**
106      * Default amount of progress variation before notifying a change in
107      * comparison progress. By default, this is set to 5%.
108      */
109     public static final float DEFAULT_PROGRESS_DELTA = 0.05f;
110 
111     /**
112      * Minimum allowed value for progress delta.
113      */
114     public static final float MIN_PROGRESS_DELTA = 0.0f;
115 
116     /**
117      * Maximum allowed value for progress delta.
118      */
119     public static final float MAX_PROGRESS_DELTA = 1.0f;
120 
121     /**
122      * Minimum horizontal coordinate when generating random samples.
123      */
124     private double minX;
125 
126     /**
127      * Maximum horizontal coordinate when generating random samples.
128      */
129     private double maxX;
130 
131     /**
132      * Minimum vertical coordinate when generating random samples.
133      */
134     private double minY;
135 
136     /**
137      * Maximum vertical coordinate when generating random samples.
138      */
139     private double maxY;
140 
141     /**
142      * Number of random samples to generate to compare fundamental matrices.
143      */
144     private int nSamples;
145 
146     /**
147      * Minimum horizontal disparity factor respect to retinal plane size defined
148      * by minimum and maximum samples coordinates. When computing residuals,
149      * matched samples are created along the corresponding epipolar lines with a
150      * random disparity within provided range of disparities, and then the
151      * epipolar line for the randomly generated matched sample is generated on
152      * the original view to determine the distance to such line and the original
153      * sample.
154      */
155     private double minHorizontalDisparityFactor;
156 
157     /**
158      * Maximum horizontal disparity factor respect to retinal plane size defined
159      * by minimum and maximum samples coordinates. When computing residuals,
160      * matched samples are created along the corresponding epipolar lines with a
161      * random disparity within provided range of disparities, and then the
162      * epipolar line for the randomly generated matched sample is generated on
163      * the original view to determine the distance to such line and the original
164      * sample.
165      */
166     private double maxHorizontalDisparityFactor;
167 
168     /**
169      * Minimum vertical disparity factor respect to retinal plane size defined
170      * by minimum and maximum samples coordinates. When computing residuals,
171      * matched samples are created along the corresponding epipolar lines with a
172      * random disparity within provided range of disparities, and then the
173      * epipolar line for the randomly generated matched sample is generated on
174      * the original view to determine the distance to such line and the original
175      * sample.
176      */
177     private double minVerticalDisparityFactor;
178 
179     /**
180      * Maximum vertical disparity factor respect to retinal plane size defined
181      * by minimum and maximum samples coordinates. When computing residuals,
182      * matched samples are created along the corresponding epipolar lines with a
183      * random disparity within provided range of disparities, and then the
184      * epipolar line for the randomly generated matched sample is generated on
185      * the original view to determine the distance to such line and the original
186      * sample.
187      */
188     private double maxVerticalDisparityFactor;
189 
190     /**
191      * Factor to determine maximum number of iterations respect to the
192      * number of samples to compute comparison.
193      */
194     private double maxIterationsFactor;
195 
196     /**
197      * Amount of progress variation before notifying a progress change during
198      * comparison.
199      */
200     protected float progressDelta;
201 
202     /**
203      * Constructor.
204      */
205     public EpipolarDistanceFundamentalMatrixComparator() {
206         super();
207         init();
208     }
209 
210     /**
211      * Constructor.
212      *
213      * @param groundTruthFundamentalMatrix fundamental matrix to be considered
214      *                                     as ground truth to compare against.
215      * @param otherFundamentalMatrix       other fundamental matrix being compared.
216      */
217     public EpipolarDistanceFundamentalMatrixComparator(
218             final FundamentalMatrix groundTruthFundamentalMatrix, final FundamentalMatrix otherFundamentalMatrix) {
219         super(groundTruthFundamentalMatrix, otherFundamentalMatrix);
220         init();
221     }
222 
223     /**
224      * Constructor.
225      *
226      * @param listener listener to handle events generated by this class.
227      */
228     public EpipolarDistanceFundamentalMatrixComparator(final FundamentalMatrixComparatorListener listener) {
229         super(listener);
230         init();
231     }
232 
233     /**
234      * Constructor.
235      *
236      * @param groundTruthFundamentalMatrix fundamental matrix to be considered
237      *                                     as ground truth to compare against.
238      * @param otherFundamentalMatrix       other fundamental matrix being compared.
239      * @param listener                     listener to handle events generated by this class.
240      */
241     public EpipolarDistanceFundamentalMatrixComparator(
242             final FundamentalMatrix groundTruthFundamentalMatrix, final FundamentalMatrix otherFundamentalMatrix,
243             final FundamentalMatrixComparatorListener listener) {
244         super(groundTruthFundamentalMatrix, otherFundamentalMatrix, listener);
245         init();
246     }
247 
248     /**
249      * Returns minimum horizontal coordinate when generating random samples.
250      *
251      * @return minimum horizontal coordinate when generating random samples.
252      */
253     public double getMinX() {
254         return minX;
255     }
256 
257     /**
258      * Returns maximum horizontal coordinate when generating random samples.
259      *
260      * @return maximum horizontal coordinate when generating random samples.
261      */
262     public double getMaxX() {
263         return maxX;
264     }
265 
266     /**
267      * Sets minimum and maximum horizontal coordinates when generating random
268      * samples.
269      *
270      * @param minX minimum horizontal coordinate when generating random samples.
271      * @param maxX maximum horizontal coordinate when generating random samples.
272      * @throws LockedException          if this instance is locked.
273      * @throws IllegalArgumentException if minimum value is larger or equal
274      *                                  than maximum one.
275      */
276     public void setMinMaxX(final double minX, final double maxX) throws LockedException {
277         if (isLocked()) {
278             throw new LockedException();
279         }
280         if (minX >= maxX) {
281             throw new IllegalArgumentException();
282         }
283 
284         this.minX = minX;
285         this.maxX = maxX;
286     }
287 
288     /**
289      * Returns minimum vertical coordinate when generating random samples.
290      *
291      * @return minimum vertical coordinate when generating random samples.
292      */
293     public double getMinY() {
294         return minY;
295     }
296 
297     /**
298      * Returns maximum vertical coordinate when generating random samples.
299      *
300      * @return maximum vertical coordinate when generating random samples.
301      */
302     public double getMaxY() {
303         return maxY;
304     }
305 
306     /**
307      * Sets minimum and maximum vertical coordinates when generating random
308      * samples.
309      *
310      * @param minY minimum vertical coordinate when generating random samples.
311      * @param maxY maximum vertical coordinate when generating random samples.
312      * @throws LockedException          if this instance is locked.
313      * @throws IllegalArgumentException if minimum value is larger or equal
314      *                                  than maximum one.
315      */
316     public void setMinMaxY(final double minY, final double maxY) throws LockedException {
317         if (isLocked()) {
318             throw new LockedException();
319         }
320         if (minY >= maxY) {
321             throw new IllegalArgumentException();
322         }
323 
324         this.minY = minY;
325         this.maxY = maxY;
326     }
327 
328     /**
329      * Returns number of random samples to generate to compare fundamental
330      * matrices.
331      *
332      * @return number of random samples to generate to compare fundamental
333      * matrices.
334      */
335     public int getNSamples() {
336         return nSamples;
337     }
338 
339     /**
340      * Sets number of random samples to generate to compare fundamental matrices.
341      *
342      * @param nSamples number of random samples to generate to compare
343      *                 fundamental matrices.
344      * @throws LockedException          if this instance is locked.
345      * @throws IllegalArgumentException if provided value is less than 1.
346      */
347     public void setNSamples(final int nSamples) throws LockedException {
348         if (isLocked()) {
349             throw new LockedException();
350         }
351         if (nSamples < MIN_N_SAMPLES) {
352             throw new IllegalArgumentException();
353         }
354 
355         this.nSamples = nSamples;
356     }
357 
358     /**
359      * Returns minimum horizontal disparity factor respect to retinal plane size
360      * defined by minimum and maximum samples coordinates. When computing
361      * residuals, matched samples are created along the corresponding epipolar
362      * lines with a random disparity within provided range of disparities, and
363      * then the epipolar line for the randomly generated matched sample is
364      * generated on the original view to determine the distance to such line and
365      * the original sample.
366      *
367      * @return minimum horizontal disparity factor.
368      */
369     public double getMinHorizontalDisparityFactor() {
370         return minHorizontalDisparityFactor;
371     }
372 
373     /**
374      * Returns maximum horizontal disparity factor respect to retinal plane size
375      * defined by minimum and maximum samples coordinates. When computing
376      * residuals, matched samples are created along the corresponding epipolar
377      * lines with a random disparity within provided range of disparities, and
378      * then the epipolar line for the randomly generated matched sample is
379      * generated on the original view to determine the distance to such line and
380      * the original sample.
381      *
382      * @return maximum horizontal disparity factor.
383      */
384     public double getMaxHorizontalDisparityFactor() {
385         return maxHorizontalDisparityFactor;
386     }
387 
388     /**
389      * Sets minimum and maximum horizontal disparity factor respect to retinal
390      * plane size defined by minimum and maximum samples coordinates. When
391      * computing residuals, matched samples are created along the corresponding
392      * epipolar lines with a random disparity within provided range of
393      * disparities, and then the epipolar line for the randomly generated matched
394      * sample is generated on the original view to determine the distance to
395      * such line and the original sample.
396      *
397      * @param minHorizontalDisparityFactor minimum horizontal disparity factor.
398      * @param maxHorizontalDisparityFactor maximum horizontal disparity factor.
399      * @throws LockedException          if this instance is locked.
400      * @throws IllegalArgumentException if minimum value is larger or equal
401      *                                  than maximum one.
402      */
403     public void setMinMaxHorizontalDisparityFactor(
404             final double minHorizontalDisparityFactor, final double maxHorizontalDisparityFactor)
405             throws LockedException {
406         if (isLocked()) {
407             throw new LockedException();
408         }
409         if (minHorizontalDisparityFactor >= maxHorizontalDisparityFactor) {
410             throw new IllegalArgumentException();
411         }
412 
413         this.minHorizontalDisparityFactor = minHorizontalDisparityFactor;
414         this.maxHorizontalDisparityFactor = maxHorizontalDisparityFactor;
415     }
416 
417     /**
418      * Returns minimum vertical disparity factor respect to retinal plane size
419      * defined by minimum and maximum samples coordinates. When computing
420      * residuals, matched samples are created along the corresponding epipolar
421      * lines with a random disparity within provided range of disparities, and
422      * then the epipolar line for the randomly generated matched sample is
423      * generated on the original view to determine the distance to such line and
424      * the original sample.
425      *
426      * @return minimum vertical disparity factor.
427      */
428     public double getMinVerticalDisparityFactor() {
429         return minVerticalDisparityFactor;
430     }
431 
432     /**
433      * Returns maximum vertical disparity factor respect to retinal plane size
434      * defined by minimum and maximum samples coordinates. When computing
435      * residuals, matched samples are created along the corresponding epipolar
436      * lines with a random disparity within provided range of disparities, and
437      * then the epipolar line for the randomly generated matched sample is
438      * generated on the original view to determine the distance to such line and
439      * the original sample.
440      *
441      * @return maximum vertical disparity factor.
442      */
443     public double getMaxVerticalDisparityFactor() {
444         return maxVerticalDisparityFactor;
445     }
446 
447     /**
448      * Sets minimum and maximum vertical disparity factor respect to retinal
449      * plane size defined by minimum and maximum samples coordinates. When
450      * computing residuals, matched samples are created along the corresponding
451      * epipolar lines with a random disparity within provided range of
452      * disparities, and then the epipolar line for the randomly generated matched
453      * sample is generated on the original view to determine the distance to
454      * such line and the original sample.
455      *
456      * @param minVerticalDisparityFactor minimum vertical disparity factor.
457      * @param maxVerticalDisparityFactor maximum vertical disparity factor.
458      * @throws LockedException          if this instance is locked.
459      * @throws IllegalArgumentException if minimum value is larger or equal
460      *                                  than maximum one.
461      */
462     public void setMinMaxVerticalDisparityFactor(
463             final double minVerticalDisparityFactor, final double maxVerticalDisparityFactor) throws LockedException {
464         if (isLocked()) {
465             throw new LockedException();
466         }
467         if (minVerticalDisparityFactor >= maxVerticalDisparityFactor) {
468             throw new IllegalArgumentException();
469         }
470 
471         this.minVerticalDisparityFactor = minVerticalDisparityFactor;
472         this.maxVerticalDisparityFactor = maxVerticalDisparityFactor;
473     }
474 
475     /**
476      * Returns factor to determine maximum number of iterations respect to the
477      * number of samples to compute comparison.
478      *
479      * @return factor to determine maximum number of iterations respect to the
480      * number of samples to compute comparison.
481      */
482     public double getMaxIterationsFactor() {
483         return maxIterationsFactor;
484     }
485 
486     /**
487      * Sets factor to determine maximum number of iterations respect to the
488      * number of samples to compute comparison.
489      *
490      * @param maxIterationsFactor maximum number of iterations respect to the
491      *                            number of samples to compute comparison.
492      * @throws LockedException          if this instance is locked.
493      * @throws IllegalArgumentException if provided value is less than 1.0.
494      */
495     public void setMaxIterationsFactor(final double maxIterationsFactor) throws LockedException {
496         if (isLocked()) {
497             throw new LockedException();
498         }
499         if (maxIterationsFactor < MIN_MAX_ITERATIONS_FACTOR) {
500             throw new IllegalArgumentException();
501         }
502 
503         this.maxIterationsFactor = maxIterationsFactor;
504     }
505 
506     /**
507      * Returns amount of progress variation before notifying a progress change
508      * during comparison computation.
509      *
510      * @return amount of progress variation before notifying a progress change
511      * during comparison computation.
512      */
513     public float getProgressDelta() {
514         return progressDelta;
515     }
516 
517     /**
518      * Sets amount of progress variation before notifying a progress change
519      * during comparison computation.
520      *
521      * @param progressDelta amount of progress variation before notifying a
522      *                      progress change during comparison computation.
523      * @throws IllegalArgumentException if progress delta is less than zero or
524      *                                  greater than 1.
525      * @throws LockedException          if this estimator is locked.
526      */
527     public void setProgressDelta(final float progressDelta) throws LockedException {
528         if (isLocked()) {
529             throw new LockedException();
530         }
531         if (progressDelta < MIN_PROGRESS_DELTA || progressDelta > MAX_PROGRESS_DELTA) {
532             throw new IllegalArgumentException();
533         }
534         this.progressDelta = progressDelta;
535     }
536 
537     /**
538      * Compares two fundamental matrices and returns the comparison value.
539      * Comparison value will depend on the method implemented to compare both
540      * fundamental matrices.
541      *
542      * @return comparison value. Typically, the smaller the absolute value the
543      * more similar the fundamental matrices are.
544      * @throws NotReadyException                    if this comparator is not  yet ready to start
545      *                                              the comparison.
546      * @throws LockedException                      if this instance is locked.
547      * @throws FundamentalMatrixComparatorException if comparison fails due to
548      *                                              some other reason.
549      */
550     @SuppressWarnings("DuplicatedCode")
551     @Override
552     public double compare() throws NotReadyException, LockedException, FundamentalMatrixComparatorException {
553         if (isLocked()) {
554             throw new LockedException();
555         }
556         if (!isReady()) {
557             throw new NotReadyException();
558         }
559 
560         try {
561             locked = true;
562 
563             if (listener != null) {
564                 listener.onCompareStart(this);
565             }
566 
567             final var minHorizontalDisparity = minHorizontalDisparityFactor * (maxX - minX);
568             final var maxHorizontalDisparity = maxHorizontalDisparityFactor * (maxX - minX);
569             final var minVerticalDisparity = minVerticalDisparityFactor * (maxY - minY);
570             final var maxVerticalDisparity = maxVerticalDisparityFactor * (maxY - minY);
571 
572             double d1;
573             double d1prime;
574             double d2;
575             double d2prime;
576             double inhomX;
577             double inhomY;
578             boolean repeat;
579             int counter;
580             final var m1 = Point2D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
581             final var m2 = Point2D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
582             final var l1real = new Line2D();
583             final var l1est = new Line2D();
584             final var l2real = new Line2D();
585             final var l2est = new Line2D();
586             final var randomizer = new UniformRandomizer();
587             var avgDist = 0.0;
588             final var maxIterations = (int) (nSamples * maxIterationsFactor);
589             var currentIter = 0;
590             float progress;
591             var previousProgress = 0.0f;
592             for (var i = 0; i < nSamples; i++) {
593                 repeat = true;
594                 counter = 0;
595                 while (repeat && (counter < nSamples)) {
596                     // set x value
597                     inhomX = randomizer.nextDouble(minX, maxX);
598 
599                     // set y value
600                     inhomY = randomizer.nextDouble(minY, maxY);
601                     m1.setInhomogeneousCoordinates(inhomX, inhomY);
602                     m1.normalize();
603 
604                     // real epipolar line for random point m1
605                     groundTruthFundamentalMatrix.rightEpipolarLine(m1, l2real);
606                     l2real.normalize();
607 
608                     // check that epipolar line lies within retinal plane size
609                     // taking into account the following equation
610                     // x*l2.getA() + y*l2.getB() + l2.getC() = 0
611                     double yMinX;
612                     double yMaxX;
613                     if (Math.abs(l2real.getB()) > Double.MIN_VALUE) {
614                         // for x = mMinX
615                         yMinX = -(minX * l2real.getA() + l2real.getC()) / l2real.getB();
616 
617                         // for x = mMaxX
618                         yMaxX = -(maxX * l2real.getA() + l2real.getC()) / l2real.getB();
619                     } else {
620                         yMinX = l2real.getA() >= 0.0 ? -Double.MAX_VALUE : Double.MAX_VALUE;
621                         yMaxX = -yMinX;
622                     }
623 
624                     // for y = mMinY
625                     final var xMinY = -(minY * l2real.getB() + l2real.getC()) / l2real.getA();
626 
627                     // for y = mMaxY
628                     final var xMaxY = -(maxY * l2real.getB() + l2real.getC()) / l2real.getA();
629 
630                     // if epipolar line does not intersect second image, we need
631                     // to repeat with a different sample
632                     repeat = (((yMinX < minY) && (yMaxX < minY)) || ((yMinX > maxY)
633                             && (yMaxX > maxY)) || ((xMinY < minX) && (xMaxY < minX))
634                             || ((xMinY > maxX) && (xMaxY > maxX)));
635                     counter++;
636                     currentIter++;
637                 }
638 
639                 if (counter >= nSamples) {
640                     continue;
641                 }
642 
643                 // choose point lying on epipolar line l2real :
644                 // m2.getX() * l2real.getA() + m2.getY() * l2real.getB() +
645                 // l2real.getC() = 0
646                 // choose random horizontal component within provided disparity:
647                 inhomX = m1.getInhomX() + randomizer.nextDouble(minHorizontalDisparity, maxHorizontalDisparity);
648                 if (Math.abs(l2real.getB()) > Double.MIN_VALUE) {
649                     inhomY = -(inhomX * l2real.getA() + l2real.getC()) / l2real.getB();
650                 } else {
651                     inhomY = Double.MAX_VALUE;
652                 }
653 
654                 // if point lies outside retinal plane limits, try setting random
655                 // vertical component within provided disparity
656                 if ((inhomY < minY) || (inhomY > maxY)) {
657                     inhomY = m1.getInhomY() + randomizer.nextDouble(minVerticalDisparity, maxVerticalDisparity);
658                     inhomX = -(inhomY * l2real.getB() + l2real.getC()) / l2real.getA();
659                 }
660 
661                 m2.setInhomogeneousCoordinates(inhomX, inhomY);
662                 m2.normalize();
663 
664                 // estimated epipolar line for some random point m1
665                 otherFundamentalMatrix.rightEpipolarLine(m1, l2est);
666                 l2est.normalize();
667 
668                 // compute distance from l2est to m2 (distance from estimated to
669                 // real)
670                 d1prime = Math.abs(l2est.signedDistance(m2));
671 
672                 otherFundamentalMatrix.leftEpipolarLine(m2, l1est);
673                 l1est.normalize();
674                 d1 = Math.abs(l1est.signedDistance(m1));
675 
676                 // repeat reversing roles of ground truth and other fundamental
677                 // matrix
678                 repeat = true;
679                 counter = 0;
680                 while (repeat && (counter < nSamples)) {
681                     // set x value
682                     inhomX = randomizer.nextDouble(minX, maxX);
683 
684                     // set y value
685                     inhomY = randomizer.nextDouble(minY, maxY);
686                     m1.setInhomogeneousCoordinates(inhomX, inhomY);
687                     m1.normalize();
688 
689                     // real epipolar line for random point m1
690                     otherFundamentalMatrix.rightEpipolarLine(m1, l2est);
691                     l2est.normalize();
692 
693                     // check that epipolar line lies within retinal plane size
694                     // taking into account the following equation
695                     // x*l2.getA() + y*l2.getB() + l2.getC() = 0
696                     double yMinX;
697                     double yMaxX;
698                     if (Math.abs(l2est.getB()) > Double.MIN_VALUE) {
699                         // for x = mMinX
700                         yMinX = -(minX * l2est.getA() + l2est.getC()) / l2est.getB();
701 
702                         // for x = mMaxX
703                         yMaxX = -(maxX * l2est.getA() + l2est.getC()) / l2est.getB();
704                     } else {
705                         yMinX = l2est.getA() >= 0.0 ? -Double.MAX_VALUE : Double.MAX_VALUE;
706                         yMaxX = -yMinX;
707                     }
708 
709                     // for y = mMinY
710                     final var xMinY = -(minY * l2est.getB() + l2est.getC()) / l2est.getA();
711 
712                     // for y = mMaxY
713                     final var xMaxY = -(maxY * l2est.getB() + l2est.getC()) / l2est.getA();
714 
715                     // if epipolar line does not intersect second image, we need
716                     // to repeat with a different sample
717                     repeat = (((yMinX < minY) && (yMaxX < minY)) || ((yMinX > maxY)
718                             && (yMaxX > maxY)) || ((xMinY < minX) && (xMaxY < minX))
719                             || ((xMinY > maxX) && (xMaxY > maxX)));
720                     counter++;
721                     currentIter++;
722                 }
723 
724                 if (counter >= nSamples) {
725                     continue;
726                 }
727 
728                 // choose point lying on epipolar line l2est :
729                 // m2.getX() * l2est.getA() + m2.getY() * l2est.getB() +
730                 // l2est.getC() = 0
731                 // choose random horizontal component within provided disparity:
732                 inhomX = m1.getInhomX() + randomizer.nextDouble(minHorizontalDisparity, maxHorizontalDisparity);
733                 if (Math.abs(l2real.getB()) > Double.MIN_VALUE) {
734                     inhomY = -(inhomX * l2est.getA() + l2est.getC()) / l2est.getB();
735                 } else {
736                     inhomY = Double.MAX_VALUE;
737                 }
738 
739                 // if point lies outside retinal plane limits, try setting random
740                 // Vertical component within provided disparity
741                 if ((inhomY < minY) || (inhomY > maxY)) {
742                     inhomY = m1.getInhomY() + randomizer.nextDouble(minVerticalDisparity, maxVerticalDisparity);
743                     inhomX = -(inhomY * l2est.getB() + l2est.getC()) / l2est.getA();
744                 }
745 
746                 m2.setInhomogeneousCoordinates(inhomX, inhomY);
747                 m2.normalize();
748 
749                 // estimated epipolar line for some random point m1
750                 otherFundamentalMatrix.rightEpipolarLine(m1, l2real);
751                 l2real.normalize();
752 
753                 // compute distance from l2real to m2 (distance from estimated to
754                 // real)
755                 d2prime = Math.abs(l2real.signedDistance(m2));
756 
757                 otherFundamentalMatrix.leftEpipolarLine(m2, l1real);
758                 l1real.normalize();
759                 d2 = Math.abs(l1real.signedDistance(m1));
760 
761                 avgDist += (d1 + d1prime + d2 + d2prime) / 4.0;
762 
763                 if (currentIter > maxIterations) {
764                     throw new FundamentalMatrixComparatorException();
765                 }
766 
767                 progress = (float) currentIter / (float) maxIterations;
768 
769                 if (listener != null && progress - previousProgress > progressDelta) {
770                     previousProgress = progress;
771                     listener.onCompareProgressChange(this, progress);
772                 }
773             }
774 
775 
776             if (listener != null) {
777                 listener.onCompareEnd(this);
778             }
779 
780             return avgDist / nSamples;
781 
782         } finally {
783             locked = false;
784         }
785     }
786 
787     /**
788      * Returns type of comparator.
789      *
790      * @return type of comparator.
791      */
792     @Override
793     public FundamentalMatrixComparatorType getType() {
794         return FundamentalMatrixComparatorType.EPIPOLAR_DISTANCE_COMPARATOR;
795     }
796 
797     /**
798      * Initializes default settings.
799      */
800     private void init() {
801         minX = DEFAULT_MIN_X;
802         maxX = DEFAULT_MAX_X;
803         minY = DEFAULT_MIN_Y;
804         maxY = DEFAULT_MAX_Y;
805         nSamples = DEFAULT_N_SAMPLES;
806         minHorizontalDisparityFactor = minVerticalDisparityFactor = DEFAULT_MIN_DISPARITY_FACTOR;
807         maxHorizontalDisparityFactor = maxVerticalDisparityFactor = DEFAULT_MAX_DISPARITY_FACTOR;
808         maxIterationsFactor = DEFAULT_MAX_ITERATIONS_FACTOR;
809         progressDelta = DEFAULT_PROGRESS_DELTA;
810     }
811 }