Camera.java
/*
* Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.irurueta.geometry;
import java.util.ArrayList;
import java.util.List;
/**
* A camera defines relations between 3D and 2D worlds.
*/
public abstract class Camera {
/**
* Constant defining default camera type.
*/
public static final CameraType DEFAULT_CAMERA_TYPE = CameraType.PINHOLE_CAMERA;
/**
* Projects a 3D point into a 2D point in a retinal plane.
*
* @param point 3D point to be projected.
* @return 2D projected point.
*/
public Point2D project(final Point3D point) {
final var projected = Point2D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
project(point, projected);
return projected;
}
/**
* Projects a 3D point into a 2D point in a retinal plane and stores the
* result into provided instance.
*
* @param inputPoint 3D point to be projected.
* @param result Instance where 2D projected point result is stored.
*/
public abstract void project(final Point3D inputPoint, final Point2D result);
/**
* Projects 3D points into 2D points in a retinal plane.
*
* @param points 3D points to be projected.
* @return 2D projected points.
*/
public List<Point2D> project(final List<Point3D> points) {
final var projected = new ArrayList<Point2D>(points.size());
project(points, projected);
return projected;
}
/**
* Projects 3D points into 2D points in a retinal plane and stores the
* result into provided list.
* Note that if result list is not empty, it will be cleared when calling
* this method and then the estimated 2D points will be stored in it.
*
* @param inputPoints 3D points to be projected.
* @param result Instance where 2D projected points are stored.
*/
public void project(final List<Point3D> inputPoints, final List<Point2D> result) {
result.clear();
for (final var point : inputPoints) {
result.add(project(point));
}
}
/**
* Back-projects a 2D line into a 3D plane.
*
* @param line 2D line to be back-projected.
* @return 3D plane that has been back-projected.
*/
public Plane backProject(final Line2D line) {
final var plane = new Plane();
backProject(line, plane);
return plane;
}
// M^T * PLANE = 0
// m^T * l = 0
// m = P * M
// (P * M)^T * l = 0 --> M^T * (P^T * l) = 0
// PLANE = P^T * l
/**
* Back-projects a line into a plane and stores the result into provided
* instance.
*
* @param line 2D line to be back-projected.
* @param result Instance where computed back-projected 3D plane data is
* stored.
*/
public abstract void backProject(final Line2D line, final Plane result);
/**
* Back-projects provided 2D lines into their corresponding 3D planes.
*
* @param lines 2D lines to be back-projected.
* @return 3D planes that have been back-projected.
*/
public List<Plane> backProjectLines(final List<Line2D> lines) {
final var planes = new ArrayList<Plane>(lines.size());
backProjectLines(lines, planes);
return planes;
}
/**
* Back-projects provided 2D lines into their corresponding 3D planes and
* stores the result into provided list.
* Note that if result list is not empty, its contents will be cleared when
* calling this method and then the estimated 3D planes will be stored in it.
*
* @param lines 2D lines to be back-projected.
* @param result 3D planes that have been back-projected.
*/
public void backProjectLines(final List<Line2D> lines, final List<Plane> result) {
result.clear();
for (final var line : lines) {
result.add(backProject(line));
}
}
// Note: multiple point3D points can be back-projected (indeed is a ray of
// light). In other words, solution is not unique
/**
* Back-projects provided 2D point into a 3D point.
* Notice that estimated solution is not unique, since back-projecting a 2D
* point results in an infinite number of 3D points located in the same
* ray of light.
* This method only returns one possible solution. Any possible solution can
* be computed as a linear combination between the camera center and the
* estimated point.
*
* @param point 2D point to be back-projected.
* @return A back-projected 3D point.
* @throws CameraException thrown if 2D point cannot be back-projected
* because camera is degenerate.
*/
public Point3D backProject(final Point2D point) throws CameraException {
final var result = Point3D.create();
backProject(point, result);
return result;
}
/**
* Back-projects provided 2D point into a 3D point and stores the result into
* provided instance.
* Notice that estimated solution is not unique, since back-projecting a 2D
* point results in an infinite number of 3D points located in the same
* ray of light.
* This method only computes one possible solution. Any other solution can
* be computed as a linear combination between the camera center and the
* estimated back-projected point.
*
* @param point 2D point to be back-projected.
* @param result Instance where back-projected 3D point data will be stored
* @throws CameraException thrown if 2D point cannot be back-projected
* because camera is degenerate.
*/
public abstract void backProject(final Point2D point, final Point3D result) throws CameraException;
/**
* Back-projects provided 2D points into their corresponding 3D points.
*
* @param points 2D points to be back-projected.
* @return 3D points that have been back-projected.
* @throws CameraException thrown if 2D point cannot be back-projected
* because camera is degenerate.
*/
public List<Point3D> backProjectPoints(final List<Point2D> points) throws CameraException {
final var result = new ArrayList<Point3D>(points.size());
backProjectPoints(points, result);
return result;
}
/**
* Back-projects provided 2D points into their corresponding 3D points and
* stores the result into provided list.
* Note that if result list is not empty, its contents will be cleared when
* calling this method and then the estimated 3D points will be stored in it.
*
* @param points 2D points to be back-projected.
* @param result 3D points that have been back-projected.
* @throws CameraException thrown if 2D point cannot be back-projected
* because camera is degenerate.
*/
public void backProjectPoints(final List<Point2D> points, final List<Point3D> result) throws CameraException {
result.clear();
for (final var point : points) {
result.add(backProject(point));
}
}
/**
* Back-projects a 2D conic into a 3D quadric.
*
* @param conic 2D conic to be back-projected.
* @return A back-projected 3D quadric.
*/
public Quadric backProject(final Conic conic) {
final var quadric = new Quadric();
backProject(conic, quadric);
return quadric;
}
/**
* Back-projects a 2D conic into a 3D quadric and stores the result into
* provided instance.
*
* @param conic 2D conic to be back-projected.
* @param result Instance where data of back-projected 3D quadric will be
* stored.
*/
public abstract void backProject(final Conic conic, final Quadric result);
/**
* Projects a 3D dual quadric into a 2D dual conic.
*
* @param dualQuadric 3D dual quadric to be projected.
* @return A 2D dual conic.
*/
public DualConic project(final DualQuadric dualQuadric) {
final var dualConic = new DualConic();
project(dualQuadric, dualConic);
return dualConic;
}
/**
* Projects a 3D dual quadric into a 2D dual conic and stores the result
* into provided instance.
*
* @param dualQuadric 3D dual quadric to be projected.
* @param result Instance where data of projected 2D dual conic will be
* stored.
*/
public abstract void project(final DualQuadric dualQuadric, final DualConic result);
/**
* Projects a 3D quadric into a 2D conic.
* The internal implementation of this method needs to compute the dual
* quadric of provided quadric, and also needs to compute the resulting
* conic from an internal dual conic, for that reason a CameraException might
* be raised if provided quadric is degenerate or if internal estimated dual
* conic is degenerate and cannot be converted into a conic.
*
* @param quadric 3D quadric to be projected.
* @return A projected 2D conic.
* @throws CameraException thrown if there are geometric degeneracies.
*/
public Conic project(final Quadric quadric) throws CameraException {
final var conic = new Conic();
project(quadric, conic);
return conic;
}
/**
* Projects a 3D quadric into a 2D conic and stores the result into provided
* conic instance.
* The internal implementation of this method needs to compute the dual
* quadric of provided quadric, and also needs to compute the resulting
* conic from an internal dual conic, for that reason a
* CameraException might be raised if provided quadric is degenerate or
* if internal estimated dual conic is degenerate and cannot be converted
* into a conic.
*
* @param quadric 3D quadric to be projected.
* @param result Instance where data of projected 2D conic will be stored.
* @throws CameraException thrown if there are geometric degeneracies.
*/
public void project(final Quadric quadric, final Conic result) throws CameraException {
quadric.normalize();
try {
final var dualQuadric = quadric.getDualQuadric();
final var dualConic = new DualConic();
project(dualQuadric, dualConic);
dualConic.conic(result);
} catch (final GeometryException e) {
throw new CameraException(e);
}
}
/**
* Back-projects a 2D dual conic into a 3D dual quadric.
* The internal implementation of this method needs to compute a conic from
* provided dual conic, and also needs to compute the resulting dual quadric
* from an internal quadric. For that reason a CameraException might be
* raised if provided dual conic is degenerate or if internal estimated
* quadric is degenerate and cannot be converted into a dual quadric.
*
* @param dualConic 2D dual conic to be back-projected.
* @return A back-projected 3D dual quadric.
* @throws CameraException thrown if there are geometric degeneracies.
*/
public DualQuadric backProject(final DualConic dualConic) throws CameraException {
final var dualQuadric = new DualQuadric();
backProject(dualConic, dualQuadric);
return dualQuadric;
}
/**
* Back-projects a 2D dual conic into a 3D dual quadric and stores the result
* into provided dual quadric instance.
* The internal implementation of this method needs to compute a conic from
* provided dual conic, and also needs to compute the resulting dual quadric
* from an internal quadric. For that reason a CameraException might be
* raised if provided dual conic is degenerate or if internal estimated
* quadric is degenerate and cannot be converted into a dual quadric.
*
* @param dualConic 2D dual conic to be back-projected.
* @param result Instance where data of back-projected 3D dual quadric will
* be stored.
* @throws CameraException thrown if there are geometric degeneracies.
*/
public void backProject(final DualConic dualConic, final DualQuadric result) throws CameraException {
try {
dualConic.normalize();
final var conic = dualConic.getConic();
final var quadric = new Quadric();
backProject(conic, quadric);
quadric.dualQuadric(result);
} catch (final GeometryException e) {
throw new CameraException(e);
}
}
/**
* Returns the type of this camera.
*
* @return Type of this camera.
*/
public abstract CameraType getType();
/**
* Creates an instance of a camera using default type.
*
* @return A newly instantiated camera.
*/
public static Camera create() {
return new PinholeCamera();
}
}