Pinhole Camera
| This documentation was generated with the assistance of AI. Please report any inaccuracies. |
PinholeCamera is where irurueta-geometry’s 3D and 2D worlds meet: it maps 3D points, lines, planes, conics and
quadrics onto their 2D image counterparts (points, lines, conics) using the standard linear (projective) pinhole
model. This page follows the treatment in chapter 2, "Camera models", of
Irurueta’s PhD thesis — the repository owner’s own dissertation — cross-checked
against chapters 6 and 8 of Hartley & Zisserman, and grounded line-by-line
in the actual `PinholeCamera, Camera and PinholeCameraIntrinsicParameters source.
From pinhole to matrix: why a camera is a P3 → P2 map
A pinhole camera has no lens: light passes through a single small aperture and forms an inverted image on a
photographic plate behind it (PHD
§2.1, fig. 2.1). Equivalently, and more
conveniently, one can place a virtual retinal plane in front of the projection center and reason with the
(non-inverted) image formed there. Ignoring lens effects — most notably radial distortion, which bends straight
lines near the image border and is not modeled by PinholeCamera at all (see the aside below) — this projection
is exactly linear once points are expressed in homogeneous coordinates, which is the whole reason projective
geometry is the natural language for it.
Put the camera center at the origin, pointing down the Z axis, with the retinal plane at . A 3D point projects to (PHD eq. 2.1). In homogeneous coordinates this inhomogeneous division disappears and the mapping becomes linear:
which is exactly the canonical camera ] (PHD eq. 2.2-2.3; HZ §6.1, "the basic pinhole model"). Every other camera in this library — however it was constructed or estimated — is this same linear idea generalized with a focal length, a principal-point offset, a pixel/skew correction, and an arbitrary position and orientation in the world. That generalization is what the rest of this page unpacks.
Real cameras also exhibit radial distortion (barrel or pincushion, following Brown’s model — see
PHD §2.2.4).
PinholeCamera intentionally does not model it: it is a purely
linear projective camera, so any distortion correction has to happen upstream, on the 2D image coordinates,
before they are handed to this class.
|
Representation: a single 3x4 matrix
A PinholeCamera is stored as one 3×4 projective matrix P (not separately as K, R, C). Starting from
the canonical camera above, adding a focal length , a principal point offset
, a pixel/skew correction (all folded into a single upper-triangular calibration matrix
), and a world-to-camera rigid motion (rotation , camera center ) gives the
classic factorization
(PHD eq. 2.22-2.24; HZ eq. 6.7-6.8). Writing ] with the top-left 3×3 block, a camera is called finite when — this is the case the rest of this page mostly addresses — and a camera at infinity otherwise, whose center lies on the plane at infinity (PHD §2.3; HZ §6.2.1/6.3).
The factorization above is recovered lazily, on demand, via decompose():
public void decompose() throws CameraException {
decompose(DEFAULT_DECOMPOSE_INTRINSICS_AND_ROTATION);
}
public void decompose(final boolean decomposeIntrinsicsAndRotation) throws CameraException {
decompose(decomposeIntrinsicsAndRotation, DEFAULT_DECOMPOSE_CAMERA_CENTER);
}
public void decompose(final boolean decomposeIntrinsicsAndRotation, final boolean decomposeCameraCenter)
throws CameraException {
// clean up previous intrinsics, rotation and camera center
intrinsicParameters = null;
cameraRotation = null;
cameraCenter = null;
if (decomposeIntrinsicsAndRotation) {
computeIntrinsicsAndRotation();
}
if (decomposeCameraCenter) {
if (cameraCenter == null) {
cameraCenter = Point3D.create(CoordinatesType.HOMOGENEOUS_COORDINATES);
}
computeCameraCenterSVD(cameraCenter);
}
}
-
The top-left 3×3 block
Mpis RQ-decomposed (com.irurueta.algebra.RQDecomposer) into an upper-triangularK(intrinsics) and an orthogonalQ(rotation), fixing signs so `K’s diagonal is positive — see "Decomposing `Mp`" below. -
The camera center defaults to the SVD null-space method (
computeCameraCenterSVD), but two cheaper, interchangeable alternatives exist — see "The camera center" below.
The decomposed PinholeCameraIntrinsicParameters, Rotation3D and Point3D (center) are cached and are only
recomputed the next time they are requested after any setter (setInternalMatrix, setIntrinsicParameters,
setCameraRotation, …) invalidates the cache; areIntrinsicParametersAvailable(), isCameraRotationAvailable()
and isCameraCenterAvailable() report whether a cached value already exists without forcing a decomposition.
Intrinsic parameters
PinholeCameraIntrinsicParameters wraps a 3×3 upper-triangular calibration matrix K:
| fx | s | px |
|---|---|---|
0 |
fy |
py |
0 |
0 |
1 |
-
Focal length (§2.2.2.1) — for a retinal plane at distance from the center instead of , the canonical mapping becomes , i.e.
K = diag(f, f, 1). -
Principal point offset (§2.2.2.2) — the retinal plane’s own coordinate origin rarely coincides exactly with where the optical axis pierces it, so an offset is added: , giving
Kits off-diagonalpx/pycolumn. -
CCD pixels and skew (§2.2.2.3) — because image coordinates are measured in pixels, not the physical units of the focal length, world-to-pixel conversion multiplies by the number of pixels per unit length in each direction, , giving separate horizontal/vertical focal lengths , (
fx/fy), and a possible skew term when the pixel grid is not perfectly rectangular (HZ eq. 6.9-6.10 arrives at the identical matrix).
getAspectRatio() returns fy/fx; setAspectRatioKeepingHorizontalFocalLength(double) /
…KeepingVerticalFocalLength(double) adjust it while pinning one focal length. getSkewnessAngle() /
setSkewnessAngle(double) expose the skew as the angle from the
formula above, rather than the raw coefficient s.
Three factories cover the common cases. createCanonicalIntrinsicParameters() returns the identity K (the bare
canonical camera derived above). createTypicalIntrinsicParameters(width, height) centers the principal point on
the image and picks a focal length equal to (width + height) / 2, roughly a 45° field of view — a reasonable
guess when nothing else is known. create(focalLength, sensorWidth, sensorHeight, imageWidth, imageHeight) performs
exactly the CCD-pixel conversion of step 3 above, in physical units (millimeters):
public static PinholeCameraIntrinsicParameters create(
final double focalLength, final double sensorWidth, final double sensorHeight, final int imageWidth,
final int imageHeight) {
// compute the size of a pixel taking into account sensor and image sizes
final var pixelWidth = sensorWidth / imageWidth; // mm/px
final var pixelHeight = sensorHeight / imageHeight; // mm/px
// compute focal lengths expressed in pixels
final var horizontalFocalLength = focalLength / pixelWidth;
final var verticalFocalLength = focalLength / pixelHeight;
return new PinholeCameraIntrinsicParameters(horizontalFocalLength, verticalFocalLength, 0.0,
0.0, 0.0);
}
1 / pixelWidth and 1 / pixelHeight are precisely and above, so
focalLength / pixelWidth — this is
PHD eq. 2.16 read directly
off a datasheet’s focal length and sensor size.
Skew and principal-point offset are assumed zero here, which is the common case for real CCD/CMOS sensors.
getInverseInternalMatrix() computes with a hand-derived closed form from fx, fy, s
and the principal point, rather than a generic matrix inversion, purely for efficiency: K is triangular and
small enough that this is both faster and better conditioned than Utils.inverse.
Decomposing Mp: RQ decomposition into K and R
Given only P, recovering K and R from the top-left 3×3 block Mp = K·R is exactly the problem an
RQ decomposition solves: factor an arbitrary square matrix into an upper-triangular matrix times an orthogonal
one, (note the unfortunate name clash — the "R" in "RQ" is unrelated to the camera’s rotation
matrix, which happens to be the "Q" here). RQ decomposition is not unique up to sign/scale, so the extra
constraints that K’s diagonal be positive and `R be a proper rotation ( , not just orthogonal)
pin down a unique answer, up to the camera matrix’s own overall scale ambiguity
(PHD §2.3.6, eq.
2.74-2.78).
com.irurueta.algebra.RQDecomposer (in the sibling module irurueta-algebra) implements this with Givens
rotations, following HZ's Algorithm A4.1: three successive rotations
about the coordinate axes, each chosen to zero one below-diagonal entry of the matrix without disturbing the
entries already cleared, leaving an upper-triangular R times a rotation Q
(HZ Appendix §A4.1.1, p. 579). This is the same family of
factorizations as the more familiar QR decomposition via Householder reflections
(Numerical Recipes §2.10, pp. 102-106) — RQDecomposer
internally wraps a QRDecomposer rather than reimplementing Givens rotations directly.
PinholeCamera.computeIntrinsicsAndRotation() uses it exactly like this:
final var mMp = internalMatrix.getSubmatrix(0, 0,
PINHOLE_CAMERA_MATRIX_ROWS - 1, PINHOLE_CAMERA_MATRIX_ROWS - 1);
// Use RQ decomposition to obtain intrinsic parameters as R ensuring
// that elements on the diagonal are positive and element (3, 3) is 1,
// and Q is an orthogonal matrix
final var decomposer = new RQDecomposer(mMp);
decomposer.decompose();
final var r = decomposer.getR();
final var q = decomposer.getQ();
The rest of the method (not reproduced in full) multiplies r and q by a suitable
so that r’s diagonal ends up strictly positive and `q ends up
a proper rotation — exactly the sign-fixing recipe of
PHD eq. 2.76-2.78 — before
wrapping the results as
PinholeCameraIntrinsicParameters and MatrixRotation3D.
The camera center: three methods, one geometric fact
The camera center C is the unique 4-vector (up to scale) with — the right
null-space of P. Geometrically, any 3D line through C and another point M is a ray of light: every point on it
projects to the same image point (the projection of M), so the "extra" degree of freedom along the ray must
vanish when multiplied by P, which is exactly
(PHD §2.3.1,
Proof 2.1; HZ
§6.2.1, "camera centre"). Because P is rank 3 with 4 columns, this null-space is always exactly
one-dimensional — the camera always has a well-defined center, even when it lies at infinity (an "affine" or
degenerate camera, ).
That single geometric fact can be turned into a concrete number in three different ways, and PinholeCamera ships
all three because they trade off cost against generality:
computeCameraCenterSVD() (the default used by decompose()). Any null-space is most robustly found via
singular value decomposition: writing P’s SVD as , the column of `V
corresponding to P’s zero singular value is exactly the null-space vector
(PHD §2.3.1.1;
HZ §6.2.4,
"Finding the camera centre", citing its own Appendix §A4.4 for SVD; general SVD algorithm in
Numerical Recipes §2.6, pp. 65-67). It remains correct even
when the true center is at infinity (rank-deficient `Mp) since the SVD makes no finiteness assumption — at the
cost of being the most expensive of the three:
public void computeCameraCenterSVD(final Point3D result) throws CameraException {
normalize(); // to increase accuracy
// camera center is the null-space of camera matrix
final var decomposer = new SingularValueDecomposer(internalMatrix);
decomposer.decompose();
// because camera matrix is at most rank 3, the camera center is the
// last column of decomposed matrix V
final var v = decomposer.getV();
result.setHomogeneousCoordinates(
v.getElementAt(0, PINHOLE_CAMERA_MATRIX_COLS - 1),
v.getElementAt(1, PINHOLE_CAMERA_MATRIX_COLS - 1),
v.getElementAt(2, PINHOLE_CAMERA_MATRIX_COLS - 1),
v.getElementAt(3, PINHOLE_CAMERA_MATRIX_COLS - 1));
}
computeCameraCenterDet(). The same null-space can be obtained algebraically, without a full SVD, from the
determinants of `P’s four 3×3 minors (each formed by dropping one column):
This follows from the fact that the camera center lies on all three of the camera’s row-planes (see below), so
appending any other plane through it to a 3×4 stack yields a rank-deficient, zero-determinant matrix; expanding
that determinant by Laplace’s formula reproduces exactly this cofactor expression
(PHD §2.3.1.2, Proof
2.2). It is cheaper than SVD (four 3×3
determinants via Utils.det, instead of a full decomposition of the whole 3×4 matrix) and, like the SVD
method, remains valid even at infinity. The real method repeats the same four lines (build a 3×3 minor by
copying three of the four columns, take its determinant) once per coordinate, with alternating sign
(abbreviated here for brevity):
public void computeCameraCenterDet(final Point3D result) throws CameraException {
normalize();
final var m = new Matrix(PINHOLE_CAMERA_MATRIX_ROWS, PINHOLE_CAMERA_MATRIX_ROWS);
// build minor using columns 2, 3 and 4 (drop column 1) -> x = det(m)
// build minor using columns 1, 3 and 4 (drop column 2) -> y = -det(m)
// build minor using columns 1, 2 and 4 (drop column 3) -> z = det(m)
// build minor using columns 1, 2 and 3 (drop column 4) -> w = -det(m)
final var x = Utils.det(m);
// ... (y, z, w computed the same way, from the other three minors)
result.setHomogeneousCoordinates(x, y, z, w);
result.normalize();
}
computeCameraCenterFiniteCamera(). If the camera is known to be finite ( invertible), the
center follows directly from
(PHD §2.3.1.3, eq.
2.45-2.46; HZ
§6.2.4, closed form ). This is the cheapest of the three (one 3×3 matrix
inverse instead of a determinant per column or a full SVD) but the only one of the three that requires a
non-degenerate, finite camera — it will fail or be numerically unstable if Mp is singular or ill-conditioned:
public void computeCameraCenterFiniteCamera(final Point3D result) throws CameraException {
normalize();
final var mMp = internalMatrix.getSubmatrix(0, 0,
PINHOLE_CAMERA_MATRIX_ROWS - 1, PINHOLE_CAMERA_MATRIX_ROWS - 1);
final var mInvMp = Utils.inverse(mMp);
final var mP4 = internalMatrix.getSubmatrix(0, PINHOLE_CAMERA_MATRIX_COLS - 1,
PINHOLE_CAMERA_MATRIX_ROWS - 1, PINHOLE_CAMERA_MATRIX_COLS - 1);
mInvMp.multiply(mP4);
result.setInhomogeneousCoordinates(-mInvMp.getElementAtIndex(0), -mInvMp.getElementAtIndex(1),
-mInvMp.getElementAtIndex(2));
result.normalize();
}
In short: use the closed form when the camera is known to be finite and speed matters; fall back to the cofactor method for a cheaper alternative that still tolerates a center at infinity; use SVD (the default) when robustness matters more than speed, or when nothing is known about the camera’s degeneracy in advance.
Vanishing points, axis planes and the principal point
Several classic projective-camera facts fall directly out of P’s own columns and rows, without needing a full
`decompose():
-
Vanishing points —
getXAxisVanishingPoint()/Y/Z(plusxAxisVanishingPoint(Point2D)etc.) return columns 1-3 ofP. Since the world X/Y/Z axis directions are the points at infinity , , , their images and so on are exactly those columns (PHD §2.3.2, eq. 2.47-2.48). -
Image of the world origin —
getImageOfWorldOrigin()/imageOfWorldOrigin(Point2D)return column 4 ofP, the image of (PHD eq. 2.49). -
Axis and principal planes —
getVerticalAxisPlane()/getHorizontalAxisPlane()/getPrincipalPlane()read off rows 1/2/3 ofPas 3D plane coefficients. Each row, read as a plane equation, is satisfied by every point that projects onto one particular image line (the vertical or horizontal image axis) or onto the line at infinity (the principal plane, parallel to the retinal plane); the camera center lies on all three, since holds row by row (PHD §2.3.3, eq. 2.51-2.59; HZ Table 6.1, p. 158). The principal plane’s director vector always points where the camera looks, and the camera center is its locus. -
Principal point —
getPrincipalPoint()/principalPoint(Point2D)compute , the projection of the point at infinity perpendicular to the principal plane, where is the principal plane’s own first three coefficients (PHD §2.3.4, eq. 2.60-2.63; HZ §6.2.1, ). -
Principal axis vector —
getPrincipalAxisArray()/principalAxisArray(double[])return the first three elements of `P’s last row (the principal plane’s director vector), scaled by and re-normalized so that it always points towards the front of the camera regardless of the matrix’s own homogeneous scale — see camera sign below (PHD §2.3.5, eq. 2.66-2.72).
Cheirality, camera sign and depth
Because P is only defined up to an arbitrary non-zero scale, and describe the exact
same camera geometry — yet only one of the two sign choices consistently places 3D points "in front of" rather
than "behind" the lens. Resolving that ambiguity is what camera sign and cheirality are for.
getCameraSign() / getCameraSign(double threshold) return the sign of . This works because
scaling P by any scales by and the third row of Mp by k,
so the vector is invariant to regardless of the sign of
k — it always points toward the front of the camera
(PHD §2.3.5, eq.
2.65-2.72;
HZ §6.2.1, principal axis vector). fixCameraSign() multiplies the
whole matrix by that sign once, and isCameraSignFixed() reports whether that has already happened (any setter
resets the flag).
getDepth(Point3D point) computes the signed distance of a 3D point from the principal plane, along the principal
axis:
(PHD §2.4.1, eq.
2.79-2.82; HZ
Result 6.1, eq. 6.15). In code this is exactly the dot product of (point - cameraCenter) with the already
sign-fixed, unit-length principalAxisArray():
public double getDepth(final Point3D point) throws CameraException {
if (!isCameraCenterAvailable()) {
cameraCenter = computeCameraCenterSVD();
}
final var principalAxis = getPrincipalAxisArray();
final var diff = new double[INHOM_COORDS];
diff[0] = point.getInhomX() - cameraCenter.getInhomX();
diff[1] = point.getInhomY() - cameraCenter.getInhomY();
diff[2] = point.getInhomZ() - cameraCenter.getInhomZ();
return ArrayUtils.dotProduct(principalAxis, diff);
}
getCheirality(Point3D point) answers the cheaper yes/no question — is the point in front of, or behind, the
camera — without computing an actual distance. It reduces to the sign of the last homogeneous coordinate of the
projected point times the last homogeneous coordinate of the 3D point,
(PHD §2.4.2, eq.
2.83-2.96), corrected by the camera sign whenever it has
not already been fixed:
public double getCheirality(final Point3D point) throws CameraException {
point.normalize();
normalize();
final var hom3DW = point.getHomW();
final var hom2DW = point.getHomX() * internalMatrix.getElementAt(2, 0)
+ point.getHomY() * internalMatrix.getElementAt(2, 1)
+ point.getHomZ() * internalMatrix.getElementAt(2, 2)
+ point.getHomW() * internalMatrix.getElementAt(2, 3);
var cheiral = hom3DW * hom2DW;
if (!isCameraSignFixed()) {
cheiral *= getCameraSign();
}
return cheiral;
}
Projection and back-projection
Because lines/planes and points transform contragradiently (see Transformations), the camera’s project/back-project operations use different formulas depending on what is being mapped:
| From | To | Formula |
|---|---|---|
|
|
|
|
|
(transpose, not inverse — back-projecting an image line gives the 3D plane through the camera center and that line; PHD §2.7, eq. 2.152) |
|
|
using the Moore-Penrose pseudo-inverse ; explicitly non-unique — any point on the same ray of light is an equally valid answer, and any solution is a linear combination of this one and the camera center (PHD §2.7, eq. 2.146-2.150; HZ §6.2.2, eq. 6.13) |
|
|
(back-projection, sandwiched transpose — see derivation below) |
|
|
(forward projection of the dual; PHD §2.8, eq. 2.157; HZ Result 8.9, eq. 8.5) |
Deriving Q = Pᵗ·C·P, and how duality composes
The pseudo-inverse ray formula above (backProject(Point2D, Point3D)) computes via
Utils.pseudoInverse(internalMatrix), normalized by its Frobenius norm, exactly as the general pseudo-inverse
built from an SVD
(Numerical Recipes §2.6, pp. 65-67 for the SVD itself; Appendix
§A5.2 of HZ, p. 590, for the pseudo-inverse built from it):
final var pseudoInverseInternalMatrix = Utils.pseudoInverse(internalMatrix);
final var norm = Utils.normF(pseudoInverseInternalMatrix);
pseudoInverseInternalMatrix.multiplyByScalar(1.0 / norm);
pseudoInverseInternalMatrix.multiply(m); // m = homogeneous image point
The conic back-projection is a different, simpler idea: it is a direct algebraic pullback of the conic’s quadratic form. If a 3D point projects to , and satisfies the conic equation , then
so is exactly the quadric locus of every 3D point whose projection lies on C — the cone of rays traced back through the conic. The source comment in PinholeCamera.backProject(Conic, Quadric)
states this identity directly ("We need to compute: Q = P^T * C * P"), and one can check that the camera center
itself always lies on this cone: \$\mathbf{C}_{\text{cam}}^{\mathsf T}\mathbf{Q}\mathbf{C}_{\text{cam}}=(\mathbf{P}\,\mathbf{C}_{\text{cam}})^{\mathsf
T}\mathbf{C}(\mathbf{P}\,\mathbf{C}_{\text{cam}})=\mathbf 0^{\mathsf T}\mathbf{C}\,\mathbf 0=0\$, as expected of a cone whose vertex is the camera center
(HZ §8.3, fig. 8.4, "the cone of rays"). The dual analogue — forward-projecting a dual quadric into a dual conic — is
HZ’s Result 8.9 and PHD’s eq. 2.155-2.157, obtained the mirror
way: a plane tangent to back-projects
from a line tangent to , via the same used for plain lines/planes
above.
Because the direct (Conic/Quadric) and dual (DualConic/DualQuadric) representations of the same geometric
object are related by matrix inversion, duality composes: Camera.project(Quadric) does not reimplement anything
new, it converts the quadric to its dual, calls project(DualQuadric, DualConic) above, then converts the resulting
dual conic back:
public void project(final Quadric quadric, final Conic result) throws CameraException {
quadric.normalize();
final var dualQuadric = quadric.getDualQuadric();
final var dualConic = new DualConic();
project(dualQuadric, dualConic);
dualConic.conic(result);
}
and Camera.backProject(DualConic) is the exact mirror route (dual conic → conic →
backProject(Conic, Quadric) → dual quadric).
Camera matrices, the geometry strata and the absolute conic
A camera’s images are unaffected by which projective/affine/metric stratum the world points and the camera are
expressed in, as long as both are transformed consistently: if , then
, i.e. any 3D transformation induces
on the camera (PHD
§2.8, eq. 2.153-2.154; see
Transformations for how Transformation3D acts on a PinholeCamera). Composing this with the
dual-quadric projection formula above and specializing to the dual absolute quadric
gives the dual image of the absolute conic (DIAC),
, which in the metric stratum reduces to
— i.e. K is recoverable from
by Cholesky factorization (PHD
§2.8, eq. 2.158-2.160;
HZ §8.5, Result 8.17, ). This
is the theoretical basis for camera self-calibration and for recovering the metric stratum from a purely
projective reconstruction.
This library does not, however, ship a ready-made self-calibration/DIAC-estimator class — there is no
Quadric.createCanonicalAbsoluteQuadric() or Kruppa-equation solver in com.irurueta.geometry.estimators or
com.irurueta.geometry.refiners; only DualQuadric.createCanonicalDualAbsoluteQuadric() exists as a building
block. The EPnPPointCorrespondencePinholeCameraEstimator
(Lepetit, Moreno-Noguer and Fua) remains the only estimator this page cites
directly. See Conics and Quadrics for the absolute conic/quadric themselves, and
Transformations for the stratification of 3D transformations.
Structural shortcuts baked into the matrix, at a glance
-
Camera center at infinity — explicitly supported:
computeCameraCenterSVD/computeCameraCenterDetare documented as valid "even when center is located at infinity (w = 0)", modeling the classical affine-camera degeneracy (HZ §6.3); only the cheapestcomputeCameraCenterFiniteCameramethod requires a finite center. -
Cheirality/sign —
getCameraSign()/fixCameraSign()use the sign of the top-left 3×3 block’s determinant to resolve the front/behind-camera ambiguity inherent to a homogeneous, scale-free matrix. -
Everything else (vanishing points, axis/principal planes, principal point/axis) is read directly off `P’s own rows and columns without any decomposition at all — see "Vanishing points, axis planes and the principal point" above.
References
-
Alberto Irurueta, Fixed Scene 3D Reconstruction for Mobile Applications, PhD thesis, chapter 2, "Camera models" — the pinhole and canonical camera (§2.1-2.2, pp. 44-50), intrinsic/extrinsic parameters and CCD pixel conversion (§2.2.2-2.2.3, pp. 47-52), radial distortion aside (§2.2.4, pp. 52-54), camera decomposition and the three camera-center methods (§2.3.1.1-2.3.1.3, pp. 55-58), vanishing points/axis planes/principal point/principal axis and camera sign (§2.3.2-2.3.5, pp. 59-63), RQ decomposition for K and R (§2.3.6, p. 64), depth and cheirality (§2.4, pp. 64-68), back-projection of points and lines (§2.7, pp. 82-83), and camera matrices, the geometry strata and their relation to conics/quadrics and the DIAC (§2.8, pp. 83-85). See the bibliography entry for the full citation.
-
Hartley & Zisserman, Multiple View Geometry in Computer Vision, 2nd edition — chapter 6, "Camera Models": the projective/finite camera hierarchy, camera anatomy, centre, column/row vectors, principal point/axis (§6.1-6.2, pp. 153-165, Table 6.1 p. 158), depth of points (Result 6.1, pp. 162-163), decomposition via RQ and SVD/cofactors (§6.2.4, p. 164), and cameras at infinity (§6.3, pp. 166-174); chapter 8, "More Single View Geometry": action on quadrics and the cone of rays (§8.3, Result 8.9-8.10, pp. 201-202), the importance of the camera centre (§8.4, pp. 202-208), camera calibration and the image of the absolute conic (§8.5, Result 8.17, pp. 208-211), vanishing points and lines (§8.6, pp. 212-220), affine 3D measurements (§8.7, pp. 220-222), and calibration from a single view (§8.8, pp. 222-225); Appendix 4, "Matrix Properties and Decompositions": RQ decomposition via Givens rotations (Algorithm A4.1, §A4.1.1, p. 579) and QR via Householder matrices (§A4.1.2, p. 580); Appendix 5, "Least-squares Minimization": the pseudo-inverse (§A5.2, p. 590) and its SVD-based construction (Algorithm A5.4, p. 593).
-
Press, Teukolsky, Vetterling and Flannery, Numerical Recipes: The Art of Scientific Computing, 3rd edition — §2.6, "Singular Value Decomposition" (pp. 65-67), the general-purpose numerical machinery behind
Camera.backProject(Point2D, Point3D)’s pseudo-inverse and `computeCameraCenterSVD()’s null-space computation; §2.10, "QR Decomposition" (pp. 102-106), the broader algorithm family that HZ’s Appendix 4 specializes into the RQ decomposition `com.irurueta.algebra.RQDecomposerimplements. -
Lepetit, Moreno-Noguer and Fua, "EPnP: An Accurate O(n) Solution to the PnP Problem" — cited directly in
EPnPPointCorrespondencePinholeCameraEstimatorjavadoc (theestimatorspackage), used internally by some pinhole camera estimators.
Key classes
The classes exercised by the code examples above, with links to their source and Javadoc:
| Class | Links |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The examples also use com.irurueta.algebra.RQDecomposer, SingularValueDecomposer, Matrix and Utils, from the sibling irurueta-algebra library, not this repository.
|
Related pages
-
Transformations — how a
Transformation3Dacts on aPinholeCamera( ), and the projective/affine/metric stratification referenced in "Camera matrices, the geometry strata…" above. -
Conics and Quadrics — conics, quadrics, their duals, and the absolute conic/quadric that underlie the DIAC and back-projection formulas on this page.
-
Rotations — the camera’s orientation is stored as a
Rotation3D. -
Points, Lines and Planes — the points, lines and planes being projected/back-projected.