单目相机标定-python


1、准备一个标定板,按照上文提出的要求制作。为了简便也可以用A4纸打印,粘贴在一个平板上。
2、采集棋盘格标定板若干张图片,一般10至20张就够。但是每次拍摄,标定板角度和位置要改变。
3、检测图片中角点。
4、利用解析解估算方法计算出5个内部参数,以及6个外部参数
5、使用畸变公式的线性方程组求解近似的畸变系数(或者直接使用0也可以)
6、使用非线性优化法计算精确的内外参数和畸变系数。

 

# -*- coding:utf-8 -*-
import os
import cv2
import numpy as np
import glob
import configparser

class MonocularCalibration(object):
    '''单目标定和矫正类'''
    def __init__(self, data_root, cali_file="MonoCalib_Param_720p.ini", board_size=(7,11), img_shape=(720, 1280), suffix="png"):
        self.cali_file = os.path.join(data_root, cali_file)
        self.H, self.W = img_shape
        if os.path.exists(self.cali_file):
            print("\n===> Read Calibration file from {}...".format(self.cali_file))
            self.read_cali_file(self.cali_file)
        else:
            print("\n===> Start Calibration...")
            # 步骤1:初始化
            self.board_size = board_size
            # 步骤2:标定
            self.Mono_Calibration(corner_h=self.board_size[0], corner_w=self.board_size[1], source_path=data_root, suffix=suffix)
            # 步骤3:生成标定文件
            self.write_cali_file(cali_file=self.cali_file)

    # 计算单目相机内参、畸变系数
    def Mono_Calibration(self, corner_h, corner_w, source_path, suffix="png"):
        # 设置寻找亚像素角点的参数,采用的停止准则是最大循环次数30和最大误差容限0.001
        criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001)
        # 获取标定板角点的位置
        # 世界坐标系中的棋盘格点,例如(0,0,0), (1,0,0), (2,0,0) ....,(8,5,0),去掉Z坐标,记为二维矩阵
        objp = np.zeros((corner_h * corner_w, 3), np.float32)
        # 将世界坐标系建在标定板上,所有点的Z坐标全部为0,所以只需要赋值x和y
        objp[:, :2] = np.mgrid[0:corner_w, 0:corner_h].T.reshape(-1, 2)
        square_size = 50.0  # 18.1  # 18.1 mm
        objp = objp * square_size

        # 储存棋盘格角点的世界坐标和图像坐标对
        obj_points = []  # 存储3D点  # 在世界坐标系中的三维点
        img_points = []  # 存储2D点  # 在图像平面的二维点

        images = glob.glob(os.path.join(source_path, "*." + suffix))
        for index, fname in enumerate(images):
            print("=== ", index)
            img = cv2.imread(fname)
            h_, w_, _ = img.shape
            if h_ != self.H or w_ != self.W:
                img = cv2.resize(img, (self.W, self.H), interpolation=cv2.INTER_CUBIC)

            self.gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            size = self.gray.shape[::-1]
            ret, corners = cv2.findChessboardCorners(self.gray, (corner_w, corner_h), None)

            if (corners[0, 0, 0] < corners[-1, 0, 0]):
                print("*" * 5 + " order of {} is inverse! ".format(index) + "*" * 5)
                corners = np.flip(corners, axis=0).copy()

            if ret:
                obj_points.append(objp)
                corners2 = cv2.cornerSubPix(self.gray, corners, (11, 11), (-1, -1), criteria)  # 在原角点的基础上寻找亚像素角点
                img_points.append(corners2)

                cv2.drawChessboardCorners(img, (corner_w, corner_h), corners2, ret)  # 记住,OpenCV的绘制函数一般无返回值
                cv2.imshow('img', img)
                cv2.waitKey(50)

        cv2.destroyAllWindows()

        # print("img_points: ", img_points.shape)
        # print("obj_points: ", obj_points.shape)

        # 标定
        ret, self.mtx, self.dist, self.rvecs, self.tvecs = cv2.calibrateCamera(obj_points, img_points, self.gray.shape[::-1], None, None)

        print("ret:", ret)
        print("mtx:\n", self.mtx)  # 内参数矩阵
        print("dist:\n", self.dist)  # 畸变系数   distortion cofficients = (k_1,k_2,p_1,p_2,k_3)

        # 并且通过实验表明,distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
        # 三个参数的时候由于k3所对应的非线性较为剧烈。估计的不好,容易产生极大的扭曲,所以k_3强制设置为0.0
        # self.dist[0, 4] = 0.0

        return self.mtx, self.dist, self.rvecs, self.tvecs

    def Mono_Undisort_list(self, source_path, suffix="png"):
        # 对所有图片进行去畸变,有两种方法实现分别为: undistort()和remap()
        images = glob.glob(os.path.join(source_path, "*." + suffix))
        for fname in images:
            fname = '/'.join(fname.split('\\'))
            print(fname)
            prefix = fname.split('/')[-1].replace(".", "_undistort.")
            # print(prefix)
            img = cv2.imread(fname)
            h_, w_, _ = img.shape
            if h_ != self.H or w_ != self.W:
                img = cv2.resize(img, (self.W, self.H), interpolation=cv2.INTER_CUBIC)


            dst = self.Mono_Rectify(img, flag=True)


            print("img: ", img.shape)
            print("dst: ", dst.shape)
            # cv2.imshow('img', img)
            # cv2.imshow('dst', dst)
            #
            # cv2.waitKey(500)

            if not os.path.isdir(os.path.join(source_path, "mono_rec")):
                os.mkdir(os.path.join(source_path, "mono_rec"))
            cv2.imwrite(os.path.join(source_path, "mono_rec", prefix), dst)

    def Mono_Rectify(self, image, flag=True):
        image = cv2.resize(image, (self.W, self.H))

        # 使用 cv.undistort()进行畸变校正
        if flag:
            # 矫正单目图像:直接使用计算的相机内参矩阵
            image_rec = cv2.undistort(image, self.mtx, self.dist, None, self.mtx)

        else:
            # 矫正单目图像:使用计算的相机内参矩阵计算新的内参矩阵
            '''
            1、默认情况下,我们通常不会求取新的CameraMatrix,这样代码中会默认使用标定得到的CameraMatrix。
                而这个摄像机矩阵是在理想情况下没有考虑畸变得到的,所以并不准确,重要的是fx和fy的值会比考虑畸变情况下的偏大,
                会损失很多有效像素。我们可以通过这个函数getOptimalNewCameraMatrix ()求取一个新的摄像机内参矩阵,
            2、cv2.getOptimalNewCameraMatrix()。如果参数alpha = 0, 它返回含有最小不需要像素的非扭曲图像,
                所以它可能移除一些图像角点。如果alpha = 1, 所有像素都返回。还会返回一个 ROI 图像,我们可以用来对结果进行裁剪。
            '''
            h, w = self.H, self.W
            newcameramtx, roi = cv2.getOptimalNewCameraMatrix(self.mtx, self.dist, (w, h), 0, (w, h))
            image_rec = cv2.undistort(image, self.mtx, self.dist, None, newcameramtx)
            # crop and save the image
            # x, y, w, h = roi
            # image_rec = image_rec[y:y + h, x:x + w]

        return image_rec

    def read_cali_file(self, cali_file):
        con = configparser.ConfigParser()
        con.read(cali_file, encoding='utf-8')
        sections = con.sections()
        # print(sections.items)
        calib = con.items('Calib')
        rectify = con.items('Rectify')
        calib = dict(calib)
        rectify = dict(rectify)

        self.u0 = calib['u0']
        self.v0 = calib['v0']
        self.fx = calib['fx']
        self.fy = calib['fy']

        self.mtx = np.array([[rectify['mtx_0'], rectify['mtx_1'], rectify['mtx_2']],
                            [rectify['mtx_3'], rectify['mtx_4'], rectify['mtx_5']],
                            [rectify['mtx_6'], rectify['mtx_7'], rectify['mtx_8']]]).astype('float32')

        self.dist = np.array([[rectify['dist_k1'],
                               rectify['dist_k2'],
                               rectify['dist_p1'],
                               rectify['dist_p2'],
                               rectify['dist_k3']]]).astype('float32')

    def write_cali_file(self, cali_file="./MonoCalib_Param.ini"):
        self.u0 = self.mtx[0, 2]
        self.v0 = self.mtx[1, 2]
        self.fx = self.mtx[0, 0]
        self.fy = self.mtx[1, 1]
        self.Calib = {"fx": self.fx,
                      "fy": self.fy,
                      "u0": self.u0,
                      "v0": self.v0,
                     }
        # 相机内参矩阵
        # fx  s   x0
        # 0   fy  y0
        # 0   0    1
        Rectify = {}
        for i in range(3):
            for j in range(3):
                Rectify['mtx_{}'.format(i*3+j)] = self.mtx[i, j]

        # 相机畸变系数
        # 畸变系数   distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
        Rectify['dist_k1'] = self.dist[0, 0]
        Rectify['dist_k2'] = self.dist[0, 1]
        Rectify['dist_p1'] = self.dist[0, 2]
        Rectify['dist_p2'] = self.dist[0, 3]
        Rectify['dist_k3'] = self.dist[0, 4]

        self.Rectify = Rectify
        # Write calibration param to file.
        config = configparser.ConfigParser()
        config["Calib"] = self.Calib
        config["Rectify"] = self.Rectify
        # with open("Calib_Param_4.ini", 'w') as configfile:
        with open(cali_file, 'w') as configfile:
            config.write(configfile)


def main():
    source_path = "path to your image"
    mono_calibration = MonocularCalibration(data_root=source_path,
                                            cali_file="MonoCalib_Param_720p.ini",
                                            board_size=(7, 11),
                                            img_shape=(720, 1280),
                                            suffix="png")
    print(mono_calibration.mtx)
    print(mono_calibration.dist)
    data_root = source_path
    mono_calibration.Mono_Undisort_list(data_root, suffix="png")


if __name__ == '__main__':
    main()

————————————————

原文链接:https://blog.csdn.net/qq_38906523/article/details/125321999

你可能感兴趣的:(python,图像处理)