Pose estimation values are offset from camera frame centre

Hi, I’m setting up pose estimation using the aruco module and I’m finding that when I allign the aruco marker centre with the centre of the camera viewport, the tvec X and Y values are not at [0, 0].

Wondering if anyone here has seen similar offsets or has a hunch for why it might be happening.

tvec X at 0: (the purple circle is at [0, 0] on the video frame)

tvec Y at 0:

Apologies for the large code chunks, I’m sharing my camera calibration and pose estimation code in case it’s helpful to see.

# camera calibration
def calibrate():
    criteria = (cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS, 30, 0.001) # termination criteria
    chessboardSquareDimensions = 0.024 # metres
    chessboardDimensions = (6, 9)
    patternPoints = np.zeros((chessboardDimensions[0] * chessboardDimensions[1], 3), np.float32)
    patternPoints[:,:2] = np.mgrid[0:chessboardDimensions[0], 0:chessboardDimensions[1]].T.reshape(-1, 2)
    patternPoints = patternPoints * chessboardSquareDimensions
    objpoints = [] # 3d point in real world space
    imgpoints = [] # 2d points in image place

    images = glob.glob('calibration_images_laptop_cam/*.png')

    for image in images:
        print("next image ")            
        img = cv2.imread(image)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        size = gray.shape[::-1]

        ret, corners = cv2.findChessboardCorners(gray, chessboardDimensions, None)

        if ret == True:
            corners2 = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
            cv2.drawChessboardCorners(img, chessboardDimensions, corners2, ret)
            cv2.imshow('img', img)
            key = cv2.waitKey(0)

            if key == ord("q"):
                raise SystemExit

    return cv2.calibrateCamera(objpoints, imgpoints, size, None, None)

# pose estimation
arucoSquareDimensions = 0.1 # marker is 10 cm
arucoDict =cv2.aruco.Dictionary_get(cv2.aruco.DICT_4X4_100)
arucoParams = cv2.aruco.DetectorParameters_create()

ret, cameraMatrix, distortionCoefficients, camrvecs, camtvecs = calibrateCamera.calibrate() 

print("Sarting video stream..")
capture = cv2.VideoCapture(0)

while True:
    ret, frame = capture.read()
    (corners, ids, rejected) = cv2.aruco.detectMarkers(frame, arucoDict, parameters=arucoParams)
    if len(corners) > 0:

        ids = ids.flatten()

        rvecs, tvecs, markerPoints  = cv2.aruco.estimatePoseSingleMarkers(corners, 0.1, cameraMatrix, distortionCoefficients)

Hi, to update I just re-shot the calibration images (with the checkerboard)

The results are much better

tvec X value

tvec Y value

I’m not sure why the previous set of images produced a bad result as I tried to include variation across the axes. Perhaps not enough, or bad lighting. Still not completely perfect here, should I be expecting perfect accuracy?