具体实现方案:
棋盘是一块由黑白方块间隔组成的标定板,我们用它来作为相机标定的标定物(从真实世界映射到数字图像内的对象)。之所以我们用棋盘作为标定物是因为平面棋盘模式更容易处理(相对于复杂的三维物体),但与此同时,二维物体相对于三维物体会缺少一部分信息,于是我们会多次改变棋盘的方位来捕捉图像,以求获得更丰富的坐标信息。
下面将依次对刚体进行一系列变换,使之从世界坐标系进行仿射变换、投影透射,最终得到像素坐标系下的离散图像点,过程中会逐步引入各参数矩阵。
标定图片需要使用标定板在不同位置、不同角度、不同姿态下拍摄,最少需要3张,以10~20张为宜。标定板需要是黑白相间的矩形构成的棋盘图,制作精度要求较高,如下图所示:
标定步骤:
1、打印一张棋盘格,把它贴在一个平面上,作为标定物。
2、通过调整标定物或摄像机的方向,为标定物拍摄一些不同方向的照片。
3、从照片中提取棋盘格角点。
4、估算理想无畸变的情况下,五个内参和六个外参。
5、应用最小二乘法估算实际存在径向畸变下的畸变系数。
6、极大似然法,优化估计,提升估计精度。
import cv2 import numpy as np import glob criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) w = 8 h = 6 objp = np.zeros((w*h,3), np.float32) objp[:,:2] = np.mgrid[0:w,0:h].T.reshape(-1,2) objp = 2 * objp objpoints = [] imgpoints = [] def get_internal_reference(path): images = glob.glob(path + '/*.jpg') for frame in images: img = cv2.imread(frame) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners = cv2.findChessboardCorners(gray, (w,h),None) #在灰度图像 gray 中找到棋盘格角点 if ret: cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria) #亚像素级别的角点精确化 objpoints.append(objp)#将世界坐标系中的棋盘格角点的理论坐标(objp)加入 objpoints 和 imgpoints 中。 # 这些点用于后续相机标定的计算。 imgpoints.append(corners)#图像坐标系中检测到的角点坐标(corners)加入 imgpoints 中 cv2.drawChessboardCorners(img, (w,h), corners, ret) # cv2.imshow('draw',img) # cv2.waitKey(0) #利用棋盘格角点的理论坐标和图像坐标进行相机标定。 ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None) # ret表示的是重投影误差;mtx是相机的内参矩阵;dist表述的相机畸变参数; # rvecs表示标定棋盘格世界坐标系到相机坐标系的旋转参数:rotation vectors,需要进行罗德里格斯转换; # tvecs表示translation vectors,主要是平移参数。 #返回相机矩阵 mtx 和畸变参数 dist return ret, mtx, dist, rvecs, tvecs
以上为获得参数矩阵的代码。若要对一组图像进行标定,使用以下函数调用以上代码。
from calibration import get_internal_reference def camera_calibration(path): # path为棋盘标定内参的图片路径 if os.path.exists(path): ret, mtx, dist, rvecs, tvecs = get_internal_reference(path) else: os.makedirs(path) cap = cv2.VideoCapture(0) count = 0 i = 1 EXTRACT_FREQUENCY = 10 while len(os.listdir(path)) < 25: _, frame = cap.read() if frame is None: break if count % EXTRACT_FREQUENCY == 0: save_path = '{}/{}.jpg'.format(path, i) cv2.imwrite(save_path, frame) cv2.imshow('calibration_img', frame) cv2.waitKey(1000) i += 1 count += 1 cv2.destroyAllWindows() ret, mtx, dist, rvecs, tvecs = get_internal_reference(path) return ret, mtx, dist, rvecs, tvecs #(mtx是相机的内参矩阵;dist表述的相机畸变参数;) ret, mtx, dist, rvecs, tvecs = camera_calibration('img') img=cv2.imread('img/1.jpg',cv2.IMREAD_GRAYSCALE) u, v = img.shape[:2] # img=cv2.imread('E:/project/calibration images/1.jpg',cv2.IMREAD_GRAYSCALE) # 校正图像 h,w = img.shape print(h,w) new_camera_matrix, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (u, v ), 0, (u, v )) undistorted_image = cv2.undistort(img, mtx, dist, None, new_camera_matrix) # undistorted_image = cv2.undistort(img, np.array(mtx), np.array(dist)) img_diff = cv2.absdiff(img, undistorted_image) cv2.imshow('img',img) cv2.imshow('yun',undistorted_image) cv2.imshow('yun1',img_diff) cv2.waitKey(0) cv2.destroyAllWindows()
即可得到矫正结果。如下显示
以下为标定图像和原图的差异图
标签:张正友,mtx,dist,img,python,cv2,标定,path,棋盘 From: https://www.cnblogs.com/heyrroo/p/17956888