Created
September 26, 2016 00:23
-
-
Save michaelchum/8bcd8605e4e6680df3e77d63038da260 to your computer and use it in GitHub Desktop.
Revisions
-
michaelchum created this gist
Sep 26, 2016 .There are no files selected for viewing
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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,254 @@ #Cam_calib_web.py #Created by Chris Rillahan #Last Updated: 02/02/2015 #Written with Python 2.7.2, OpenCV 3.0.0 and NumPy 1.8.0 #This program calculates the distortion parameters of a GoPro camera. #A video must first be taken of a chessboard pattern moved to a variety of positions #in the field of view with a GoPro. import cv2, sys import numpy as np #Import Information filename = 'GOPR0042.MP4' #Input the number of board images to use for calibration (recommended: ~20) n_boards = 20 #Input the number of squares on the board (width and height) board_w = 9 board_h = 6 #Board dimensions (typically in cm) board_dim = 25 #Image resolution image_size = (1920, 1080) #Crop mask # A value of 0 will crop out all the black pixels. This will result in a loss of some actual pixels. # A value of 1 will leave in all the pixels. This maybe useful if there is some important information # at the corners. Ideally, you will have to tweak this to see what works for you. crop = 0.5 #The ImageCollect function requires two input parameters. Filename is the name of the file #in which checkerboard images will be collected from. n_boards is the number of images of #the checkerboard which are needed. In the current writing of this function an additional 5 #images will be taken. This ensures that the processing step has the correct number of images #and can skip an image if the program has problems. #This function loads the video file into a data space called video. It then collects various #meta-data about the file for later inputs. The function then enters a loop in which it loops #through each image, displays the image and waits for a fixed amount of time before displaying #the next image. The playback speed can be adjusted in the waitKey command. During the loop #checkerboard images can be collected by pressing the spacebar. Each image will be saved as a #*.png into the directory which stores this file. The ESC key will terminate the function. #The function will end once the correct number of images are collected or the video ends. #For the processing step, try to collect all the images before the video ends. def ImageCollect(filename, n_boards): #Collect Calibration Images print('-----------------------------------------------------------------') print('Loading video...') #Load the file given to the function video = cv2.VideoCapture(0) video.set(3,2592) video.set(4,1944) #Checks to see if a the video was properly imported status = video.isOpened() if status == True: #Collect metadata about the file. FPS = video.get(cv2.cv.CV_CAP_PROP_FPS) FPS = 15.0 FrameDuration = 1/(FPS/1000) width = video.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH) height = video.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT) size = (int(width), int(height)) size = (2592, 1944) total_frames = video.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT) print size total_frames = 150 #Initializes the frame counter and collected_image counter current_frame = 0 collected_images = 0 #Video loop. Press spacebar to collect images. ESC terminates the function. while current_frame < total_frames: success, image = video.read() current_frame = video.get(cv2.cv.CV_CAP_PROP_POS_FRAMES) cv2.imshow('Video', image) k = cv2.waitKey(int(FrameDuration)) #You can change the playback speed here print k if collected_images == n_boards: break if k == 32: collected_images += 1 cv2.imwrite('/Users/michaelho/Downloads/calib/Calibration_Image' + str(collected_images) + '.png', image) print(str(collected_images) + ' images collected.') if k == 27: break #Clean up video.release() cv2.destroyAllWindows() else: print('Error: Could not load video') sys.exit() #The ImageProcessing function performs the calibration of the camera based on the images #collected during ImageCollect function. This function will look for the images in the folder #which contains this file. The function inputs are the number of boards which will be used for #calibration (n_boards), the number of squares on the checkerboard (board_w, board_h) as #determined by the inside points (i.e. where the black squares touch). board_dim is the actual #size of the square, this should be an integer. It is assumed that the checkerboard squares are #square. #This function first initializes a series of variables. Opts will store the true object points #(i.e. checkerboard points). Ipts will store the points as determined by the calibration images. #The function then loops through each image. Each image is converted to grayscale, and the #checkerboard corners are located. If it is successful at finding the correct number of corners #then the true points and the measured points are stored into opts and ipts, respectively. The #image with the checkerboard points are then displays. If the points are not found that image #is skipped. Once the desired number of checkerboard points are acquired the calibration #parameters (intrinsic matrix and distortion coefficients) are calculated. #The distortion parameter are saved into a numpy file (calibration_data.npz). The total #total reprojection error is calculated by comparing the "true" checkerboard points to the #image measured points once the image is undistorted. The total reprojection error should be #close to zero. #Finally the function will go through the calbration images and display the undistorted image. def ImageProcessing(n_boards, board_w, board_h, board_dim): # #Initializing variables # board_n = board_w * board_h # opts = [] # ipts = [] # npts = np.zeros((n_boards, 1), np.int32) # intrinsic_matrix = np.zeros((3, 3), np.float32) # distCoeffs = np.zeros((5, 1), np.float32) # criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.1) # # prepare object points based on the actual dimensions of the calibration board # # like (0,0,0), (25,0,0), (50,0,0) ....,(200,125,0) # objp = np.zeros((board_h*board_w,3), np.float32) # objp[:,:2] = np.mgrid[0:(board_w*board_dim):board_dim,0:(board_h*board_dim):board_dim].T.reshape(-1,2) # #Loop through the images. Find checkerboard corners and save the data to ipts. # for i in range(1, n_boards + 1): # #Loading images # print 'Loading... Calibration_Image' + str(i) + '.png' # image = cv2.imread('/Users/michaelho/Downloads/calib/Calibration_Image' + str(i) + '.png') # #Converting to grayscale # grey_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) # #Find chessboard corners # found, corners = cv2.findChessboardCorners(grey_image, (board_w,board_h),flags=(cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE)) # print (found) # if found == True: # #Add the "true" checkerboard corners # opts.append(objp) # #Improve the accuracy of the checkerboard corners found in the image and save them to the ipts variable. # cv2.cornerSubPix(grey_image, corners, (20, 20), (-1, -1), criteria) # ipts.append(corners) # #Draw chessboard corners # cv2.drawChessboardCorners(image, (board_w, board_h), corners, found) # #Show the image with the chessboard corners overlaid. # cv2.imshow("Corners", image) # char = cv2.waitKey(0) # cv2.destroyWindow("Corners") # print '' # print 'Finished processes images.' # #Calibrate the camera # print 'Running Calibrations...' # print(' ') # ret, intrinsic_matrix, distCoeff, rvecs, tvecs = cv2.calibrateCamera(opts, ipts, grey_image.shape[::-1],None,None) # #Save matrices # print('Intrinsic Matrix: ') # print(str(intrinsic_matrix)) # print(' ') # print('Distortion Coefficients: ') # print(str(distCoeff)) # print(' ') # #Save data # print 'Saving data file...' # np.savez('calibration_data', distCoeff=distCoeff, intrinsic_matrix=intrinsic_matrix) # print 'Calibration complete' file = np.load('calibration_data.npz') distCoeff = file['distCoeff'] intrinsic_matrix = file['intrinsic_matrix'] #Calculate the total reprojection error. The closer to zero the better. # tot_error = 0 # for i in xrange(len(opts)): # imgpoints2, _ = cv2.projectPoints(opts[i], rvecs[i], tvecs[i], intrinsic_matrix, distCoeff) # error = cv2.norm(ipts[i],imgpoints2, cv2.NORM_L2)/len(imgpoints2) # tot_error += error # print "total reprojection error: ", tot_error/len(opts) #Undistort Images #Scale the images and create a rectification map. newMat, ROI = cv2.getOptimalNewCameraMatrix(intrinsic_matrix, distCoeff, image_size, alpha = crop, centerPrincipalPoint = 1) mapx, mapy = cv2.initUndistortRectifyMap(intrinsic_matrix, distCoeff, None, newMat, image_size, m1type = cv2.CV_32FC1) for i in range(1, n_boards + 1): #Loading images print 'Loading... Calibration_Image' + str(i) + '.png' image = cv2.imread('Calibration_Image' + str(i) + '.png') # undistort dst = cv2.remap(image, mapx, mapy, cv2.INTER_LINEAR) cv2.imshow('Undisorted Image',dst) # write cv2.imwrite('/Users/michaelho/Downloads/calib/Calibration_Image_Undistorted' + str(i) + '.png', dst) char = cv2.waitKey(0) cv2.destroyAllWindows() print("Starting camera calibration....") print("Step 1: Image Collection") print("We will playback the calibration video. Press the spacebar to save") print("calibration images.") print(" ") print('We will collect ' + str(n_boards) + ' calibration images.') # ImageCollect(filename, n_boards) print(' ') print('All the calibration images are collected.') print('------------------------------------------------------------------------') print('Step 2: Calibration') print('We will analyze the images take and calibrate the camera.') print('Press the esc button to close the image windows as they appear.') print(' ') ImageProcessing(n_boards, board_w, board_h, board_dim)