1

I am using a camera calibration routine and I want to calibrate a camera with large set of images.

Code: (from here)

import numpy as np
import cv2
import glob
import argparse

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


def calibrate():
    height = 8
    width = 10
    """ Apply camera calibration operation for images in the given directory path. """
    # prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(8,6,0)
    objp = np.zeros((height*width, 3), np.float32)
    objp[:, :2] = np.mgrid[0:width, 0:height].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.

    # Get the images
    images = glob.glob('thermal_final set/*.png')

    # Iterate through the pairs and find chessboard corners. Add them to arrays
    # If openCV can't find the corners in an image, we discard the image.
    for fname in images:
        img = cv2.imread(fname)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        # Find the chess board corners
        ret, corners = cv2.findChessboardCorners(gray, (width, height), None)

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

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

            # Draw and display the corners
            # Show the image to see if pattern is found ! imshow function.
            img = cv2.drawChessboardCorners(img, (width, height), corners2, ret)

    e1 = cv2.getTickCount()

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

    e2 = cv2.getTickCount()
    t = (e2 - e1) / cv2.getTickFrequency()

    print(t)


    return [ret, mtx, dist, rvecs, tvecs]


if __name__ == '__main__':

    ret, mtx, dist, rvecs, tvecs = calibrate()

    print("Calibration is finished. RMS: ", ret)

Now, the problem is that the time that cv2.calibratecamera() takes, based on number of points(derived from images) used.

Result with 40 images:

9.34462341234 seconds
Calibration is finished. RMS:  2.357820395255311

Result with 80 images:

66.378870749 seconds
Calibration is finished. RMS:  2.864052963156834

The time taken increases exponentially with increase in images.

Now, I have a really huge set of images (500).

I have tried calibrating camera with points from a single image and then calculating average of all the results I get, but they are different than what I get from this method.

Also, I am sure that my setup is using optimized OpenCV, check using:

print(cv2.useOptimized())

How do I make this process faster? Can I leverage threads here?

Edit: Updated the concept and language from "calibrating images" to "calibrating camera using images"

vwertuzy
  • 171
  • 1
  • 2
  • 12
  • In my experience above 100 images you don't improve the accuracy anymore. One really important aspect is actually the experimental setup, I don't know much about yours but usually people tend to use poorly designed targets. Also, you have to take care that the optical parameters are constant for a given calibration. – 87VN0 Apr 26 '21 at 16:14
  • @87VN0 I completely agree that I do not need 500+ images, but I still do wonder how does VisionLib (that uses OpenCV too) manage to give me instant results. – vwertuzy Apr 27 '21 at 13:30

1 Answers1

2

First, I strongly suspect the reason of your dramatic slowdown is memory related: you may be running out and starting to swap.

But the basic approach you seem to be following is incorrect. You don't calibrate images, you calibrate a camera, i.e. a lens + sensor combo.

Calibrating a camera means estimating the parameters of a mathematical model of that lens+sensor package. Therefore you only need use enough independent data points to achieve the desired level of accuracy in the parameter estimation.

A couple dozen well chosen images will be enough most of the time, provided you are following a well designed calibration procedure. Some time ago I wrote a few tips on how to do such a design, you can find them here.

Francesco Callari
  • 11,300
  • 2
  • 25
  • 40
  • I was actually trying to stick to the exact same post you shared which I came across before. I know that I might be going overboard here with the amount of images, but I have tried VisionLib's calibration (that uses OpenCV) and they are able to provide almost instant results with 500+ images. I wanted to replicate something along the lines of that. – vwertuzy Apr 27 '21 at 13:25
  • 1
    I don't doubt that it is possible to do it, I have done bundle adjustment on large image collections too. I question the usefulness for this particular use case: if your camera can be calibrated (meaning, it is really time-invariant), and you have a dedicated image capture step for the calibration, then using 500 images, each one yielding 100 points or so, is extremely redundant - the vast majority of the data points will likely be correlated. If you do _not_ control the image capture (e.g., you are matchmoving some given footage), it's a completely different story. – Francesco Callari Apr 27 '21 at 13:37
  • That is a helpful insight. My whole idea is to get 500+ images, so that some if it that have larger relative error, the quantity of data brings that down. Also, I will not be controlling the image capture, a live feedback image will be displayed to show how much of the camera's view is covered with the pattern (like opencv's interactive calibration). – vwertuzy Apr 27 '21 at 13:56