张正友棋盘代码-python

发布时间 2024-01-10 17:08:45作者: heyrro

具体实现方案:

棋盘是一块由黑白方块间隔组成的标定板,我们用它来作为相机标定的标定物(从真实世界映射到数字图像内的对象)。之所以我们用棋盘作为标定物是因为平面棋盘模式更容易处理(相对于复杂的三维物体),但与此同时,二维物体相对于三维物体会缺少一部分信息,于是我们会多次改变棋盘的方位来捕捉图像,以求获得更丰富的坐标信息。

下面将依次对刚体进行一系列变换,使之从世界坐标系进行仿射变换、投影透射,最终得到像素坐标系下的离散图像点,过程中会逐步引入各参数矩阵。

标定图片需要使用标定板在不同位置、不同角度、不同姿态下拍摄,最少需要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()

即可得到矫正结果。如下显示

以下为标定图像和原图的差异图