简体   繁体   中英

Root Mean Square for OpenCV Camera Calibration is too High

I am attempting to calibrate my single webcam using opencv cv2 in python. I am using the cv2.findChessboardCorners and cv2.calibrateCamera functions. However, my root mean square returned from the calibrateCamera function seems very high. It is always around 50 no matter how many frames with found boards are used. I read that good values range from 0-1. I am using a 5x8 black and white checker pattern on a piece of paper taped to a wooden board. Can anyone help me out with getting this lower? The weird thing is I used images I rendered from Blender, a 3d modeling software, in which there is no lens distortion and the board's coordinates are known for sure and I was able to get a RMS of 0.22 which is good. Using similar code though I cannot replicate those results with my webcam. Perhaps I am missing something. Thanks so much to everyone who takes a look at this. Here is the complete code:

import sys
import os
import numpy as np
import cv2
import time

'''
This module finds the intrinsic parameters of the camera. These parameters include
the focal length, pixel aspect ratio, image center, and lens distortion (see wiki
entry for "camera resectioning" for more detail). It is important to note that the
parameters found by this class are independent of location and rotation of the camera.
Thus, it only needs to be calculated once assuming the lens and focus of the camera is
unaltered. The location and rotation matrix are defined by the extrinsic parameters.
'''

class Find_Intrinsics:
    '''Finds the intrinsic parameters of the camera.'''
    def __init__(self):
        #Import user input from Blender in the form of argv's 
        self.rows = int(sys.argv[1])
        self.cols = int(sys.argv[2])
        self.board_width_pxls = float(sys.argv[3])
        self.pxls_per_sq_unit = float(sys.argv[4])
        self.printer_scale = float(sys.argv[5])


    def find_calib_grid_points(self,cols,rows,board_width_pxls,pxls_per_sq_unit,printer_scale):
        '''Defines the distance of the board squares from each other and scale them.

            The scaling is to correct for the scaling of the printer. Most printers
            cannot print all the way to the end of the page and thus scale images to
            fit the entire image. If the user does not desire to maintain real world
            scaling, then an arbitrary distance is set. The 3rd value appended to
            calib_points signifies the depth of the points and is always zero because
            they are planar. 
        '''
        #should be dist for each square
        point_dist = (((board_width_pxls)/(pxls_per_sq_unit))*printer_scale)/(cols+2)
        calib_points = []
        for i in range(0,cols):
            for j in range(0,rows):
                pointX = 0 + (point_dist*j)
                pointY = 0 - (point_dist*i)
                calib_points.append((pointX,pointY,0))    
        np_calib_points = np.array(calib_points,np.float32)
        return np_calib_points


    def main(self):
        print '---------------------------Finding Intrinsics----------------------------------'       
        np_calib_points = self.find_calib_grid_points(self.cols,self.rows,self.board_width_pxls,
                                                 self.pxls_per_sq_unit,self.printer_scale)
        pattern_size = (self.cols,self.rows)
        obj_points = []
        img_points = []        

        camera =  cv2.VideoCapture(0)
        found_count = 0
        while True:
            found_cam,img = camera.read()            
            h, w = img.shape[:2]
            print h,w
            gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
            found, corners = cv2.findChessboardCorners(img, pattern_size)            

            if found:            
                term = ( cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 30, 0.1 )
                cv2.cornerSubPix(gray_img, corners, (5, 5), (-1, -1), term)

                cv2.drawChessboardCorners(img, pattern_size, corners, found)
                found_count+=1

                img_points.append(corners.reshape(-1, 2))
                obj_points.append(np_calib_points)

            cv2.putText(img,'Boards found: '+str(found_count),(30,30), 
                cv2.FONT_HERSHEY_DUPLEX, 0.8,(0,0,255,1))
            cv2.putText(img,'Press any key when finished',(30,h-30), 
                cv2.FONT_HERSHEY_DUPLEX, 0.8,(0,0,255,1))
            cv2.imshow("webcam",img)

            if (cv2.waitKey (1000) != -1):
                cv2.destroyAllWindows()
                del(camera)
                np_obj_points = np.array(obj_points)
                print "Calibrating.Please be patient"
                start = time.clock()

                #OpenCV function to solve for camera matrix
                try:
                    print obj_points[0:10]
                    rms, camera_matrix, dist_coefs, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, (w, h))
                    print "RMS:", rms
                    print "camera matrix:\n", camera_matrix
                    print "distortion coefficients: ", dist_coefs

                    #Save the camera matrix and the distortion coefficients to the hard drive to use
                    #to find the extrinsics
                    #want to use same file directory as this file
                    #directory = os.path.dirname(os.path.realpath('Find_Intrinsics.py'))
                    np.save('C:\\Users\\Owner\\Desktop\\3D_Scanning\\Blender_3d_Scanning\\camera_matrix.npy',camera_matrix)
                    np.save('C:\\Users\\Owner\\Desktop\\3D_Scanning\\Blender_3d_Scanning\\dist_coefs.npy',dist_coefs)    

                    elapsed = (time.clock() - start)
                    print("Elapsed time: ", elapsed, " seconds") 

                    img_undistort = cv2.undistort(img,camera_matrix,dist_coefs)
                    cv2.namedWindow('Undistorted Image',cv2.WINDOW_NORMAL)#WINDOW_NORMAL used bc WINDOW_AUTOSIZE does not let you resize
                    cv2.resizeWindow('Undistorted Image',w,h)
                    cv2.imshow('Undistorted Image',img_undistort)
                    cv2.waitKey(0)
                    cv2.destroyAllWindows()

                    break

                except:
                    print "\nSorry, an error has occurred. Make sure more than zero boards are found."
                    break

if __name__ == '__main__' and len(sys.argv)== 6:
    Intrinsics = Find_Intrinsics()
    Intrinsics_main = Intrinsics.main()
else:
    print "Incorrect number of args found. Make sure that the python27 filepath is entered correctly."
print '--------------------------------------------------------------------------------'

Wow, I do not know what it is about having aha moments (or doh! moments if you watch The Simpsons) right after posting on stackoverflow haha. This one was a bone head mistake. I managed to mess up the construction of the obj_points parameter for the calibrateCamera function. OpenCV takes the first point to be the top left and goes through each row , left to right, until it comes to the last point (bottom right). As such, my find_calib_grid_points function was wrong. Here is the correct code just in case someone else gets tripped up on that, though that is probably unlikely:

for j in range(0,rows):
            for i in range(0,cols):
                pointX = 0 + (point_dist*i)
                pointY = 0 - (point_dist*j)
                calib_points.append((pointX,pointY,0))    
        np_calib_points = np.array(calib_points,np.float32)

Thanks to everyone who looked at this and sent me psychic messages so I could figure this one out right haha. They worked! My RMS was 0.33!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM