├── .gitignore ├── README.md └── camera_calibrate.py /.gitignore: -------------------------------------------------------------------------------- 1 | ._* 2 | .DS_Store 3 | *.swp 4 | *.pyc 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | stereo_calibration - StereoCamera Calibration using Python-OpenCV 3 | ================================================================= 4 | * Python Implementation of Stereo Camera Calibration using [checkerboard pattern images](http://docs.opencv.org/2.4/doc/tutorials/calib3d/camera_calibration/camera_calibration.html). 5 | 6 | Features 7 | -------- 8 | * Prints -or- Returns dict object containing Camera Matrices and R, T, E and F 9 | * Takes Stereo Camera Images path as an input 10 | 11 | Requirements 12 | ------------ 13 | * cv2 14 | * numpy 15 | 16 | Example 17 | ------- 18 | * Run using the command line 19 | ``` 20 | python camera_calibration.py /path/to/stereo/camera/images/ 21 | ``` 22 | * Run using Python 23 | ``` 24 | >>> from camera_calibration import StereoCalibration 25 | >>> cal = StereoCalibration('/path/to/stereo/camera/images') 26 | >>> cal.camera_model 27 | ``` 28 | 29 | Results 30 | ------- 31 | ``` 32 | $python camera_calibration.py /path/to/stereo/camera/images/ 33 | ('Intrinsic_mtx_1', array([[ 4.14602008e+03, 0.00000000e+00, 7.59295870e+02], 34 | [ 0.00000000e+00, 4.21635719e+03, 4.42260382e+02], 35 | [ 0.00000000e+00, 0.00000000e+00, 1.00000000e+00]])) 36 | ('dist_1', array([[ -2.73378180e+00, 1.41433393e+02, -1.36677475e-02, 37 | 1.01134046e-01, -5.17885999e+03]])) 38 | ('Intrinsic_mtx_2', array([[ 4.16808926e+03, 0.00000000e+00, 7.33997545e+02], 39 | [ 0.00000000e+00, 4.20937958e+03, 7.02753997e+01], 40 | [ 0.00000000e+00, 0.00000000e+00, 1.00000000e+00]])) 41 | ('dist_2', array([[ -7.91010976e-01, 4.49627502e+01, -1.55972074e-02, 42 | -3.95037927e-03, -1.24662356e+03]])) 43 | ('R', array([[ 0.99869078, 0.00100433, 0.05114416], 44 | [-0.00564778, 0.99585969, 0.09072808], 45 | [-0.05084128, -0.09089815, 0.99456156]])) 46 | ('T', array([[-9.87912629], 47 | [ 0.24163047], 48 | [ 1.61658844]])) 49 | ('E', array([[ -3.15467525e-03, -1.63185902e+00, 9.36464101e-02], 50 | [ 1.11220449e+00, -8.96370670e-01, 9.90807829e+00], 51 | [ -1.85519033e-01, -9.83846631e+00, -9.08672125e-01]])) 52 | ('F', array([[ 3.44154284e-09, 1.75055259e-06, -1.20037917e-03], 53 | [ -1.20144000e-06, 9.52136218e-07, -4.38838342e-02], 54 | [ 9.25481179e-04, 4.26384886e-02, 1.00000000e+00]])) 55 | ``` 56 | 57 | Output Parameters 58 | ----------------- 59 | __Intrinsic_mtx_1__ – output first camera matrix 60 | 61 | __dist_1__ – output vector of distortion coefficients (k_1, k_2, p_1, p_2[, k_3[, k_4, k_5, k_6]]) of 4, 5, or 8 elements. The output vector length depends on the flags. 62 | 63 | __Intrinsic_mtx_2__ – output second camera matrix 64 | 65 | __dist_2__ – output lens distortion coefficients for the second camera 66 | 67 | __R__ – Output rotation matrix between the 1st and the 2nd camera coordinate systems. 68 | 69 | __T__ – Output translation vector between the coordinate systems of the cameras. 70 | 71 | __E__ – Output essential matrix. 72 | 73 | __F__ – Output fundamental matrix. 74 | 75 | More reference on __R__, __T__, __E__ and __F__ can be found [here](http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#stereocalibrate) 76 | 77 | Notes 78 | ----- 79 | * Assumption here is that given image path contains two folders of checkerboard images named `LEFT` and `RIGHT`. User can change these relative folder paths. 80 | * User may also change `flags` as per calibration output requirements. 81 | 82 | References 83 | ---------- 84 | * [Stereo Camera Calibration](http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html) using OpenCV 85 | -------------------------------------------------------------------------------- /camera_calibrate.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | import glob 4 | import argparse 5 | 6 | 7 | class StereoCalibration(object): 8 | def __init__(self, filepath): 9 | # termination criteria 10 | self.criteria = (cv2.TERM_CRITERIA_EPS + 11 | cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) 12 | self.criteria_cal = (cv2.TERM_CRITERIA_EPS + 13 | cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-5) 14 | 15 | # prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0) 16 | self.objp = np.zeros((9*6, 3), np.float32) 17 | self.objp[:, :2] = np.mgrid[0:9, 0:6].T.reshape(-1, 2) 18 | 19 | # Arrays to store object points and image points from all the images. 20 | self.objpoints = [] # 3d point in real world space 21 | self.imgpoints_l = [] # 2d points in image plane. 22 | self.imgpoints_r = [] # 2d points in image plane. 23 | 24 | self.cal_path = filepath 25 | self.read_images(self.cal_path) 26 | 27 | def read_images(self, cal_path): 28 | images_right = glob.glob(cal_path + 'RIGHT/*.JPG') 29 | images_left = glob.glob(cal_path + 'LEFT/*.JPG') 30 | images_left.sort() 31 | images_right.sort() 32 | 33 | for i, fname in enumerate(images_right): 34 | img_l = cv2.imread(images_left[i]) 35 | img_r = cv2.imread(images_right[i]) 36 | 37 | gray_l = cv2.cvtColor(img_l, cv2.COLOR_BGR2GRAY) 38 | gray_r = cv2.cvtColor(img_r, cv2.COLOR_BGR2GRAY) 39 | 40 | # Find the chess board corners 41 | ret_l, corners_l = cv2.findChessboardCorners(gray_l, (9, 6), None) 42 | ret_r, corners_r = cv2.findChessboardCorners(gray_r, (9, 6), None) 43 | 44 | # If found, add object points, image points (after refining them) 45 | self.objpoints.append(self.objp) 46 | 47 | if ret_l is True: 48 | rt = cv2.cornerSubPix(gray_l, corners_l, (11, 11), 49 | (-1, -1), self.criteria) 50 | self.imgpoints_l.append(corners_l) 51 | 52 | # Draw and display the corners 53 | ret_l = cv2.drawChessboardCorners(img_l, (9, 6), 54 | corners_l, ret_l) 55 | cv2.imshow(images_left[i], img_l) 56 | cv2.waitKey(500) 57 | 58 | if ret_r is True: 59 | rt = cv2.cornerSubPix(gray_r, corners_r, (11, 11), 60 | (-1, -1), self.criteria) 61 | self.imgpoints_r.append(corners_r) 62 | 63 | # Draw and display the corners 64 | ret_r = cv2.drawChessboardCorners(img_r, (9, 6), 65 | corners_r, ret_r) 66 | cv2.imshow(images_right[i], img_r) 67 | cv2.waitKey(500) 68 | img_shape = gray_l.shape[::-1] 69 | 70 | rt, self.M1, self.d1, self.r1, self.t1 = cv2.calibrateCamera( 71 | self.objpoints, self.imgpoints_l, img_shape, None, None) 72 | rt, self.M2, self.d2, self.r2, self.t2 = cv2.calibrateCamera( 73 | self.objpoints, self.imgpoints_r, img_shape, None, None) 74 | 75 | self.camera_model = self.stereo_calibrate(img_shape) 76 | 77 | def stereo_calibrate(self, dims): 78 | flags = 0 79 | flags |= cv2.CALIB_FIX_INTRINSIC 80 | # flags |= cv2.CALIB_FIX_PRINCIPAL_POINT 81 | flags |= cv2.CALIB_USE_INTRINSIC_GUESS 82 | flags |= cv2.CALIB_FIX_FOCAL_LENGTH 83 | # flags |= cv2.CALIB_FIX_ASPECT_RATIO 84 | flags |= cv2.CALIB_ZERO_TANGENT_DIST 85 | # flags |= cv2.CALIB_RATIONAL_MODEL 86 | # flags |= cv2.CALIB_SAME_FOCAL_LENGTH 87 | # flags |= cv2.CALIB_FIX_K3 88 | # flags |= cv2.CALIB_FIX_K4 89 | # flags |= cv2.CALIB_FIX_K5 90 | 91 | stereocalib_criteria = (cv2.TERM_CRITERIA_MAX_ITER + 92 | cv2.TERM_CRITERIA_EPS, 100, 1e-5) 93 | ret, M1, d1, M2, d2, R, T, E, F = cv2.stereoCalibrate( 94 | self.objpoints, self.imgpoints_l, 95 | self.imgpoints_r, self.M1, self.d1, self.M2, 96 | self.d2, dims, 97 | criteria=stereocalib_criteria, flags=flags) 98 | 99 | print('Intrinsic_mtx_1', M1) 100 | print('dist_1', d1) 101 | print('Intrinsic_mtx_2', M2) 102 | print('dist_2', d2) 103 | print('R', R) 104 | print('T', T) 105 | print('E', E) 106 | print('F', F) 107 | 108 | # for i in range(len(self.r1)): 109 | # print("--- pose[", i+1, "] ---") 110 | # self.ext1, _ = cv2.Rodrigues(self.r1[i]) 111 | # self.ext2, _ = cv2.Rodrigues(self.r2[i]) 112 | # print('Ext1', self.ext1) 113 | # print('Ext2', self.ext2) 114 | 115 | print('') 116 | 117 | camera_model = dict([('M1', M1), ('M2', M2), ('dist1', d1), 118 | ('dist2', d2), ('rvecs1', self.r1), 119 | ('rvecs2', self.r2), ('R', R), ('T', T), 120 | ('E', E), ('F', F)]) 121 | 122 | cv2.destroyAllWindows() 123 | return camera_model 124 | 125 | if __name__ == '__main__': 126 | parser = argparse.ArgumentParser() 127 | parser.add_argument('filepath', help='String Filepath') 128 | args = parser.parse_args() 129 | cal_data = StereoCalibration(args.filepath) 130 | --------------------------------------------------------------------------------