Python OpenCV实践,相机标定

Python OpenCV实践,相机标定

  • 前言
  • 准备棋盘格
  • 标定相机
  • 图像去畸变

前言

本篇主要是使用python opencv标定相机内参和畸变参数的记录,主要参考opencv官方文档中的示例。

本篇不会涉及标定原理。

准备棋盘格

一张棋盘格图(最好把棋盘格图粘贴到一块平板上,保证棋盘上的角点都在同一平面)。

这里提供一个简单的棋盘格生成程序,在A4纸打印分辨率为96ppi时,棋盘每个格子的宽度为cube_cm:

def generate_chessboard(cube_cm=2., pattern_size=(8, 6), scale=37.79527559055118):
    """
    generate chessboard image with given cube length, which adapts to A4 paper print
    :param cube_cm: float, single cube length in cm
    :param pattern_size: (x, y), the number of points in x, y axes in the chessboard
    :param scale: float, scale pixel/cm in A4 paper
    """
    # convert cm to pixel
    cube_pixel = cube_cm * scale
    width = round(pattern_size[0] * cube_cm * scale)
    height = round(pattern_size[1] * cube_cm * scale)

    # generate canvas
    image = np.zeros([width, height, 3], dtype=np.uint8)
    image.fill(255)
    color = (255, 255, 255)
    fill_color = 0
    # drawing the chessboard
    for j in range(0, height + 1):
        y = round(j * cube_pixel)
        for i in range(0, width + 1):
            x0 = round(i * cube_pixel)
            y0 = y
            rect_start = (x0, y0)

            x1 = round(x0 + cube_pixel)
            y1 = round(y0 + cube_pixel)
            rect_end = (x1, y1)
            cv2.rectangle(image, rect_start, rect_end, color, 1, 0)
            image[y0:y1, x0:x1] = fill_color
            if width % 2:
                if i != width:
                    fill_color = (0 if (fill_color == 255) else 255)
            else:
                if i != width + 1:
                    fill_color = (0 if (fill_color == 255) else 255)

    # add border around the chess
    chessboard = cv2.copyMakeBorder(image, 30, 30, 30, 30, borderType=cv2.BORDER_CONSTANT, value=(255, 255, 255))
    # visualize
    win_name = "chessboard"
    cv2.imshow(win_name, chessboard)
    cv2.waitKey(0)

Python OpenCV实践,相机标定_第1张图片

标定相机

把需要标定的相机以不同姿态角度向棋盘格拍照,然后放到一个文件夹中,通过下面的程序获得相机内参和畸变参数。主要就是cv2.findChessboardCorners()找到棋盘格角点的2d坐标,然后cv2.calibrateCamera()根据角点的2d坐标与3d空间中的位置顺序计算相机内参和畸变参数:

def calib_camera(calib_dir, pattern_size=(8, 6), draw_points=False):
    """
    calibrate camera
    :param calib_dir: str
    :param pattern_size: (x, y), the number of points in x, y axes in the chessboard
    :param draw_points: bool, whether to draw the chessboard points
    """
    # store 3d object points and 2d image points from all the images
    object_points = []
    image_points = []

    # 3d object point coordinate
    xl = np.linspace(0, pattern_size[0], pattern_size[0], endpoint=False)
    yl = np.linspace(0, pattern_size[1], pattern_size[1], endpoint=False)
    xv, yv = np.meshgrid(xl, yl)
    object_point = np.insert(np.stack([xv, yv], axis=-1), 2, 0, axis=-1).astype(np.float32).reshape([-1, 3])

    # set termination criteria
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)

    # load image
    img_dir = calib_dir
    assert os.path.isdir(img_dir), 'Path {} is not a dir'.format(img_dir)
    imagenames = os.listdir(img_dir)
    for imagename in imagenames:
        if not os.path.splitext(imagename)[-1] in ['.jpg', '.png', '.bmp', '.tiff', '.jpeg']:
            continue
        img_path = os.path.join(img_dir, imagename)
        img = cv2.imread(img_path)
        img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        # find chessboard points
        ret, corners = cv2.findChessboardCorners(img_gray, patternSize=pattern_size)
        if ret:
            # add the corresponding 3d points to the summary list
            object_points.append(object_point)
            # if chessboard points are found, refine them to SubPix level (pixel location in float)
            corners_refined = cv2.cornerSubPix(img_gray, corners, (11, 11), (-1, -1), criteria)
            # add the 2d chessboard points to the summary list
            image_points.append(corners.reshape([-1, 2]))
            # visualize the points
            if draw_points:
                cv2.drawChessboardCorners(img, pattern_size, corners_refined, ret)
                if img.shape[0] * img.shape[1] > 1e6:
                    scale = round((1. / (img.shape[0] * img.shape[1] // 1e6)) ** 0.5, 3)
                    img_draw = cv2.resize(img, (0, 0), fx=scale, fy=scale)
                else:
                    img_draw = img

                cv2.imshow('img', img_draw)
                cv2.waitKey(0)

    assert len(image_points) > 0, 'Cannot find any chessboard points, maybe incorrect pattern_size has been set'
    # calibrate the camera, note that ret is the rmse of reprojection error, ret=1 means 1 pixel error
    reproj_err, k_cam, dist_coeffs, rvecs, tvecs = cv2.calibrateCamera(object_points,
                                                                       image_points,
                                                                       img_gray.shape[::-1],
                                                                       None,
                                                                       None,
                                                                       criteria=criteria)

return k_cam, dist_coeffs

if name == '__main__':
	calib_camera('my_dir/')

图像去畸变

把上面求出来的相机内参和畸变参数带入cv2.undistort()函数中去畸变:

dst = cv2.undistort(img, k_cam, dist_coeffs)

你可能感兴趣的:(图像处理,OpenCV,python,c++,学习)