Table of Contents
Overview#
Camera calibration determines the intrinsic and extrinsic parameters needed to accurately map 3D world coordinates to 2D image coordinates.
Camera Model#
Projection Equation#
$$ s \begin{bmatrix} u \\ v \\ 1 \end{bmatrix} = K [R | t] \begin{bmatrix} X \\ Y \\ Z \\ 1 \end{bmatrix} $$Where:
- \((u, v)\): Image coordinates (pixels)
- \((X, Y, Z)\): World coordinates
- \(K\): Intrinsic matrix
- \([R|t]\): Extrinsic matrix (rotation + translation)
Intrinsic Matrix#
$$ K = \begin{bmatrix} f_x & 0 & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1 \end{bmatrix} $$| Parameter | Description |
|---|---|
| \(f_x, f_y\) | Focal length (pixels) |
| \(c_x, c_y\) | Principal point (image center) |
Distortion Coefficients#
Radial: \(k_1, k_2, k_3\) Tangential: \(p_1, p_2\)
$$ dist = [k_1, k_2, p_1, p_2, k_3] $$Implementation#
Setup#
import numpy as np
import cv2
import glob
# Checkerboard dimensions (inner corners)
CHECKERBOARD = (7, 10)
# Termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)Prepare Object Points#
# 3D points in real world space (z=0 for flat checkerboard)
objp = np.zeros((CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2)
# Arrays to store points from all images
objpoints = [] # 3D points
imgpoints = [] # 2D pointsDetect Corners#
images = glob.glob('calibration_images/*.jpg')
for fname in images:
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Find checkerboard corners
ret, corners = cv2.findChessboardCorners(gray, CHECKERBOARD, None)
if ret:
objpoints.append(objp)
# Refine corner positions
corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
imgpoints.append(corners2)
# Visualize
cv2.drawChessboardCorners(img, CHECKERBOARD, corners2, ret)
cv2.imshow('Corners', img)
cv2.waitKey(500)
cv2.destroyAllWindows()Calibrate#
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
objpoints, imgpoints, gray.shape[::-1], None, None
)
print("Camera Matrix:\n", mtx)
print("Distortion Coefficients:\n", dist)Output Parameters#
Camera Matrix (mtx)#
[[fx 0 cx]
[ 0 fy cy]
[ 0 0 1]]Distortion Coefficients (dist)#
[k1, k2, p1, p2, k3]Rotation & Translation Vectors#
- rvecs: Object orientation relative to camera (per image)
- tvecs: Object position relative to camera (per image)
Undistortion#
# Get optimal new camera matrix
h, w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
# Undistort
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# Crop to valid region
x, y, w, h = roi
dst = dst[y:y+h, x:x+w]Tips#
- Use 10-20 images from different angles
- Cover entire frame with checkerboard
- Vary orientation - tilted views improve accuracy
- Print checkerboard on flat surface
- Good lighting - avoid reflections and shadows