· Hakan Çelik · OpenCV / Camera Calibration · 3 dk okuma

Camera Calibration

Learn about types of distortion caused by cameras and how to find intrinsic/extrinsic properties. We cover cv.calibrateCamera() and cv.undistort() using a chessboard pattern.

Camera Calibration

Goal

In this section, we will learn about:

  • Types of distortion caused by cameras
  • How to find the intrinsic and extrinsic properties of a camera
  • How to undistort images based off these properties

Basics

Some pinhole cameras introduce significant distortion to images. Two major kinds of distortion are radial distortion and tangential distortion.

Radial distortion causes straight lines to appear curved. Radial distortion becomes larger the farther points are from the center of the image:

Radial distortion

Radial distortion can be represented as follows:

x_distorted = x(1 + k₁r² + k₂r⁴ + k₃r⁶) y_distorted = y(1 + k₁r² + k₂r⁴ + k₃r⁶)

Tangential distortion occurs because the image-taking lens is not aligned perfectly parallel to the imaging plane:

x_distorted = x + [2p₁xy + p₂(r² + 2x²)] y_distorted = y + [p₁(r² + 2y²) + 2p₂xy]

In short, we need to find five parameters, known as distortion coefficients: (k₁, k₂, p₁, p₂, k₃)

In addition to this, we need the intrinsic parameters, like the focal length (fx, fy) and optical centers (cx, cy). The camera matrix is expressed as a 3×3 matrix:

camera_matrix = [[fx, 0, cx],
                  [0, fy, cy],
                  [0,  0,  1]]

To find these parameters, we must provide some sample images of a well defined pattern (e.g. a chess board). We need at least 10 test patterns.

Code

import numpy as np
import cv2 as cv
import glob

# termination criteria
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)

# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ..., (6,5,0)
objp = np.zeros((6 * 7, 3), np.float32)
objp[:, :2] = np.mgrid[0:7, 0:6].T.reshape(-1, 2)

# Arrays to store object points and image points from all the images
objpoints = []  # 3d point in real world space
imgpoints = []  # 2d points in image plane.

images = glob.glob('*.jpg')

for fname in images:
    img = cv.imread(fname)
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

    # Find the chess board corners
    ret, corners = cv.findChessboardCorners(gray, (7, 6), None)

    # If found, add object points, image points (after refining them)
    if ret == True:
        objpoints.append(objp)

        corners2 = cv.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
        imgpoints.append(corners2)

        # Draw and display the corners
        cv.drawChessboardCorners(img, (7, 6), corners2, ret)
        cv.imshow('img', img)
        cv.waitKey(500)

cv.destroyAllWindows()

One image with pattern drawn on it:

Calibration pattern

Calibration

Now that we have our object points and image points, we are ready to go for calibration using cv.calibrateCamera() which returns the camera matrix, distortion coefficients, rotation and translation vectors etc.

ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

Undistortion

Method 1: Using cv.undistort()

img = cv.imread('left12.jpg')
h, w = img.shape[:2]
newcameramtx, roi = cv.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))

# undistort
dst = cv.undistort(img, mtx, dist, None, newcameramtx)

# crop the image
x, y, w, h = roi
dst = dst[y:y + h, x:x + w]
cv.imwrite('calibresult.png', dst)

Method 2: Using remapping

mapx, mapy = cv.initUndistortRectifyMap(mtx, dist, None, newcameramtx, (w, h), 5)
dst = cv.remap(img, mapx, mapy, cv.INTER_LINEAR)

x, y, w, h = roi
dst = dst[y:y + h, x:x + w]
cv.imwrite('calibresult.png', dst)

Both methods give the same result. See the result below:

Calibration result

You can see in the result that all the edges are straight.

Re-projection Error

Re-projection error gives a good estimation of just how exact the found parameters are:

mean_error = 0
for i in range(len(objpoints)):
    imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist)
    error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2SQR) / len(imgpoints2)
    mean_error += error

print("total error: {}".format(np.sqrt(mean_error / len(objpoints))))

Exercises

  1. Try camera calibration with circular grid.

Source: OpenCV Python Tutorials — Original Documentation

Back to Blog

Related Posts

View All Posts »
How OpenCV-Python Bindings Work

How OpenCV-Python Bindings Work

OpenCV · 3 dk

Learn how OpenCV-Python bindings are generated from C++ headers. We cover CV_EXPORTS_W, CV_WRAP, and other macros, plus the gen2.py generator and hdr_parser.py header parser scripts.

Face Detection using Haar Cascades

Face Detection using Haar Cascades

OpenCV · 3 dk

Learn to use Haar Cascade classifiers in OpenCV for face and eye detection. This tutorial covers the theory behind Haar features, integral images, AdaBoost, and cascade classifiers.

High Dynamic Range (HDR) Imaging

High Dynamic Range (HDR) Imaging

OpenCV · 3 dk

Learn how to generate and display HDR images from an exposure sequence in OpenCV. We cover Debevec, Robertson, and Mertens exposure fusion algorithms with camera response function estimation.

Image Inpainting

Image Inpainting

OpenCV · 2 dk

Learn how to remove small noises, strokes, and damage from old photographs using OpenCV's cv.inpaint(). We cover the Telea and Navier-Stokes inpainting algorithms.