|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +""" |
| 4 | +From https://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_calib3d/py_calibration/py_calibration.html#calibration |
| 5 | +
|
| 6 | +Calling: |
| 7 | +cameracalib.py <folder> <image type> <num rows> <num cols> <cell dimension> |
| 8 | +
|
| 9 | +like cameracalib.py folder_name png |
| 10 | +
|
| 11 | +--h for help |
| 12 | +""" |
| 13 | + |
| 14 | +import numpy as np |
| 15 | +import cv2 |
| 16 | +import glob |
| 17 | +import sys |
| 18 | +import argparse |
| 19 | + |
| 20 | +#---------------------- SET THE PARAMETERS |
| 21 | +nRows = 8 |
| 22 | +nCols = 8 |
| 23 | +dimension = 20 #- mm |
| 24 | + |
| 25 | + |
| 26 | +workingFolder = "./camera_01" |
| 27 | +imageType = 'jpg' |
| 28 | +#------------------------------------------ |
| 29 | + |
| 30 | +# termination criteria |
| 31 | +criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, dimension, 0.001) |
| 32 | + |
| 33 | +# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0) |
| 34 | +objp = np.zeros((nRows*nCols,3), np.float32) |
| 35 | +objp[:,:2] = np.mgrid[0:nCols,0:nRows].T.reshape(-1,2) |
| 36 | + |
| 37 | +# Arrays to store object points and image points from all the images. |
| 38 | +objpoints = [] # 3d point in real world space |
| 39 | +imgpoints = [] # 2d points in image plane. |
| 40 | + |
| 41 | +if len(sys.argv) < 6: |
| 42 | + print("\n Not enough inputs are provided. Using the default values.\n\n" \ |
| 43 | + " type -h for help") |
| 44 | +else: |
| 45 | + workingFolder = sys.argv[1] |
| 46 | + imageType = sys.argv[2] |
| 47 | + nRows = int(sys.argv[3]) |
| 48 | + nCols = int(sys.argv[4]) |
| 49 | + dimension = float(sys.argv[5]) |
| 50 | + |
| 51 | +if '-h' in sys.argv or '--h' in sys.argv: |
| 52 | + print("\n IMAGE CALIBRATION GIVEN A SET OF IMAGES") |
| 53 | + print(" call: python cameracalib.py <folder> <image type> <num rows (9)> <num cols (6)> <cell dimension (25)>") |
| 54 | + print("\n The script will look for every image in the provided folder and will show the pattern found." \ |
| 55 | + " User can skip the image pressing ESC or accepting the image with RETURN. " \ |
| 56 | + " At the end the end the following files are created:" \ |
| 57 | + " - cameraDistortion.txt" \ |
| 58 | + " - cameraMatrix.txt \n\n") |
| 59 | + |
| 60 | + sys.exit() |
| 61 | + |
| 62 | +# Find the images files |
| 63 | +filename = workingFolder + "/*." + imageType |
| 64 | +images = glob.glob(filename) |
| 65 | + |
| 66 | +print(len(images)) |
| 67 | +if len(images) < 9: |
| 68 | + print("Not enough images were found: at least 9 shall be provided!!!") |
| 69 | + sys.exit() |
| 70 | + |
| 71 | + |
| 72 | + |
| 73 | +else: |
| 74 | + nPatternFound = 0 |
| 75 | + imgNotGood = images[1] |
| 76 | + |
| 77 | + for fname in images: |
| 78 | + if 'calibresult' in fname: continue |
| 79 | + #-- Read the file and convert in greyscale |
| 80 | + img = cv2.imread(fname) |
| 81 | + gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) |
| 82 | + |
| 83 | + print("Reading image ", fname) |
| 84 | + |
| 85 | + # Find the chess board corners |
| 86 | + ret, corners = cv2.findChessboardCorners(gray, (nCols,nRows),None) |
| 87 | + |
| 88 | + # If found, add object points, image points (after refining them) |
| 89 | + if ret == True: |
| 90 | + print("Pattern found! Press ESC to skip or ENTER to accept") |
| 91 | + #--- Sometimes, Harris cornes fails with crappy pictures, so |
| 92 | + corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria) |
| 93 | + |
| 94 | + # Draw and display the corners |
| 95 | + cv2.drawChessboardCorners(img, (nCols,nRows), corners2,ret) |
| 96 | + cv2.imshow('img',img) |
| 97 | + # cv2.waitKey(0) |
| 98 | + k = cv2.waitKey(0) & 0xFF |
| 99 | + if k == 27: #-- ESC Button |
| 100 | + print("Image Skipped") |
| 101 | + imgNotGood = fname |
| 102 | + continue |
| 103 | + |
| 104 | + print("Image accepted") |
| 105 | + nPatternFound += 1 |
| 106 | + objpoints.append(objp) |
| 107 | + imgpoints.append(corners2) |
| 108 | + |
| 109 | + # cv2.waitKey(0) |
| 110 | + else: |
| 111 | + imgNotGood = fname |
| 112 | + |
| 113 | + |
| 114 | +cv2.destroyAllWindows() |
| 115 | + |
| 116 | +if (nPatternFound > 1): |
| 117 | + print("Found %d good images" % (nPatternFound)) |
| 118 | + ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None) |
| 119 | + |
| 120 | + # Undistort an image |
| 121 | + img = cv2.imread(imgNotGood) |
| 122 | + h, w = img.shape[:2] |
| 123 | + print("Image to undistort: ", imgNotGood) |
| 124 | + newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h)) |
| 125 | + |
| 126 | + # undistort |
| 127 | + mapx,mapy = cv2.initUndistortRectifyMap(mtx,dist,None,newcameramtx,(w,h),5) |
| 128 | + dst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR) |
| 129 | + |
| 130 | + # crop the image |
| 131 | + x,y,w,h = roi |
| 132 | + dst = dst[y:y+h, x:x+w] |
| 133 | + print("ROI: ", x, y, w, h) |
| 134 | + |
| 135 | + cv2.imwrite(workingFolder + "/calibresult.png",dst) |
| 136 | + print("Calibrated picture saved as calibresult.png") |
| 137 | + print("Calibration Matrix: ") |
| 138 | + print(mtx) |
| 139 | + print("Disortion: ", dist) |
| 140 | + |
| 141 | + #--------- Save result |
| 142 | + filename = workingFolder + "/cameraMatrix.txt" |
| 143 | + np.savetxt(filename, mtx, delimiter=',') |
| 144 | + filename = workingFolder + "/cameraDistortion.txt" |
| 145 | + np.savetxt(filename, dist, delimiter=',') |
| 146 | + |
| 147 | + mean_error = 0 |
| 148 | + for i in xrange(len(objpoints)): |
| 149 | + imgpoints2, _ = cv2.projectPoints(objpoints[i], rvecs[i], tvecs[i], mtx, dist) |
| 150 | + error = cv2.norm(imgpoints[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2) |
| 151 | + mean_error += error |
| 152 | + |
| 153 | + print("total error: ", mean_error/len(objpoints)) |
| 154 | + |
| 155 | +else: |
| 156 | + print("In order to calibrate you need at least 9 good pictures... try again") |
| 157 | + |
| 158 | + |
| 159 | + |
| 160 | + |
| 161 | + |
0 commit comments