Camera calibration with circular pattern
Question: I'm following [this tutorial](http://opencv-python-
tutroals.readthedocs.io/en/latest/py_tutorials/py_calib3d/py_calibration/py_calibration.html#calibration)
to calibrate my camera (with some lens) on Raspberry Pi, but using a [circular
pattern](http://nerian.com/support/resources/patterns/) instead of the
chessboard one. The problem is that the resulting undistorted image is
shrinked and not full, and when I get every shrinking away from the code, it
looks like the right part of
[![this](http://i.stack.imgur.com/L9sgp.jpg)](http://i.stack.imgur.com/L9sgp.jpg)
(usually it's even worse). The question is if it is possible to do something
with the code so that it would give away a picture like [the example from the
tutorial](http://opencv-python-
tutroals.readthedocs.io/en/latest/_images/calib_result.jpg). Do I have to use
the chessboard pattern for this?

My code:

    
    
    import numpy as np
    import cv2
    from picamera.array import PiRGBArray
    from picamera import PiCamera
    import time
    
    
    
    # termination criteria
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
    
    # prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
    #objp = np.zeros((4*11,3), np.float32)
    #objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
    objp=np.array([[0,0,0],[1,0,0],[2,0,0],[3,0,0],[0.5,0.5,0],[1.5,0.5,0],[2.5,0.5,0],[3.5,0.5,0]])
    for y in range(2,11):
            for x in range(4):
                    objp=np.append(objp,[np.array([objp[4*(y-2)+x][0],objp[4*(y-2)+x][1]+1,0])],axis=0)
    
    # Arrays to store object points and image points from all the images.
    objpoints = [] # 3d point in real world space
    imgpoints = [] # 2d points in image plane.
    
    #images = glob.glob('pict*.jpg')
    
    # initialize the camera and grab a reference to the raw camera capture
    camera = PiCamera()
    camera.resolution = (640, 480)
    camera.framerate = 32
    rawCapture = PiRGBArray(camera, size=(640, 480))
    
    # allow the camera to warmup
    time.sleep(0.1)
    ret0=[]
    j=0
    for i,frame in enumerate(camera.capture_continuous(rawCapture, format="bgr", use_video_port=True)):
        image = frame.array
        img=image[::-1]
        gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    
        # Find the chess board corners
        ret, corners = cv2.findCirclesGrid(gray, (4,11),None,flags=cv2.CALIB_CB_ASYMMETRIC_GRID)
        # If found, add object points, image points (after refining them)
        if ret == True and np.sum(np.int32(ret0))<15 and not i%10:
            ret0.append(ret)
            print("{} more for proper calibration".format(15-np.sum(np.int32(ret0))))
            objpoints.append(objp.astype('float32'))
    
            corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
            imgpoints.append(corners2.reshape(-1, 2).astype('float32'))
    
            # Draw and display the corners
            img = cv2.drawChessboardCorners(img.copy(), (4,11), corners2,ret)
            cv2.imshow('img',img)
            cv2.waitKey(1000)
            cv2.imwrite('cal{}.jpg'.format(j),img)
            j+=1
            rawCapture.truncate(0)
        elif np.sum(np.int32(ret0))<15:
            cv2.imshow('img',img)
            cv2.waitKey(1)
            rawCapture.truncate(0)
        else:
            rawCapture.truncate(0)
            break
    
    dist = np.array([-0.13615181, 0.53005398, 0, 0, 0]) # no translation
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)
    
    h,  w = img.shape[:2]
    newcameramtx, roi=cv2.getOptimalNewCameraMatrix(mtx,dist,(w,h),1,(w,h))
    
    np.savetxt('newcameramtx.out',newcameramtx)
    np.savetxt('mtx.out',mtx)
    np.savetxt('dist.out',dist)
    
    #img=cv2.imread('pict1.jpg')
    # undistort
    dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
    
    # crop the image
    x,y,w,h = roi
    dst = dst[y:y+h, x:x+w]
    cv2.imwrite('calibresult.png',dst)
    
    cv2.imshow('undistorted',dst)
    cv2.waitKey(0)&0xFF
    
    cv2.destroyAllWindows()
    

UPD: I've tried calibration with the chessboard pattern, and the program
didn't even want to recognize the pattern! Here are examples with detected
circular pattern:
[![one](http://i.stack.imgur.com/BMxcg.jpg)](http://i.stack.imgur.com/BMxcg.jpg)
[![other](http://i.stack.imgur.com/x2L9b.jpg)](http://i.stack.imgur.com/x2L9b.jpg)
[![one
more](http://i.stack.imgur.com/tA3Mf.jpg)](http://i.stack.imgur.com/tA3Mf.jpg)
[![enter image description
here](http://i.stack.imgur.com/Sdm0A.jpg)](http://i.stack.imgur.com/Sdm0A.jpg)

So the program allegedly detects the circles, but not as ideally, as it would
be wanted.


Answer: You could try to tune the parameters of the blob detector. By the default the
`findCirclesGrid` uses the SimpleBlobDetector. So try to adust the parameters,
for example:

    
    
    params = cv2.SimpleBlobDetector_Params()
    params.minArea = 10;
    params.minDistBetweenBlobs = 5;
    detector = cv2.SimpleBlobDetector_create(params)
    

and then pass it to findCirclesGrid:

    
    
    cv2.findCirclesGrid(gray, (4,11),None,flags=cv2.CALIB_CB_ASYMMETRIC_GRID,detector)
    

Additionaly you can try to use `cv2.CALIB_CB_ASYMMETRIC_GRID +
cv2.CALIB_CB_CLUSTERING`

For more information about the SimpleBlobDetector and its parameters see [this
tutrial](https://www.learnopencv.com/blob-detection-using-opencv-python-c/).

