1 /*
2 * Copyright (C) 2012 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;
17
18 import java.util.ArrayList;
19 import java.util.List;
20
21 /**
22 * A camera defines relations between 3D and 2D worlds.
23 */
24 public abstract class Camera {
25
26 /**
27 * Constant defining default camera type.
28 */
29 public static final CameraType DEFAULT_CAMERA_TYPE = CameraType.PINHOLE_CAMERA;
30
31 /**
32 * Projects a 3D point into a 2D point in a retinal plane.
33 *
34 * @param point 3D point to be projected.
35 * @return 2D projected point.
36 */
37 public Point2D project(final Point3D point) {
38 final var projected = Point2D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
39 project(point, projected);
40 return projected;
41 }
42
43 /**
44 * Projects a 3D point into a 2D point in a retinal plane and stores the
45 * result into provided instance.
46 *
47 * @param inputPoint 3D point to be projected.
48 * @param result Instance where 2D projected point result is stored.
49 */
50 public abstract void project(final Point3D inputPoint, final Point2D result);
51
52 /**
53 * Projects 3D points into 2D points in a retinal plane.
54 *
55 * @param points 3D points to be projected.
56 * @return 2D projected points.
57 */
58 public List<Point2D> project(final List<Point3D> points) {
59 final var projected = new ArrayList<Point2D>(points.size());
60 project(points, projected);
61 return projected;
62 }
63
64 /**
65 * Projects 3D points into 2D points in a retinal plane and stores the
66 * result into provided list.
67 * Note that if result list is not empty, it will be cleared when calling
68 * this method and then the estimated 2D points will be stored in it.
69 *
70 * @param inputPoints 3D points to be projected.
71 * @param result Instance where 2D projected points are stored.
72 */
73 public void project(final List<Point3D> inputPoints, final List<Point2D> result) {
74 result.clear();
75 for (final var point : inputPoints) {
76 result.add(project(point));
77 }
78 }
79
80 /**
81 * Back-projects a 2D line into a 3D plane.
82 *
83 * @param line 2D line to be back-projected.
84 * @return 3D plane that has been back-projected.
85 */
86 public Plane backProject(final Line2D line) {
87 final var plane = new Plane();
88 backProject(line, plane);
89 return plane;
90 }
91
92 // M^T * PLANE = 0
93 // m^T * l = 0
94 // m = P * M
95 // (P * M)^T * l = 0 --> M^T * (P^T * l) = 0
96 // PLANE = P^T * l
97
98 /**
99 * Back-projects a line into a plane and stores the result into provided
100 * instance.
101 *
102 * @param line 2D line to be back-projected.
103 * @param result Instance where computed back-projected 3D plane data is
104 * stored.
105 */
106 public abstract void backProject(final Line2D line, final Plane result);
107
108 /**
109 * Back-projects provided 2D lines into their corresponding 3D planes.
110 *
111 * @param lines 2D lines to be back-projected.
112 * @return 3D planes that have been back-projected.
113 */
114 public List<Plane> backProjectLines(final List<Line2D> lines) {
115 final var planes = new ArrayList<Plane>(lines.size());
116 backProjectLines(lines, planes);
117 return planes;
118 }
119
120 /**
121 * Back-projects provided 2D lines into their corresponding 3D planes and
122 * stores the result into provided list.
123 * Note that if result list is not empty, its contents will be cleared when
124 * calling this method and then the estimated 3D planes will be stored in it.
125 *
126 * @param lines 2D lines to be back-projected.
127 * @param result 3D planes that have been back-projected.
128 */
129 public void backProjectLines(final List<Line2D> lines, final List<Plane> result) {
130 result.clear();
131 for (final var line : lines) {
132 result.add(backProject(line));
133 }
134 }
135
136 // Note: multiple point3D points can be back-projected (indeed is a ray of
137 // light). In other words, solution is not unique
138
139 /**
140 * Back-projects provided 2D point into a 3D point.
141 * Notice that estimated solution is not unique, since back-projecting a 2D
142 * point results in an infinite number of 3D points located in the same
143 * ray of light.
144 * This method only returns one possible solution. Any possible solution can
145 * be computed as a linear combination between the camera center and the
146 * estimated point.
147 *
148 * @param point 2D point to be back-projected.
149 * @return A back-projected 3D point.
150 * @throws CameraException thrown if 2D point cannot be back-projected
151 * because camera is degenerate.
152 */
153 public Point3D backProject(final Point2D point) throws CameraException {
154 final var result = Point3D.create();
155 backProject(point, result);
156 return result;
157 }
158
159 /**
160 * Back-projects provided 2D point into a 3D point and stores the result into
161 * provided instance.
162 * Notice that estimated solution is not unique, since back-projecting a 2D
163 * point results in an infinite number of 3D points located in the same
164 * ray of light.
165 * This method only computes one possible solution. Any other solution can
166 * be computed as a linear combination between the camera center and the
167 * estimated back-projected point.
168 *
169 * @param point 2D point to be back-projected.
170 * @param result Instance where back-projected 3D point data will be stored
171 * @throws CameraException thrown if 2D point cannot be back-projected
172 * because camera is degenerate.
173 */
174 public abstract void backProject(final Point2D point, final Point3D result) throws CameraException;
175
176 /**
177 * Back-projects provided 2D points into their corresponding 3D points.
178 *
179 * @param points 2D points to be back-projected.
180 * @return 3D points that have been back-projected.
181 * @throws CameraException thrown if 2D point cannot be back-projected
182 * because camera is degenerate.
183 */
184 public List<Point3D> backProjectPoints(final List<Point2D> points) throws CameraException {
185 final var result = new ArrayList<Point3D>(points.size());
186 backProjectPoints(points, result);
187 return result;
188 }
189
190 /**
191 * Back-projects provided 2D points into their corresponding 3D points and
192 * stores the result into provided list.
193 * Note that if result list is not empty, its contents will be cleared when
194 * calling this method and then the estimated 3D points will be stored in it.
195 *
196 * @param points 2D points to be back-projected.
197 * @param result 3D points that have been back-projected.
198 * @throws CameraException thrown if 2D point cannot be back-projected
199 * because camera is degenerate.
200 */
201 public void backProjectPoints(final List<Point2D> points, final List<Point3D> result) throws CameraException {
202
203 result.clear();
204 for (final var point : points) {
205 result.add(backProject(point));
206 }
207 }
208
209 /**
210 * Back-projects a 2D conic into a 3D quadric.
211 *
212 * @param conic 2D conic to be back-projected.
213 * @return A back-projected 3D quadric.
214 */
215 public Quadric backProject(final Conic conic) {
216 final var quadric = new Quadric();
217 backProject(conic, quadric);
218 return quadric;
219 }
220
221 /**
222 * Back-projects a 2D conic into a 3D quadric and stores the result into
223 * provided instance.
224 *
225 * @param conic 2D conic to be back-projected.
226 * @param result Instance where data of back-projected 3D quadric will be
227 * stored.
228 */
229 public abstract void backProject(final Conic conic, final Quadric result);
230
231 /**
232 * Projects a 3D dual quadric into a 2D dual conic.
233 *
234 * @param dualQuadric 3D dual quadric to be projected.
235 * @return A 2D dual conic.
236 */
237 public DualConic project(final DualQuadric dualQuadric) {
238 final var dualConic = new DualConic();
239 project(dualQuadric, dualConic);
240 return dualConic;
241 }
242
243 /**
244 * Projects a 3D dual quadric into a 2D dual conic and stores the result
245 * into provided instance.
246 *
247 * @param dualQuadric 3D dual quadric to be projected.
248 * @param result Instance where data of projected 2D dual conic will be
249 * stored.
250 */
251 public abstract void project(final DualQuadric dualQuadric, final DualConic result);
252
253 /**
254 * Projects a 3D quadric into a 2D conic.
255 * The internal implementation of this method needs to compute the dual
256 * quadric of provided quadric, and also needs to compute the resulting
257 * conic from an internal dual conic, for that reason a CameraException might
258 * be raised if provided quadric is degenerate or if internal estimated dual
259 * conic is degenerate and cannot be converted into a conic.
260 *
261 * @param quadric 3D quadric to be projected.
262 * @return A projected 2D conic.
263 * @throws CameraException thrown if there are geometric degeneracies.
264 */
265 public Conic project(final Quadric quadric) throws CameraException {
266 final var conic = new Conic();
267 project(quadric, conic);
268 return conic;
269 }
270
271 /**
272 * Projects a 3D quadric into a 2D conic and stores the result into provided
273 * conic instance.
274 * The internal implementation of this method needs to compute the dual
275 * quadric of provided quadric, and also needs to compute the resulting
276 * conic from an internal dual conic, for that reason a
277 * CameraException might be raised if provided quadric is degenerate or
278 * if internal estimated dual conic is degenerate and cannot be converted
279 * into a conic.
280 *
281 * @param quadric 3D quadric to be projected.
282 * @param result Instance where data of projected 2D conic will be stored.
283 * @throws CameraException thrown if there are geometric degeneracies.
284 */
285 public void project(final Quadric quadric, final Conic result) throws CameraException {
286 quadric.normalize();
287 try {
288 final var dualQuadric = quadric.getDualQuadric();
289 final var dualConic = new DualConic();
290 project(dualQuadric, dualConic);
291 dualConic.conic(result);
292 } catch (final GeometryException e) {
293 throw new CameraException(e);
294 }
295 }
296
297 /**
298 * Back-projects a 2D dual conic into a 3D dual quadric.
299 * The internal implementation of this method needs to compute a conic from
300 * provided dual conic, and also needs to compute the resulting dual quadric
301 * from an internal quadric. For that reason a CameraException might be
302 * raised if provided dual conic is degenerate or if internal estimated
303 * quadric is degenerate and cannot be converted into a dual quadric.
304 *
305 * @param dualConic 2D dual conic to be back-projected.
306 * @return A back-projected 3D dual quadric.
307 * @throws CameraException thrown if there are geometric degeneracies.
308 */
309 public DualQuadric backProject(final DualConic dualConic) throws CameraException {
310 final var dualQuadric = new DualQuadric();
311 backProject(dualConic, dualQuadric);
312 return dualQuadric;
313 }
314
315 /**
316 * Back-projects a 2D dual conic into a 3D dual quadric and stores the result
317 * into provided dual quadric instance.
318 * The internal implementation of this method needs to compute a conic from
319 * provided dual conic, and also needs to compute the resulting dual quadric
320 * from an internal quadric. For that reason a CameraException might be
321 * raised if provided dual conic is degenerate or if internal estimated
322 * quadric is degenerate and cannot be converted into a dual quadric.
323 *
324 * @param dualConic 2D dual conic to be back-projected.
325 * @param result Instance where data of back-projected 3D dual quadric will
326 * be stored.
327 * @throws CameraException thrown if there are geometric degeneracies.
328 */
329 public void backProject(final DualConic dualConic, final DualQuadric result) throws CameraException {
330 try {
331 dualConic.normalize();
332 final var conic = dualConic.getConic();
333 final var quadric = new Quadric();
334 backProject(conic, quadric);
335 quadric.dualQuadric(result);
336 } catch (final GeometryException e) {
337 throw new CameraException(e);
338 }
339 }
340
341 /**
342 * Returns the type of this camera.
343 *
344 * @return Type of this camera.
345 */
346 public abstract CameraType getType();
347
348 /**
349 * Creates an instance of a camera using default type.
350 *
351 * @return A newly instantiated camera.
352 */
353 public static Camera create() {
354 return new PinholeCamera();
355 }
356 }