我组装了一个立体摄影机,在使用它来产生良好的视差图时遇到了麻烦。这是两个校正后的图像和我用它们产生的视差图的示例:

如您所见,结果非常糟糕。更改StereoBM的设置不会有太大变化。

设置

  • 两个相机的型号相同,并通过USB连接到我的计算机。
  • 它们固定在刚性木板上,因此不会移动。我尽了最大的努力使它们对齐,但这当然不是完美的。它们无法移动,因此在校准期间和校准之后它们的位置相同。
  • 我使用OpenCV校准了立体声对,并且正在使用OpenCV的StereoBM类生成视差图。
  • 可能不那么相关,但是我在Python中进行编码。

  • 我可以想象的问题

    我是第一次这样做,所以我离成为专家还差得远,但是我猜测问题出在校准或立体校正中,而不是视差图的计算中。我已经尝试了StereoBM的所有设置置换,尽管得到了不同的结果,但它们都像上面显示的视差图:黑色和白色补丁。

    据我所知,立体整流应对齐每张图片上的所有点,以便它们通过一条直线(在我的情况下为水平线)连接,这一事实进一步得到了支持。如果我将两个校正后的图片彼此紧挨着检查,那么显然不是这种情况。右图的对应点比左图高。不过,我不确定是校准还是整流。

    代码

    实际的代码包装在对象中-如果您有兴趣完整查看它,可以使用on GitHub。这是实际运行的简化示例(当然,在实际代码中,我仅使用了两张图片进行校准):

    import cv2
    import numpy as np
    
    ## Load test images
    # TEST_IMAGES is a list of paths to test images
    input_l, input_r = [cv2.imread(image, cv2.CV_LOAD_IMAGE_GRAYSCALE)
                        for image in TEST_IMAGES]
    image_size = input_l.shape[:2]
    
    ## Retrieve chessboard corners
    # CHESSBOARD_ROWS and CHESSBOARD_COLUMNS are the number of inside rows and
    # columns in the chessboard used for calibration
    pattern_size = CHESSBOARD_ROWS, CHESSBOARD_COLUMNS
    object_points = np.zeros((np.prod(pattern_size), 3), np.float32)
    object_points[:, :2] = np.indices(pattern_size).T.reshape(-1, 2)
    # SQUARE_SIZE is the size of the chessboard squares in cm
    object_points *= SQUARE_SIZE
    image_points = {}
    ret, corners_l = cv2.findChessboardCorners(input_l, pattern_size, True)
    cv2.cornerSubPix(input_l, corners_l,
                     (11, 11), (-1, -1),
                     (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS,
                      30, 0.01))
    image_points["left"] = corners_l.reshape(-1, 2)
    ret, corners_r = cv2.findChessboardCorners(input_r, pattern_size, True)
    cv2.cornerSubPix(input_r, corners_r,
                     (11, 11), (-1, -1),
                     (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS,
                      30, 0.01))
    image_points["right"] = corners_r.reshape(-1, 2)
    
    ## Calibrate cameras
    (cam_mats, dist_coefs, rect_trans, proj_mats, valid_boxes,
     undistortion_maps, rectification_maps) = {}, {}, {}, {}, {}, {}, {}
    criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS,
                100, 1e-5)
    flags = (cv2.CALIB_FIX_ASPECT_RATIO + cv2.CALIB_ZERO_TANGENT_DIST +
             cv2.CALIB_SAME_FOCAL_LENGTH)
    (ret, cam_mats["left"], dist_coefs["left"], cam_mats["right"],
     dist_coefs["right"], rot_mat, trans_vec, e_mat,
     f_mat) = cv2.stereoCalibrate(object_points,
                                  image_points["left"], image_points["right"],
                                  image_size, criteria=criteria, flags=flags)
    (rect_trans["left"], rect_trans["right"],
     proj_mats["left"], proj_mats["right"],
     disp_to_depth_mat, valid_boxes["left"],
     valid_boxes["right"]) = cv2.stereoRectify(cam_mats["left"],
                                               dist_coefs["left"],
                                               cam_mats["right"],
                                               dist_coefs["right"],
                                               image_size,
                                               rot_mat, trans_vec, flags=0)
    for side in ("left", "right"):
        (undistortion_maps[side],
         rectification_maps[side]) = cv2.initUndistortRectifyMap(cam_mats[side],
                                                               dist_coefs[side],
                                                               rect_trans[side],
                                                               proj_mats[side],
                                                               image_size,
                                                               cv2.CV_32FC1)
    
    ## Produce disparity map
    rectified_l = cv2.remap(input_l, undistortion_maps["left"],
                            rectification_maps["left"],
                            cv2.INTER_NEAREST)
    rectified_r = cv2.remap(input_r, undistortion_maps["right"],
                            rectification_maps["right"],
                            cv2.INTER_NEAREST)
    cv2.imshow("left", rectified_l)
    cv2.imshow("right", rectified_r)
    block_matcher = cv2.StereoBM(cv2.STEREO_BM_BASIC_PRESET, 0, 5)
    disp = block_matcher.compute(rectified_l, rectified_r, disptype=cv2.CV_32F)
    cv2.imshow("disparity", disp)
    

    这是怎么了

    最佳答案

    事实证明,问题出在可视化,而不是数据本身。我在某处读到cv2.reprojectImageTo3D需要视差图作为浮点值,这就是为什么我要从cv2.CV_32F请求block_matcher.compute的原因。

    更加仔细地阅读OpenCV文档使我认为我在错误地考虑了这一点,为了提高速度,我实际上想使用整数而不是浮点数,但是cv2.imshow的文档尚不清楚它的作用具有16位带符号整数(与16位无符号整数相比),因此对于可视化,我将值保留为浮点型。

    documentation of cv2.imshow 显示假定32位浮点值在0到1之间,因此将它们乘以255。255是像素显示为白色的饱和点。就我而言,这种假设产生了一个二进制图。我手动将其缩放到0-255的范围内,然后将其除以255,以消除OpenCV也执行相同操作的事实。我知道,这是一个可怕的操作,但我这样做只是为了脱机调整StereoBM,因此性能并不重要。解决方案如下所示:

    # Other code as above
    disp = block_matcher.compute(rectified_l, rectified_r, disptype=cv2.CV_32F)
    norm_coeff = 255 / disp.max()
    cv2.imshow("disparity", disp * norm_coeff / 255)
    

    然后视差图看起来还可以。

    09-06 11:55