镜头变形是摄影中常见的现象,使用广角的时候更可以看的出来,失真主要有以下两种:
径向畸变(radial distortion)
切向畸变(tangential distortion)
径向畸变
光线经入透镜折射后会产生偏折,造成形变,而当透镜越小时形变会越明显,也就是原本的直线会出现弯曲,而离中心点越远,变化会越来越严重,广角的话常产生下图中间的形变,而长焦镜头则常是右侧的形变。
切向畸变
主要发生在硬件瑕疵上,当镜头没有完全与成像平面对齐时,便会产生误差,有些图片会比想像中的来的更紧密。
我们需要的参数
透过上述俩个式子,我们需要找出五个系数,以及相机固有/外部参数
Five params
Intrinsic Params
Extrinsix Params
Five params
Intrinsic Params
有焦距(focal length)→(fx,fy),光学中心(optical center)→(cx,cy)等信息,我们可以将它们组合成一个矩阵来去除因特定相机镜头而造成的失真。显而易见,这个matrix只需调整一次,便可应用在这个相机拍摄的不同照片上。
Extrinsix Params
外部参数则是对应于3D坐标系的转换(旋转/平移),要找到这些参数,我们需要透过一些(至少>10张)来找到相关的某个特定点的。(例如:国际象棋,利用正方形边框的顶点)。因为我们已经知道现实空间中棋盘格各点的坐标,也知道图片内的各点坐标,如此便可消除失真的影响。
让我们进入代码实作部分
现在提供Github Repository,让大家可以运行:
https://github.com/tarkers/Camera_Calibration_OpenCV
先定义一下关键字:3D点叫对象点(object points),2D像点叫图像点(image points)
我们所需要的就是在图像中的棋盘中寻找有一组对象点的图像点,因为有照片,所以对我们来说是相对比较好找的。我们知道棋盘格在这几张图片内,有不同的位置及方向, 那对象点(X,Y,Z)我们又该如何找呢?
因为3维太麻烦了,我们可以将棋盘格定为XY平面(Z=0),然后让我们把摄像机放在它上面,这样我们只须找出X,Y即可。我们可以简单的标点上去
若不知道棋盘大小,那我们的基本长度就是以格子为单位:(0,0),(0,1),(0,2)……
若已知每格是M毫米,那我们也可以标为:(0,0),(0,M)(0,2M)....。
以这次实例,我们第一次实作。
openCV Func 介绍:
cv.findChessboardCorners()
https://docs.opencv.org/4.x/d9/d0c/group__calib3d.html#ga93efa9b0aa890de240ca32b11253dd4a
cv.cornerSubPix
https://docs.opencv.org/4.x/dd/d1a/group__imgproc__feature.html#ga354e0d7c86d0d9da75de9b9701a9a87e
cv.calibrateCamera()
https://docs.opencv.org/4.x/d9/d0c/group__calib3d.html#ga3207604e4b1a1758aa66acb6ed5aa65d
来自 OpenCV 的简单代码
将图像读入并计算2D/3D点,利用opencv的calibrateCameramatrix和distortion coefficients。这样便能修正图片了
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 cornersret, 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(corners)# Draw and display the cornerscv.drawChessboardCorners(img, (7,6), corners2, ret)cv.imshow('img', img)cv.waitKey(500)
cv.destroyAllWindows()
#calibration
ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
# get undistorted image
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)
结果
可以看到棋盘边缘明显弯曲的线,有被修正回来
左:失真,右:失真
透过这样的介绍,相信大家对校准有更深的认识啦!我们下回见:
参考:
https://docs.opencv.org/4.x/dc/dbb/tutorial_py_calibration.html
https://allen108108.github.io/blog/2020/02/15/%E5%BD%B1%E5%83%8F%E7%95%B8%E8%AE%8A%20Image%20Distortion/
https://learnopencv.com/camera-calibration-using-opencv/
☆ END ☆
如果看到这里,说明你喜欢这篇文章,请转发、点赞。微信搜索「uncle_pn」,欢迎添加小编微信「 woshicver」,每日朋友圈更新一篇高质量博文。
↓扫描二维码添加小编↓