FireEyes火眼双目系列教程2--如何使用代码和软件标定双目摄像头

        上一回,讲到针对FireEyes火眼双目相机,如何使用opencv快速读取压缩格式的双目摄像头,以及如何使用软件方便的保存截图和录制双目视频。本期文章,继续带大家实现完整的标定流程。双目标定网上资料很多,但大都语焉不详或经验不充分,无法给出科学的指导。

        实际上,双目标定是一个非常复杂的过程。本文且不去从理论公式去进行推导,后面有时间再详细叙述,而是从代码角度去考虑如何做一次合格的标定。首先打开老板配套的标定文件,查看双目标定参数应该是什么样子的。

<?xml version="1.0"?>
<opencv_storage>
<Camera_Matrix_Left type_id="opencv-matrix">
  <rows>3</rows>
  <cols>3</cols>
  <dt>d</dt>
  <data>
    1.5102575076556511e+03 0. 5.7050825703054056e+02 0.
    1.5131519293597491e+03 4.2155950604941597e+02 0. 0. 1.</data></Camera_Matrix_Left>
<Distortion_Coeffs_Left type_id="opencv-matrix">
  <rows>1</rows>
  <cols>14</cols>
  <dt>d</dt>
  <data>
    -1.3128679058980449e+01 4.0862925866371192e+01 0. 0.
    1.4583347052559756e+02 -1.3191469679503978e+01
    4.1873896305351899e+01 1.4086762713689984e+02 0. 0. 0. 0. 0. 0.</data></Distortion_Coeffs_Left>
<Camera_Matrix_Right type_id="opencv-matrix">
  <rows>3</rows>
  <cols>3</cols>
  <dt>d</dt>
  <data>
    1.5060488762224657e+03 0. 5.8851182947326470e+02 0.
    1.5081464609407883e+03 3.4116061588680532e+02 0. 0. 1.</data></Camera_Matrix_Right>
<Distortion_Coeffs_Right type_id="opencv-matrix">
  <rows>1</rows>
  <cols>14</cols>
  <dt>d</dt>
  <data>
    3.4286887938153086e+01 -1.2017552811473685e+01 0. 0.
    -1.0681449825891393e+02 3.4429671708623260e+01
    -1.6462504185104354e+01 -1.0354548661383124e+02 0. 0. 0. 0. 0. 0.</data></Distortion_Coeffs_Right>
<Rotation_Matrix_Stereo type_id="opencv-matrix">
  <rows>3</rows>
  <cols>3</cols>
  <dt>d</dt>
  <data>
    9.9950379179889370e-01 -1.0206938069847095e-04
    -3.1498567609896992e-02 8.2326232746755107e-04
    9.9973778948213443e-01 2.2883935861554218e-02 3.1487972605010649e-02
    -2.2898512248988541e-02 9.9924179542191338e-01</data></Rotation_Matrix_Stereo>
<Translation_Matrix_Stereo type_id="opencv-matrix">
  <rows>3</rows>
  <cols>1</cols>
  <dt>d</dt>
  <data>
    -2.4591255054712686e+00 2.5114034736998098e-02
    -3.5916004400515719e-02</data></Translation_Matrix_Stereo>
<Essential_Matrix type_id="opencv-matrix">
  <rows>3</rows>
  <cols>3</cols>
  <dt>d</dt>
  <data>
    8.2035833117598527e-04 3.5331512814355523e-02 2.5916892701990980e-02
    4.1534693963981066e-02 -5.6306649584507658e-02
    2.4583922879478179e+00 -2.7126078334167827e-02
    -2.4584781335250101e+00 -5.5483414221595966e-02</data></Essential_Matrix>
<Fundamental_Matrix type_id="opencv-matrix">
  <rows>3</rows>
  <cols>3</cols>
  <dt>d</dt>
  <data>
    4.6591706670341746e-09 2.0027914415472244e-07 1.3521207036972270e-04
    2.3556544703483535e-07 -3.1873424503415368e-07
    2.1057277146899138e-02 -3.1513066545476264e-04
    -2.0997506649126866e-02 1.</data></Fundamental_Matrix>
</opencv_storage>

        需要清楚,M1,d1,M2,d2,R,T是做双目校正时的必需量,M1--Camera1的内参,d1--C1的畸变,M2--C2的内参,d2--C2的畸变。R--旋转矩阵,T--平移矩阵。E和F可选。

        为了方便后续使用opencv进行视差计算,标定最简易的方法是采用opencv来做。虽然matlab也有很多工具箱,但转换起来着实费力且容易出错。opencv的代码如下所示:

class StereoCalibration(object):
    def __init__(self, filepath, w, h,left_dir,right_dir):
        self.criteria = (cv2.TERM_CRITERIA_EPS +
                         cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
        self.criteria_cal = (cv2.TERM_CRITERIA_EPS +
                             cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-5)
        # prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
        self.objp = np.zeros((w*h, 3), np.float32)
        self.objp[:, :2] = np.mgrid[0:w, 0:h].T.reshape(-1, 2)
        # Arrays to store object points and image points from all the images.
        self.objpoints = []  # 3d point in real world space
        self.imgpoints_l = []  # 2d points in image plane.
        self.imgpoints_r = []  # 2d points in image plane.
        self.img_shape = None
        self.cal_path = filepath
        self.camera_model = None
        self.read_images(self.cal_path,w,h,left_dir,right_dir)


    def read_images(self, cal_path,w,h,left_dir,right_dir):
        images_right = glob.glob(right_dir + '/*.png')
        images_left = glob.glob(left_dir + '/*.png')
        images_left.sort()
        images_right.sort()
        for i, fname in enumerate(images_right):
            img_l = cv2.imread(images_left[i])
            img_r = cv2.imread(images_right[i])
            gray_l = cv2.cvtColor(img_l, cv2.COLOR_BGR2GRAY)
            gray_r = cv2.cvtColor(img_r, cv2.COLOR_BGR2GRAY)
            # Find the chess board corners
            ret_l, corners_l = cv2.findChessboardCorners(gray_l, (w, h), None)
            ret_r, corners_r = cv2.findChessboardCorners(gray_r, (w, h), None)
            # If found, add object points, image points (after refining them)
            self.objpoints.append(self.objp)

            if ret_l is True:
                rt = cv2.cornerSubPix(gray_l, corners_l, (11, 11),
                                      (-1, -1), self.criteria)
                self.imgpoints_l.append(corners_l)

            if ret_r is True:
                rt = cv2.cornerSubPix(gray_r, corners_r, (11, 11),
                                      (-1, -1), self.criteria)
                self.imgpoints_r.append(corners_r)

            self.img_shape = gray_l.shape[::-1]

        rt, self.M1, self.d1, self.r1, self.t1 = cv2.calibrateCamera(
            self.objpoints, self.imgpoints_l, self.img_shape, None, None)
        rt, self.M2, self.d2, self.r2, self.t2 = cv2.calibrateCamera(
            self.objpoints, self.imgpoints_r, self.img_shape, None, None)

        self.camera_model = self.stereo_calibrate(self.img_shape)

    def stereo_calibrate(self, dims):
        flags = 0
        flags |= cv2.CALIB_USE_INTRINSIC_GUESS

        stereocalib_criteria = (cv2.TERM_CRITERIA_MAX_ITER +
                                cv2.TERM_CRITERIA_EPS, 100, 1e-5)
        ret, M1, d1, M2, d2, R, T, E, F = cv2.stereoCalibrate(
            self.objpoints, self.imgpoints_l,
            self.imgpoints_r, self.M1, self.d1, self.M2,
            self.d2, dims,
            criteria=stereocalib_criteria, flags=flags)

        print('Intrinsic_mtx_1', M1)
        print('dist_1', d1)
        print('Intrinsic_mtx_2', M2)
        print('dist_2', d2)
        print('R', R)
        print('T', T)
        print('E', E)
        print('F', F)
        self.camera_model = dict([('M1', M1), ('M2', M2), ('dist1', d1),
                            ('dist2', d2), ('rvecs1', self.r1),
                            ('rvecs2', self.r2), ('R', R), ('T', T),
                            ('E', E), ('F', F)])
        return self.camera_model

        c++的代码还要更复杂一些,操作也不方便。在实操上,我们借用FireEyes火眼双目标定的软件,虽然该软件是为火眼双目研发,但并没有限制其使用范围(👍)。

        注意看这张图片,标定板内角点是11*8,如果填错了,opencv是不会正确响应的,c++和python都一样。要点是需要去掉最边上的角点,数横竖内角点的个数!!!切记。另外,标定时,一般简易多拍一些标定板在图像里占1/3大小的图片(重要!),如果失败了,补充少量大的图片,像这张图里面一样。①1/3的图片要上下左右中五个方位全覆盖。②补充不同距离的图片,如几张更远的和几张更近的,也尽量覆盖全方位,但是个数少一些。③ 如果依然失败,那么尝试删除一些倾斜角度大的,更容易成功。

        点击多次采集图片,在标定配置里面选择文件夹:Documents\fire_eyes\2024-05-09\left和Documents\fire_eyes\2024-05-09\right。点击确定后,就开始自动标定了。

        之后,点击校正切换,软件会自动执行校正并在软件中显示,查看效果非常直观。鉴于各种未知因素,标定结果可能不尽如人意,请尽量使用随相机发放的标定文件(更专业)。本次标定实验非常成功,可以看到,在执行“校正切换”后,将双目进行了对齐(因校正后存在弧度和留黑,导致肉眼不太容易查看效果,可以通过SGBM算法来验证)!同时期待软件中添加画线查看功能。

        下一期,我们将基于这个标定参数,查看视差生成的效果了!

FireEyes 火眼 双目相机 USB摄像头模组 立体视觉 工业 U90/U140-淘宝网 (taobao.com)

软件及代码地址:

FireEyesGit/FireEyesUsbSoftware: stereo calibrate and match tools for FireEyes U90/140 (github.com)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值