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.Point2D;
20 import com.irurueta.geometry.estimators.LockedException;
21 import com.irurueta.geometry.estimators.NotReadyException;
22
23 import java.util.ArrayList;
24 import java.util.List;
25
26 /**
27 * Fixes matched pairs of points so that they perfectly follow a given epipolar
28 * geometry.
29 * When matching points typically the matching precision is about 1 pixel,
30 * however this makes that matched points under a given epipolar geometry (i.e.
31 * fundamental or essential matrix), do not lie perfectly on the corresponding
32 * epipolar plane or epipolar liens.
33 * The consequence is that triangularization of these matches will fail or
34 * produce inaccurate results.
35 * By fixing matched points using a corrector following a given epipolar
36 * geometry, this effect is alleviated.
37 * This corrector uses the Gold Standard method, which is more expensive to
38 * compute than the Sampson approximation, but is capable to remove larger
39 * errors assuming their gaussianity. Contrary to the Sampson corrector, the
40 * Gold Standard method might fail in some situations, while in those cases
41 * probably the Sampson corrector produces wrong results without failing.
42 */
43 public class GoldStandardCorrector extends Corrector {
44
45 /**
46 * Indicates that correction must use Sampson method as a fallback if Gold
47 * Standard correction fails.
48 */
49 public static final boolean DEFAULT_FALLBACK_TO_SAMPSON_ENABLED = true;
50
51 /**
52 * Indicates whether correction must use Sampson method as a fallback if Gold
53 * Standard correction fails.
54 */
55 private boolean fallbackToSampsonEnabled;
56
57 /**
58 * Constructor.
59 */
60 public GoldStandardCorrector() {
61 super();
62 fallbackToSampsonEnabled = DEFAULT_FALLBACK_TO_SAMPSON_ENABLED;
63 }
64
65 /**
66 * Constructor.
67 *
68 * @param fundamentalMatrix fundamental matrix to be set.
69 */
70 public GoldStandardCorrector(final FundamentalMatrix fundamentalMatrix) {
71 super(fundamentalMatrix);
72 fallbackToSampsonEnabled = DEFAULT_FALLBACK_TO_SAMPSON_ENABLED;
73 }
74
75 /**
76 * Constructor.
77 *
78 * @param leftPoints points to be corrected on left view.
79 * @param rightPoints points to be corrected on right view.
80 * @throws IllegalArgumentException if provided lists of points don't have
81 * the same size.
82 */
83 public GoldStandardCorrector(final List<Point2D> leftPoints, final List<Point2D> rightPoints) {
84 super(leftPoints, rightPoints);
85 fallbackToSampsonEnabled = DEFAULT_FALLBACK_TO_SAMPSON_ENABLED;
86 }
87
88 /**
89 * Constructor.
90 *
91 * @param leftPoints points to be corrected on left view.
92 * @param rightPoints points to be corrected on right view.
93 * @param fundamentalMatrix fundamental matrix to be set.
94 * @throws IllegalArgumentException if provided lists of points don't have
95 * the same size.
96 */
97 public GoldStandardCorrector(final List<Point2D> leftPoints, final List<Point2D> rightPoints,
98 final FundamentalMatrix fundamentalMatrix) {
99 super(leftPoints, rightPoints, fundamentalMatrix);
100 fallbackToSampsonEnabled = DEFAULT_FALLBACK_TO_SAMPSON_ENABLED;
101 }
102
103 /**
104 * Constructor.
105 *
106 * @param listener listener to handle events generated by this class.
107 */
108 public GoldStandardCorrector(final CorrectorListener listener) {
109 super(listener);
110 fallbackToSampsonEnabled = DEFAULT_FALLBACK_TO_SAMPSON_ENABLED;
111 }
112
113 /**
114 * Constructor.
115 *
116 * @param fundamentalMatrix fundamental matrix to be set.
117 * @param listener listener to handle events generated by this class.
118 */
119 public GoldStandardCorrector(final FundamentalMatrix fundamentalMatrix, final CorrectorListener listener) {
120 super(fundamentalMatrix, listener);
121 fallbackToSampsonEnabled = DEFAULT_FALLBACK_TO_SAMPSON_ENABLED;
122 }
123
124 /**
125 * Constructor.
126 *
127 * @param leftPoints points to be corrected on left view.
128 * @param rightPoints points to be corrected on right view.
129 * @param listener listener to handle events generated by this class.
130 * @throws IllegalArgumentException if provided lists of points don't have
131 * the same size.
132 */
133 public GoldStandardCorrector(final List<Point2D> leftPoints, final List<Point2D> rightPoints,
134 final CorrectorListener listener) {
135 super(leftPoints, rightPoints, listener);
136 fallbackToSampsonEnabled = DEFAULT_FALLBACK_TO_SAMPSON_ENABLED;
137 }
138
139 /**
140 * Constructor.
141 *
142 * @param leftPoints points to be corrected on left view.
143 * @param rightPoints points to be corrected on right view.
144 * @param fundamentalMatrix fundamental matrix to be set.
145 * @param listener listener to handle events generated by this class.
146 * @throws IllegalArgumentException if provided lists of points don't have
147 * the same size.
148 */
149 public GoldStandardCorrector(
150 final List<Point2D> leftPoints, final List<Point2D> rightPoints, final FundamentalMatrix fundamentalMatrix,
151 final CorrectorListener listener) {
152 super(leftPoints, rightPoints, fundamentalMatrix, listener);
153 fallbackToSampsonEnabled = DEFAULT_FALLBACK_TO_SAMPSON_ENABLED;
154 }
155
156 /**
157 * Indicates whether correction must use Sampson method as a fallback if Gold
158 * Standard correction fails.
159 * If true, whenever the correction of a matched pair of points fails,
160 * its correction will be done with Sampson method instead. This allows
161 * to continue correcting all points at the expense of getting worse
162 * results in those points where Gold Standard method failed.
163 * If false, whenever a single pair of matched pair of points fails to
164 * be corrected, the algorithm stops and no points are corrected at all.
165 * By default, fallback is enabled.
166 *
167 * @return true if fallback is enabled, false otherwise.
168 */
169 public boolean isFallbackToSampsonEnabled() {
170 return fallbackToSampsonEnabled;
171 }
172
173 /**
174 * Sets boolean indicating whether correction must use Sampson
175 * method as a fallback if Gold Standard correction fails.
176 * If true, whenever the correction of a matched pair of points fails,
177 * its correction will be done with Sampson method instead. This allows
178 * to continue correcting all points at the expense of getting worse
179 * results in those points where Gold Standard method failed.
180 * If false, whenever a single pair of matched pair of points fails to
181 * be corrected, the algorithm stops and no points are corrected at all.
182 * By default, fallback is enabled.
183 *
184 * @param fallback true to enable fallback, false otherwise.
185 * @throws LockedException if this instance is locked doing computations.
186 */
187 public void setFallbackToSampsonEnabled(final boolean fallback) throws LockedException {
188 if (isLocked()) {
189 throw new LockedException();
190 }
191
192 fallbackToSampsonEnabled = fallback;
193 }
194
195 /**
196 * Corrects the lists of provided matched points to be corrected.
197 *
198 * @throws NotReadyException if this instance is not ready (either points or
199 * fundamental matrix has not been provided yet).
200 * @throws LockedException if this instance is locked doing computations.
201 * @throws CorrectionException if correction fails.
202 */
203 @SuppressWarnings("DuplicatedCode")
204 @Override
205 public void correct() throws NotReadyException, LockedException, CorrectionException {
206 if (isLocked()) {
207 throw new LockedException();
208 }
209 if (!isReady()) {
210 throw new NotReadyException();
211 }
212
213 locked = true;
214
215 if (listener != null) {
216 listener.onCorrectStart(this);
217 }
218
219 leftCorrectedPoints = new ArrayList<>();
220 rightCorrectedPoints = new ArrayList<>();
221
222 final var size = leftPoints.size();
223 float progress;
224 var previousProgress = 0.0f;
225 for (var i = 0; i < size; i++) {
226 final var leftPoint = leftPoints.get(i);
227 final var rightPoint = rightPoints.get(i);
228
229 final var leftCorrectedPoint = Point2D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
230 final var rightCorrectedPoint = Point2D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
231
232 // correct single pair
233 try {
234 GoldStandardSingleCorrector.correct(leftPoint, rightPoint, fundamentalMatrix, leftCorrectedPoint,
235 rightCorrectedPoint);
236 } catch (final CorrectionException e) {
237 if (fallbackToSampsonEnabled) {
238 // try using Sampson method instead
239 SampsonSingleCorrector.correct(leftPoint, rightPoint, fundamentalMatrix, leftCorrectedPoint,
240 rightCorrectedPoint);
241 } else {
242 // let algorithm fail
243 throw e;
244 }
245 }
246
247 leftCorrectedPoints.add(leftCorrectedPoint);
248 rightCorrectedPoints.add(rightCorrectedPoint);
249
250 if (listener != null) {
251 progress = i / (float) size;
252 if (progress - previousProgress > progressDelta) {
253 // progress has changed significantly
254 previousProgress = progress;
255 listener.onCorrectProgressChange(this, progress);
256 }
257 }
258 }
259
260 if (listener != null) {
261 listener.onCorrectEnd(this);
262 }
263
264 locked = false;
265 }
266
267 /**
268 * Gets type of correction being used.
269 *
270 * @return type of correction.
271 */
272 @Override
273 public CorrectorType getType() {
274 return CorrectorType.GOLD_STANDARD;
275 }
276 }