1 /*
2 * Copyright (C) 2017 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.sfm;
17
18 import com.irurueta.ar.calibration.estimators.DualAbsoluteQuadricEstimator;
19 import com.irurueta.ar.calibration.estimators.LMSEDualAbsoluteQuadricEstimator;
20 import com.irurueta.ar.epipolar.FundamentalMatrix;
21 import com.irurueta.geometry.PinholeCamera;
22 import com.irurueta.geometry.estimators.LockedException;
23 import com.irurueta.geometry.estimators.NotReadyException;
24
25 import java.util.ArrayList;
26
27 /**
28 * Estimates an initial pair of cameras in the metric stratum (up to an
29 * arbitrary scale) using a given fundamental matrix and assuming zero skewness
30 * and principal point at the origin for the intrinsic parameters of estimated
31 * cameras.
32 * Aspect ratio can be configured but by default it is assumed to be 1.0.
33 */
34 public class DualAbsoluteQuadricInitialCamerasEstimator extends InitialCamerasEstimator {
35
36 /**
37 * Aspect ratio of intrinsic parameters of cameras.
38 * Typically, this value is 1.0 if vertical coordinates increase upwards,
39 * or -1.0 if it is the opposite.
40 */
41 private double aspectRatio = DualAbsoluteQuadricEstimator.DEFAULT_FOCAL_DISTANCE_ASPECT_RATIO;
42
43 /**
44 * Constructor.
45 */
46 public DualAbsoluteQuadricInitialCamerasEstimator() {
47 super();
48 }
49
50 /**
51 * Constructor.
52 *
53 * @param fundamentalMatrix fundamental matrix relating two views.
54 */
55 public DualAbsoluteQuadricInitialCamerasEstimator(final FundamentalMatrix fundamentalMatrix) {
56 super(fundamentalMatrix);
57 }
58
59 /**
60 * Constructor.
61 *
62 * @param listener listener to handle events raised by this instance.
63 */
64 public DualAbsoluteQuadricInitialCamerasEstimator(final InitialCamerasEstimatorListener listener) {
65 super(listener);
66 }
67
68 /**
69 * Constructor.
70 *
71 * @param fundamentalMatrix fundamental matrix relating two views.
72 * @param listener listener to handle events raised by this instance.
73 */
74 public DualAbsoluteQuadricInitialCamerasEstimator(
75 final FundamentalMatrix fundamentalMatrix, final InitialCamerasEstimatorListener listener) {
76 super(fundamentalMatrix, listener);
77 }
78
79 /**
80 * Returns method used by this estimator.
81 *
82 * @return method used by this estimator.
83 */
84 @Override
85 public InitialCamerasEstimatorMethod getMethod() {
86 return InitialCamerasEstimatorMethod.DUAL_ABSOLUTE_QUADRIC;
87 }
88
89 /**
90 * Indicates if estimator is ready.
91 *
92 * @return true if estimator is ready, false otherwise.
93 */
94 @Override
95 public boolean isReady() {
96 return fundamentalMatrix != null;
97 }
98
99 /**
100 * Estimates cameras.
101 *
102 * @throws LockedException if estimator is locked.
103 * @throws NotReadyException if estimator is not ready.
104 * @throws InitialCamerasEstimationFailedException if estimation of cameras
105 * fails for some reason, typically due to numerical
106 * instabilities.
107 */
108 @Override
109 public void estimate() throws LockedException, NotReadyException, InitialCamerasEstimationFailedException {
110 if (isLocked()) {
111 throw new LockedException();
112 }
113
114 if (!isReady()) {
115 throw new NotReadyException();
116 }
117
118 try {
119 locked = true;
120
121 if (listener != null) {
122 listener.onStart(this);
123 }
124
125 if (estimatedLeftCamera == null) {
126 estimatedLeftCamera = new PinholeCamera();
127 }
128 if (estimatedRightCamera == null) {
129 estimatedRightCamera = new PinholeCamera();
130 }
131
132 generateInitialMetricCamerasUsingDAQ(fundamentalMatrix, aspectRatio, estimatedLeftCamera,
133 estimatedRightCamera);
134
135 if (listener != null) {
136 listener.onFinish(this, estimatedLeftCamera, estimatedRightCamera);
137 }
138 } catch (final InitialCamerasEstimationFailedException e) {
139 if (listener != null) {
140 listener.onFail(this, e);
141 }
142 throw e;
143 } finally {
144 locked = false;
145 }
146 }
147
148 /**
149 * Gets aspect ratio of intrinsic parameters of cameras.
150 * Typically, this value is 1.0 if vertical coordinates increase upwards,
151 * or -1.0 if it is the opposite.
152 *
153 * @return aspect ratio of intrinsic parameters of cameras.
154 */
155 public double getAspectRatio() {
156 return aspectRatio;
157 }
158
159 /**
160 * Sets aspect ratio of intrinsic parameters of cameras.
161 * Typically, this value is 1.0 if vertical coordinates increase upwards,
162 * or -1.0 if it is the opposite.
163 *
164 * @param aspectRatio aspect ratio of intrinsic parameters of cameras.
165 * @throws LockedException if estimator is locked.
166 */
167 public void setAspectRatio(final double aspectRatio) throws LockedException {
168 if (isLocked()) {
169 throw new LockedException();
170 }
171 this.aspectRatio = aspectRatio;
172 }
173
174 /**
175 * Generates initial cameras in metric stratum (with arbitrary scale) using
176 * provided fundamental matrix by means of estimation of the Dual Absolute
177 * Quadric and the required projective to metric transformation.
178 *
179 * @param fundamentalMatrix input fundamental matrix to estimate cameras.
180 * @param leftCamera instance where left camera will be stored.
181 * @param rightCamera instance where right camera will be stored.
182 * @throws InitialCamerasEstimationFailedException if estimation fails.
183 */
184 public static void generateInitialMetricCamerasUsingDAQ(
185 final FundamentalMatrix fundamentalMatrix,
186 final PinholeCamera leftCamera,
187 final PinholeCamera rightCamera)
188 throws InitialCamerasEstimationFailedException {
189 generateInitialMetricCamerasUsingDAQ(fundamentalMatrix,
190 DualAbsoluteQuadricEstimator.DEFAULT_FOCAL_DISTANCE_ASPECT_RATIO, leftCamera, rightCamera);
191 }
192
193 /**
194 * Generates initial cameras in metric stratum (with arbitrary scale) using
195 * provided fundamental matrix by means of estimation of the Dual Absolute
196 * Quadric and the required projective to metric transformation.
197 *
198 * @param fundamentalMatrix input fundamental matrix to estimate cameras.
199 * @param aspectRatio aspect ratio of intrinsic parameters of cameras.
200 * Typically, this value is 1.0 if vertical coordinates increase upwards or
201 * -1.0 if it is the opposite.
202 * @param leftCamera instance where left camera will be stored.
203 * @param rightCamera instance where right camera will be stored.
204 * @throws InitialCamerasEstimationFailedException if estimation fails.
205 */
206 public static void generateInitialMetricCamerasUsingDAQ(
207 final FundamentalMatrix fundamentalMatrix,
208 final double aspectRatio,
209 final PinholeCamera leftCamera,
210 final PinholeCamera rightCamera)
211 throws InitialCamerasEstimationFailedException {
212
213 try {
214 // generate arbitrary projective cameras
215 fundamentalMatrix.generateCamerasInArbitraryProjectiveSpace(leftCamera, rightCamera);
216
217 final var cameras = new ArrayList<PinholeCamera>();
218 cameras.add(leftCamera);
219 cameras.add(rightCamera);
220
221 // estimate dual absolute quadric
222 final var daqEstimator = new LMSEDualAbsoluteQuadricEstimator(cameras);
223 daqEstimator.setLMSESolutionAllowed(false);
224 daqEstimator.setZeroSkewness(true);
225 daqEstimator.setPrincipalPointAtOrigin(true);
226 daqEstimator.setFocalDistanceAspectRatioKnown(true);
227 daqEstimator.setFocalDistanceAspectRatio(aspectRatio);
228 daqEstimator.setSingularityEnforced(true);
229
230 final var daq = daqEstimator.estimate();
231 final var transformation = daq.getMetricToProjectiveTransformation();
232 // inverse transformation to upgrade cameras from projective to
233 // metric stratum
234 transformation.inverse();
235
236 // transform cameras
237 transformation.transform(leftCamera);
238 transformation.transform(rightCamera);
239 } catch (final Exception e) {
240 throw new InitialCamerasEstimationFailedException(e);
241 }
242 }
243 }