calibrate.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. #!/usr/bin/env python
  2. '''
  3. camera calibration for distorted images with chess board samples
  4. reads distorted images, calculates the calibration and write undistorted images
  5. usage:
  6. calibrate.py [--debug <output path>] [--square_size] [<image mask>]
  7. default values:
  8. --debug: ./output/
  9. --square_size: 1.0
  10. <image mask> defaults to ../data/left*.jpg
  11. '''
  12. # Python 2/3 compatibility
  13. from __future__ import print_function
  14. import numpy as np
  15. import cv2 as cv
  16. # local modules
  17. from common import splitfn
  18. # built-in modules
  19. import os
  20. def main():
  21. import sys
  22. import getopt
  23. from glob import glob
  24. args, img_mask = getopt.getopt(sys.argv[1:], '', ['debug=', 'square_size=', 'threads='])
  25. args = dict(args)
  26. args.setdefault('--debug', './output/')
  27. args.setdefault('--square_size', 1.0)
  28. args.setdefault('--threads', 4)
  29. if not img_mask:
  30. img_mask = '../data/left??.jpg' # default
  31. else:
  32. img_mask = img_mask[0]
  33. img_names = glob(img_mask)
  34. debug_dir = args.get('--debug')
  35. if debug_dir and not os.path.isdir(debug_dir):
  36. os.mkdir(debug_dir)
  37. square_size = float(args.get('--square_size'))
  38. pattern_size = (9, 6)
  39. pattern_points = np.zeros((np.prod(pattern_size), 3), np.float32)
  40. pattern_points[:, :2] = np.indices(pattern_size).T.reshape(-1, 2)
  41. pattern_points *= square_size
  42. obj_points = []
  43. img_points = []
  44. h, w = cv.imread(img_names[0], cv.IMREAD_GRAYSCALE).shape[:2] # TODO: use imquery call to retrieve results
  45. def processImage(fn):
  46. print('processing %s... ' % fn)
  47. img = cv.imread(fn, 0)
  48. if img is None:
  49. print("Failed to load", fn)
  50. return None
  51. assert w == img.shape[1] and h == img.shape[0], ("size: %d x %d ... " % (img.shape[1], img.shape[0]))
  52. found, corners = cv.findChessboardCorners(img, pattern_size)
  53. if found:
  54. term = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_COUNT, 30, 0.1)
  55. cv.cornerSubPix(img, corners, (5, 5), (-1, -1), term)
  56. if debug_dir:
  57. vis = cv.cvtColor(img, cv.COLOR_GRAY2BGR)
  58. cv.drawChessboardCorners(vis, pattern_size, corners, found)
  59. _path, name, _ext = splitfn(fn)
  60. outfile = os.path.join(debug_dir, name + '_chess.png')
  61. cv.imwrite(outfile, vis)
  62. if not found:
  63. print('chessboard not found')
  64. return None
  65. print(' %s... OK' % fn)
  66. return (corners.reshape(-1, 2), pattern_points)
  67. threads_num = int(args.get('--threads'))
  68. if threads_num <= 1:
  69. chessboards = [processImage(fn) for fn in img_names]
  70. else:
  71. print("Run with %d threads..." % threads_num)
  72. from multiprocessing.dummy import Pool as ThreadPool
  73. pool = ThreadPool(threads_num)
  74. chessboards = pool.map(processImage, img_names)
  75. chessboards = [x for x in chessboards if x is not None]
  76. for (corners, pattern_points) in chessboards:
  77. img_points.append(corners)
  78. obj_points.append(pattern_points)
  79. # calculate camera distortion
  80. rms, camera_matrix, dist_coefs, _rvecs, _tvecs = cv.calibrateCamera(obj_points, img_points, (w, h), None, None)
  81. print("\nRMS:", rms)
  82. print("camera matrix:\n", camera_matrix)
  83. print("distortion coefficients: ", dist_coefs.ravel())
  84. # undistort the image with the calibration
  85. print('')
  86. for fn in img_names if debug_dir else []:
  87. _path, name, _ext = splitfn(fn)
  88. img_found = os.path.join(debug_dir, name + '_chess.png')
  89. outfile = os.path.join(debug_dir, name + '_undistorted.png')
  90. img = cv.imread(img_found)
  91. if img is None:
  92. continue
  93. h, w = img.shape[:2]
  94. newcameramtx, roi = cv.getOptimalNewCameraMatrix(camera_matrix, dist_coefs, (w, h), 1, (w, h))
  95. dst = cv.undistort(img, camera_matrix, dist_coefs, None, newcameramtx)
  96. # crop and save the image
  97. x, y, w, h = roi
  98. dst = dst[y:y+h, x:x+w]
  99. print('Undistorted image written to: %s' % outfile)
  100. cv.imwrite(outfile, dst)
  101. print('Done')
  102. if __name__ == '__main__':
  103. print(__doc__)
  104. main()
  105. cv.destroyAllWindows()