图像的镜头校正
从车载摄像头拍摄的照片不可不免有些失真,我们需要有一些方法来进行还原。
wiki distortion
存在两种失真,一种是径向的(tangential distortion)(左图),一种是切向的(Radial Distortion)(右图)
前者引起的原因在于,相机的镜头没有与所拍摄的物体完全平行,导致生成的图像有倾斜失真。
后者引起的原因在于,由于光线在透过相机的镜头的边缘的时候会有所变形,导致生成的图像边缘失真。
我们可以通过一些预定义好的真实以及失真图像来确定参数,然后把这个参数应用到其他的图像上去。

x_distorted = x_ideal* (1 + k_1 * r^2 + k_2 * r^4 + k_3 * r^6)
y_distorted = y_ideal* (1 + k_1 * r^2 + k_2 * r^4 + k_3 * r^6)
x_corrected = x + [2 * p_1 * x * y + p_2 * ( r^2 + 2 * x^2)]
y_corrected = y + [p_1 * (r^2 +2 * y^2) + 2 * p_2 * x * y]
OpenCV 提供了对应的 API 来帮助我们做这个事情。
我们可以提供一个棋盘格子的图像,来告诉 OpenCV 上面有多少个格子,
没有变形的棋盘格是方方正正的,拍出来的照片总是有一些变形。
import numpy as np
import cv2
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
# prepare object points
nx = 8
ny = 6
# Make a list of calibration images
fname = 'calibration_test.png'
img = cv2.imread(fname)
# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Find the chessboard corners
ret, corners = cv2.findChessboardCorners(gray, (nx, ny), None)
# If found, draw corners
if ret == True:
# Draw and display the corners
cv2.drawChessboardCorners(img, (nx, ny), corners, ret)
plt.imshow(img)
nx, ny 是如何确定的?
nx 和 ny 就是 x 轴 以及 y 轴上的 grid 点的数目,比如 棋盘格上面有 18 * 8,那么 nx 就是 18 - 1, ny 就是 8 - 1
我们可以这样做来进行 undistortion
import numpy as np
import cv2
import glob
import matplotlib.pyplot as plt
from matplotlib.image import imsave as mpsave
from matplotlib.image import imread as mpread
def get_calibration_mtx():
# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*9,3), np.float32)
objp[:,:2] = np.mgrid[0:9,0:6].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
img_name_list = glob.glob('../camera_cal/calibration*.jpg')
# Step through the list and search for chessboard corners
for img_name in img_name_list:
img = mpread(img_name)
gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
# Find the chessboard corners
ret, corners = cv2.findChessboardCorners(gray, (9,6), None)
# If found, add object points, image points
if ret == True:
objpoints.append(objp)
imgpoints.append(corners)
# Draw and display the corners
img = cv2.drawChessboardCorners(img, (9,6), corners, ret)
cv2.imshow('img',img) # 'img' is the show window name
cv2.waitKey(500)
cv2.destroyAllWindows()
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
return mtx, dist
注意到这里我们给 objpoints append 的是同一个 objp,毕竟在 calibrateCamera() 的调用中它会保持不变的。
返回的 mtx 以及 dist 可以用来做进一步的 undistortion
下面就来做 undistortion
import numpy as np
def img_calibration(mtx, dist):
img_name = '../camera_cal/calibration.jpg'
img_org = mpread(img_name)
img_dst = cv2.undistort(img_org, mtx, dist, None, mtx)
所以一个正常的流程就是:
1. 准备好一些棋盘格的图片,用同一个相机拍摄
2. 转换成灰度图
3. 利用函数 cv2.findChessboardCorners() 找到 corners
4. 构造对应的 grid points
5. 利用函数 cv2.calibrateCamera() 计算 distortion 相关的参数
6. 利用得到的参数作用在新的图片上(来自于同一个相机),得到 undistortion 的图片
在经过这一系列处理之后,我们能够成功的把图片进行还原,比如这样: