1
votes

I have been digging on this topic for almost a week and couldn't find any solid solution yet. It is interesting that no one ever posted a straightforward solution on how to calibrate and rectify a stereo camera with OpenCV in order to compute the depth, from here and there(this for calibration and this for rectification, the codes posted are not quite integrated though) I have come up with the following code snap, BUT it does not rectify the image OK!!

import numpy as np
import cv2
import glob

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

# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((6*9,3), np.float32)
objp[:,:2] = np.mgrid[0:9,0:6].T.reshape(-1,2)

# Arrays to store object points and image points from all the images.
objpoints = {} # 3d point in real world space
imgpoints = {} # 2d points in image plane.

# calibrate stereo
for side in ['left', 'right']:
    counter = 0
    images = glob.glob('images/%s*.jpg' %side)
    objpoints[side] = [];
    imgpoints[side] = [];
    for fname in images:
        img = cv2.imread(fname)
        gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

        # Find the chess board corners
        ret, corners = cv2.findChessboardCorners(gray, (9,6),None)
        # If found, add object points, image points (after refining them)
        if ret == True:
            objpoints[side].append(objp)

            cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
            imgpoints[side].append(corners)
            counter += 1

    assert counter == len(images), "missed chessboard!!"


stereocalib_criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS, 100, 1e-5)
stereocalib_flags = cv2.CALIB_FIX_ASPECT_RATIO | cv2.CALIB_ZERO_TANGENT_DIST | cv2.CALIB_SAME_FOCAL_LENGTH | cv2.CALIB_RATIONAL_MODEL | cv2.CALIB_FIX_K3 | cv2.CALIB_FIX_K4 | cv2.CALIB_FIX_K5
retval,cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, R, T, E, F = cv2.stereoCalibrate(objpoints['left'], imgpoints['left'], imgpoints['right'], (640, 480), criteria = stereocalib_criteria, flags = stereocalib_flags)

rectify_scale = 0.1 # 0=full crop, 1=no crop
R1, R2, P1, P2, Q, roi1, roi2 = cv2.stereoRectify(cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, (640, 480), R, T, alpha = rectify_scale)

left_maps = cv2.initUndistortRectifyMap(cameraMatrix1, distCoeffs1, R1, P1, (640, 480), cv2.CV_16SC2)
right_maps = cv2.initUndistortRectifyMap(cameraMatrix2, distCoeffs2, R2, P2, (640, 480), cv2.CV_16SC2)

# Assuming you have left01.jpg and right01.jpg that you want to rectify
lFrame = cv2.imread('images/left01.jpg')
rFrame = cv2.imread('images/right01.jpg')

left_img_remap = cv2.remap(lFrame, left_maps[0], left_maps[1], cv2.INTER_LANCZOS4)
right_img_remap = cv2.remap(rFrame, right_maps[0], right_maps[1], cv2.INTER_LANCZOS4)

for line in range(0, int(right_img_remap.shape[0] / 20)):
    left_img_remap[line * 20, :] = (0, 0, 255)
    right_img_remap[line * 20, :] = (0, 0, 255)

cv2.imshow('winname', np.hstack([left_img_remap, right_img_remap]))
cv2.waitKey(0)
exit(0)

The output of the above is the image below enter image description here

As you can see the images are not rectified!!

Question:

  • What is wrong with the code?
1
was the chessboard completely visible in both input images?Micka
probably your chessboard should contain only full-squares, not those cropped squares at the border. Maybe the algorithm misinterprets them as distorted full-squares and tries to undistort them.Micka
@Micka Yes, the chess board is completely visible, it is from the OpenCV's standard stereo image sets(see left and right image), The posted image is the output of rectification process which is weird!dariush
The cropped squares at the border should not be a problem. This is what I get with the calibration.cpp sample for left images. It should be possible to do the same with the stereo calibration sample.Catree
image_width: 640 image_height: 480 board_width: 9 board_height: 6 square_size: 1. aspectRatio: 1. flags: 2 camera_matrix: !!opencv-matrix rows: 3 cols: 3 dt: d data: [ 5.3590117051349637e+02, 0., 3.4227429926016583e+02, 0., 5.3590117051349637e+02, 2.3557560607943688e+02, 0., 0., 1. ] distortion_coefficients: !!opencv-matrix rows: 5 cols: 1 dt: d data: [ -2.6643160989580222e-01, -3.8505305722612772e-02, 1.7844280073183410e-03, -2.7702634246810361e-04, 2.3850218962079497e-01 ] avg_reprojection_error: 3.9229331915929899e-01Catree

1 Answers

1
votes

I couldn't find what was the thing I did wrong which led to incorrect answers, but for what it worth I have found a solution that does rectify OK and more!!
I came across the StereoVision library and considering the low documentation level it has, I have managed to fetch/write the following snaps which calibrate and rectifies OK.

import cv2
import os.path
import numpy as np
from stereovision.calibration import StereoCalibrator, StereoCalibration
from stereovision.blockmatchers import StereoBM, StereoSGBM

calib_dir = 'data/config/calibration'
if(not os.path.exists(calib_dir)):
    calibrator = StereoCalibrator(9, 6, 2, (480, 640))
    for idx in range(1, 14):
        calibrator.add_corners((cv2.imread('images/left%02d.jpg' %idx), cv2.imread('images/right%02d.jpg' %idx)))

    calibration = calibrator.calibrate_cameras()
    print "Calibation error:", calibrator.check_calibration(calibration)
    calibration.export(calib_dir)

calibration = StereoCalibration(input_folder=calib_dir)

if True:
    block_matcher = StereoBM()
else:
    block_matcher = StereoSGBM()

for idx in range(1, 14):
    image_pair = (cv2.imread('images/left%02d.jpg' %idx), cv2.imread('images/right%02d.jpg' %idx))
    rectified_pair = calibration.rectify(image_pair)
    disparity = block_matcher.get_disparity(rectified_pair)
    norm_coeff = 255 / disparity.max()
    cv2.imshow('Disparity %02d' %idx, disparity * norm_coeff / 255)

    for line in range(0, int(rectified_pair[0].shape[0] / 20)):
        rectified_pair[0][line * 20, :] = (0, 0, 255)
        rectified_pair[1][line * 20, :] = (0, 0, 255)

    cv2.imshow('Rect %02d' %idx, np.hstack(rectified_pair))
    cv2.waitKey()

The following is the result of rectification of the same image I have posted in my question. enter image description here Although for computing the disparity map It needs its parameters to be tuned(a tool is provided by the package) but it will do the job :)