1

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?
Community
  • 1
  • 1
dariush
  • 3,191
  • 3
  • 24
  • 43
  • was the chessboard completely visible in both input images? – Micka Jul 29 '16 at 08:30
  • 1
    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 Jul 29 '16 at 08:32
  • @Micka Yes, the chess board is completely visible, it is from the OpenCV's standard stereo image sets(see [left](https://github.com/opencv/opencv/blob/master/samples/data/left01.jpg) and [right](https://github.com/opencv/opencv/blob/master/samples/data/right01.jpg) image), The posted image is the output of rectification process which is weird! – dariush Jul 29 '16 at 09:18
  • The cropped squares at the border should not be a problem. This is what I get with the [calibration.cpp](https://github.com/opencv/opencv/blob/3.1.0/samples/cpp/calibration.cpp) sample for left images. It should be possible to do the same with the stereo calibration sample. – Catree Jul 29 '16 at 09:34
  • `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-01` – Catree Jul 29 '16 at 09:34
  • I don't have problem with the calibration, the rectification causes the headache!! – dariush Jul 30 '16 at 07:41
  • Just to make sure for people that might encounter that question in the future : almost all OpenCV features have example code in the "Sample" document of the source repository. For your case, in python, [here](https://github.com/opencv/opencv/blob/master/samples/python/calibrate.py). – Cedric Aug 01 '16 at 15:42

1 Answers1

1

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 :)

dariush
  • 3,191
  • 3
  • 24
  • 43