Created
July 5, 2020 05:54
-
-
Save mrbemani/b0d0581b6bb30d5a02700b783a46e29e to your computer and use it in GitHub Desktop.
calibrate mono camera using opencv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| __author__ = 'Shi Qi' | |
| import sys | |
| import os | |
| import argparse | |
| import json | |
| import numpy as np | |
| import cv2 | |
| from decimal import Decimal | |
| def str2bool(v): | |
| if v.lower() in ('yes', 'true', 't', 'y', '1'): | |
| return True | |
| elif v.lower() in ('no', 'false', 'f', 'n', '0'): | |
| return False | |
| else: | |
| raise argparse.ArgumentTypeError('Boolean value expected.') | |
| def deci(n): | |
| return '%.16e' % Decimal(n) | |
| def get_checkerboard_points(w, h, objp, images, imw=800, winname="Camera Checkerboard View"): | |
| # termination criteria | |
| criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001) | |
| # Arrays to store object points and image points from all the images. | |
| objpoints = [] # 3d point in real world space | |
| imgpoints = [] # camera 2d points in image plane. | |
| 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, (w,h), None) | |
| # If found, add object points, image points (after refining them) | |
| if ret is True: | |
| objpoints.append(objp) | |
| corners2=cv2.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria) | |
| imgpoints.append(corners) | |
| # Draw and display the corners | |
| cv2.drawChessboardCorners(img, (w,h), corners2, ret) | |
| ih, iw = gray.shape | |
| nw = imw | |
| nh = int(float(ih) / float(iw) * float(nw)) | |
| ims = cv2.resize(img, (nw, nh)) | |
| cv2.imshow(winname, ims) | |
| k = cv2.waitKey(100) | |
| if k == 27: | |
| break | |
| return objpoints, imgpoints | |
| def calibrate_mono_camera(cwh, camdir, outfile): | |
| w, h = cwh | |
| imgs = [] | |
| if os.path.isdir(camdir): | |
| imgs = [os.path.join(camdir, p) for p in os.listdir(camdir) if p.lower().endswith('.jpg') or p.lower().endswith('.png')] | |
| else: | |
| print ("[ERROR] camdir does not exist!") | |
| return (1, None) | |
| if len(imgs) < 2: | |
| print ("[ERROR] Images are empty.") | |
| return (2, None) | |
| imh, imw = cv2.imread(imgs[0]).shape[:2] | |
| imsize = (imw, imh) | |
| imgpoints = [] | |
| objpoints = [] | |
| objp = np.zeros((h*w,3), np.float32) | |
| objp[:,:2] = np.mgrid[0:w,0:h].T.reshape(-1,2) | |
| r = get_checkerboard_points(w, h, objp, imgs, 800) | |
| if r is not None: | |
| objpoints, imgpoints = r | |
| else: | |
| return (3, None) | |
| # camMatrix = np.zeros((3, 3), np.float32) | |
| # camMatrix[0,0] = 1.0 | |
| # camMatrix[1,1] = 1.0 | |
| camMatrix = cv2.initCameraMatrix2D(objpoints, imgpoints, imsize) | |
| print ("initial camMatrix: \n{}".format(camMatrix)) | |
| camDist = np.zeros(8, dtype=np.float32) | |
| retval, camMatrix, camDist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, imsize, camMatrix, camDist, flags=0) | |
| #retval, camMatrix, camDist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, imsize, camMatrix, camDist, flags=flags) | |
| print ("===") | |
| print ("retval: {}".format(retval)) | |
| print ("Intrinsics: {}".format(camMatrix)) | |
| print ("Distortion: {}".format(camDist)) | |
| print ("===") | |
| if outfile is not None: | |
| print ("Saving...") | |
| cv_file = cv2.FileStorage(outfile, cv2.FILE_STORAGE_WRITE) | |
| #cv_file.write("retval", retval) | |
| cv_file.write("Intrinsics", camMatrix) | |
| cv_file.write("Distortion", camDist) | |
| cv_file.release() | |
| print ("Done saving.") | |
| return (0, (retval, camMatrix, camDist, rvecs, tvecs)) | |
| def undistort_image(i_im, o_im, k, d): | |
| im = cv2.imread(i_im) | |
| ih, iw = im.shape[:2] | |
| # newk, roi = cv2.getOptimalNewCameraMatrix(k, d, (ih, iw), 1, (ih, iw)) | |
| # print (newk) | |
| # print (roi) | |
| # crop the image | |
| # x, y, w, h = roi | |
| # undistort | |
| dst = cv2.undistort(im, k, d, None, k) | |
| #dst = dst[y:y+h, x:x+w] | |
| cv2.imwrite(o_im, dst) | |
| def main(args): | |
| if type(args) is not argparse.Namespace: | |
| print ("[ERROR] Invalid Command-line parameters!") | |
| return 1 | |
| ret = 0 | |
| if args.mode == "calib": | |
| if args.checkerboard is None or len(args.checkerboard) != 2: | |
| print ("[ERROR] Invalid --checkerboard. Should be in the form: WxH") | |
| return 2 | |
| checkerboardWH = args.checkerboard | |
| cam_input_dir = os.path.abspath(args.camera_dir) | |
| cam_out_dir = os.path.abspath(args.output_params_dir) | |
| if not os.path.isdir(cam_input_dir): | |
| print ("Error: {} does not exist!".format(args.camera_dir)) | |
| return 1 | |
| if not os.path.isdir(cam_out_dir): | |
| os.mkdir(cam_out_dir) | |
| camdirs = [x for x in os.listdir(cam_input_dir) if os.path.isdir(os.path.join(cam_input_dir, x))] | |
| totalcams = len(camdirs) | |
| donecams = 0 | |
| cams = [] | |
| for camdir in camdirs: | |
| print ("[{} / {}] Calibrating: {} ...".format(donecams+1, totalcams, camdir)) | |
| r, cam = calibrate_mono_camera(checkerboardWH, os.path.join(cam_input_dir, camdir), os.path.join(cam_out_dir, "{}.xml".format(camdir))) | |
| if r != 0: | |
| print ("Failed: {}".format(camdir)) | |
| else: | |
| donecams += 1 | |
| cams.append(cam) | |
| print ("\nCameras calibrated: {} in {}.\n".format(donecams, totalcams)) | |
| elif args.mode == "undistort": | |
| imgdir = os.path.abspath(args.images_dir) | |
| unddir = os.path.abspath(args.undistorted_dir) | |
| camfile = os.path.abspath(args.input_camera_params_file) | |
| k = None | |
| d = None | |
| if not os.path.isfile(camfile): | |
| print ("Error: input camera parameters file is invalid.") | |
| return 1 | |
| else: | |
| k_file = cv2.FileStorage(camfile, cv2.FILE_STORAGE_READ) | |
| k = k_file.getNode("Intrinsics").mat() | |
| d = k_file.getNode("Distortion").mat() | |
| if not os.path.isdir(imgdir): | |
| print ("Error: input images_dir is invalid.") | |
| return 2 | |
| if not os.path.isdir(unddir): | |
| os.mkdir(unddir) | |
| imgs = [p for p in os.listdir(imgdir) if p.lower().endswith('.jpg') or p.lower().endswith('.png')] | |
| total_img = len(imgs) | |
| if total_img < 1: | |
| print("No Image for undistorting.") | |
| return 3 | |
| und_img = 0 | |
| for im in imgs: | |
| opimg = os.path.join(imgdir, im) | |
| print ("[{} / {}] Undistorting Image: {} ...".format(und_img+1, total_img, opimg)) | |
| undistort_image(opimg, os.path.join(unddir, im), k, d) | |
| und_img += 1 | |
| print ("\nDone!\nImages undistorted: {} in {}.\n".format(und_img, total_img)) | |
| else: | |
| print ("Error: Invalid --mode. only calib and undistort are implemented.") | |
| return 2 | |
| return ret | |
| if __name__ == '__main__': | |
| parser = argparse.ArgumentParser(description='Monno-Camera Calibration.') | |
| parser.add_argument('-b', '--checkerboard', type=lambda s: [int(x) for x in s.split('x')], help="checkerboard resolution. in the form: WxH") | |
| parser.add_argument('-c', '--camera_dir', type=str, help="camera image directory. it should contain camera subdirs which themselves contain JPEGs or PNGs.") | |
| parser.add_argument('-o', '--output_params_dir', type=str, help="output directory to store camera parameters.") | |
| parser.add_argument('-m', '--mode', type=str, default="calib", help="script mode, calib | undistort. default: calib") | |
| parser.add_argument('-k', '--input_camera_params_file', type=str, help="only for undistort. path to camera parameters XML file.") | |
| parser.add_argument('-i', '--images_dir', type=str, help="only for undistort. input images to undistort", ) | |
| parser.add_argument('-u', '--undistorted_dir', type=str, help="only for undistort. output undistorted images", default="undistorted") | |
| args = parser.parse_args() | |
| print ("\n=== Start ===\n") | |
| ret = main(args) | |
| if ret == 0: | |
| print ("\n=== Done ===\n") | |
| else: | |
| print ("\n[ERROR] Program ended unexpectedly! Errno: {}\n".format(ret)) | |
| print ("\n=== Failed ===\n") | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment