python-3.xopencvraspberry-picamera-calibration

Camera calibration with circular pattern


I'm following this tutorial to calibrate my camera (with some lens) on Raspberry Pi, but using a circular pattern 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 (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. 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 other one more enter image description here

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


Solution

  • 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.