View Javadoc
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 }