This tutorial is tested on multiple Linux and works with webcams also. But since all dependencies can be done via pip, instead of compilation, Windows should work similarly.
Purpose: A simple guide to camera calibration, solving cameras’ lens distortion with OpenCV.
Package Focus: OpenCV (most versions should work), Pickle, Numpy.
Side Note: Feel free to run in Spyder, so that you can play around with the code yourself.
Tutorial Content
- Obtain Images For Calibration
- Camera Calibration
- Before-After Test
1. Obtain Images For Calibration
This is step helps you acquire the images needed for calibration. Just copy & paste the codes, it’s a lot easier. Code comments and explanations are followed by “#” in the code sections.
# Import Libraries
import cv2# Input "0" means your connected usb webcap. Can be replaced with a RTSP url
cap = cv2.VideoCapture(0)
cv2.namedWindow("Image Acquisition")# For file naming
imgNum = 0
while True:
ret, frame = cap.read()
if not ret:
print("Cannot Read Frame!!!")
break
cv2.imshow("Image Acquisition", frame)comm = cv2.waitKey(1)
if comm%256 == 27:
# ESC key is pressed
print(" Command: Ending program..")
break
elif comm%256 == 32:
# SPACE key is pressed
fileName = "CalibFrame_{}.png".format(imgNum)
cv2.imwrite(fileName, frame)
imgNum += 1
print(" Command: {} is saved!".format(fileName))cap.release()
cv2.destroyAllWindows()
Run the program, and press “Space” when the orientation of the checker-board is desirable.
2. Camera Calibration
This is part is where calibration happens. It will be based on OpenCV’s tutorial https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_calibration/py_calibration.html#calibration with a bit of modifications.
Note: My checker board is 10 * 7, therefore when I will be asking the program to only calibrate based on a 9*6 surface.
# Import more libraries
import numpy as np
import glob
import matplotlib.pyplot as pltboardHeight = 6
boardWidth = 9# Prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((boardHeight*boardWidth,3), np.float32)
objp[:,:2] = np.mgrid[0:boardWidth, 0:boardHeight].T.reshape(-1,2)# Arrays to store object points and image points from all the images.
objpoints = [] # 3d points in real world space
imgpoints = [] # 2d points in image plane.# Make a list of calibration images
images = glob.glob('./CalibFrame*.png')# Step through the list and search for chessboard corners
for idx, fname in enumerate(images):
img = cv2.imread(fname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Find the chessboard corners
ret, corners = cv2.findChessboardCorners(gray, (boardWidth,boardHeight), None)# If found, add object points, image points
if ret == True:
objpoints.append(objp)
imgpoints.append(corners)# Draw and display the corners
cv2.drawChessboardCorners(img, (boardWidth,boardHeight), corners, ret)
#write_name = 'corners_found'+str(idx)+'.jpg'
#cv2.imwrite(write_name, img)
cv2.imshow('img', img)
cv2.waitKey(500)cv2.destroyAllWindows()
3. Before-After Test
Let’s see how good is the calibration.
# Import one last library
import pickle
# Read a test image
img = cv2.imread('./CalibFrame10.png')
img_size = (img.shape[1], img.shape[0])
# Do camera calibration given object and image points
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, img_size,None,None)
dst = cv2.undistort(img, mtx, dist, None, mtx)
cv2.imwrite('./CalibFrame10_undist.png',dst)
# Save the camera calibration results
dist_pickle = {}
dist_pickle["mtx"] = mtx
dist_pickle["dist"] = dist
pickle.dump(dist_pickle, open( "./undist_params.p", "wb" )) #This is your calibration results, just load and use in the future#dst = cv2.cvtColor(dst, cv2.COLOR_BGR2RGB) #uncomment this if your image color looks weird. It relates to how cv2 process image channels# Visualize calibrated result
f, (ax1, ax2) = plt.subplots(1, 2, figsize=(20,10))
ax1.imshow(img)
ax1.set_title('Original Image', fontsize=30)
ax2.imshow(dst)
ax2.set_title('Undistorted Image', fontsize=30)
— END —