Отрисовка контрольной рамки шахматной доски для оценки позы с использованием OpenCV (solvePnP + projectPoints) - PullRequest
1 голос
/ 12 апреля 2020

Я следовал учебному пособию на https://docs.opencv.org/master/d7/d53/tutorial_py_pose.html на основании данных калибровки, полученных после учебного пособия на https://docs.opencv.org/master/dc/dbb/tutorial_py_calibration.html.

Конечная цель - получить позу шахматной доски относительно камеры, но сначала я пытаюсь нарисовать опорную рамку шахматной доски.

Входные данные представляют собой набор из 2 снимков, которые я сделал с веб-канала, направленного на печатную шахматную доску 10x7.

Кажется, что калибровка прошла успешно: enter image description here enter image description here Но вывод совершенно неверный: enter image description here enter image description here Вот исправленный код:

import cv2 as cv
import numpy as np
import glob
import argparse

# algorithm parameters
CHECKERBOARD_WIDTH = 9
CHECKERBOARD_HEIGHT = 6


# termination criteria
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)

#=== CALIBRATE CAMERA ============================================================================

#Prepare object points
objp = np.zeros((CHECKERBOARD_HEIGHT * CHECKERBOARD_WIDTH, 3), np.float32)
objp[:,:2] = np.mgrid[0:CHECKERBOARD_HEIGHT, 0:CHECKERBOARD_WIDTH].T.reshape(-1,2)

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

# Load the images
ap = argparse.ArgumentParser()
ap.add_argument('-f', '--folder', required=True,  help='Path to the images folder with last slash')
ap.add_argument('-e', '--ext', required=True,  help='Extension of image files without the dot')
args = vars(ap.parse_args())

images = glob.glob(args['folder']+'*.'+args['ext'])

#Process the images
for fname in images:
    print('Calibrating on '+fname)
    img = cv.imread(fname)
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    # Find the chess board corners
    ret, corners = cv.findChessboardCorners(gray, (CHECKERBOARD_WIDTH,CHECKERBOARD_HEIGHT), None)
    # If found, add object points, image points (after refining them)
    if ret == True:
        print('Found corners')
        objpoints.append(objp)
        corners2 = cv.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria)
        imgpoints.append(corners)
        # Draw and display the corners as feedback to the user
        cv.drawChessboardCorners(img, (CHECKERBOARD_WIDTH,CHECKERBOARD_HEIGHT), corners2, ret)
        cv.imshow('Calibration', img)
        k = cv.waitKey(0) & 0xFF
        if k == ord('s'):
            cv.imwrite(fname+'_calib.png', img)

cv.destroyAllWindows()

#Obtain camera parameters
ret, mtx, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)

#=== FIND POSE OF TARGETS ===========================================================================

#Prepare object points
objp = np.zeros((CHECKERBOARD_HEIGHT * CHECKERBOARD_WIDTH, 3), np.float32)
objp[:,:2] = np.mgrid[0:CHECKERBOARD_HEIGHT, 0:CHECKERBOARD_WIDTH].T.reshape(-1,2)

axis = np.float32([[3,0,0], [0,3,0], [0,0,-3]]).reshape(-1,3)

#Display 
def draw(img, corners, imgpts):
    corner = tuple(corners[0].ravel())
    img = cv.line(img, corner, tuple(imgpts[0].ravel()), (255,0,0), 5)
    img = cv.line(img, corner, tuple(imgpts[1].ravel()), (0,255,0), 5)
    img = cv.line(img, corner, tuple(imgpts[2].ravel()), (0,0,255), 5)
    return img

for fname in images:
    print('Processing '+fname)
    img = cv.imread(fname)
    gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
    ret, corners = cv.findChessboardCorners(gray, (CHECKERBOARD_WIDTH,CHECKERBOARD_HEIGHT), None)
    if ret == True:
        print('Found corners')
        corners2 = cv.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
        # Find the rotation and translation vectors.
        ret,rvecs, tvecs = cv.solvePnP(objp, corners2, mtx, dist)
        # project 3D points to image plane
        imgpts, jac = cv.projectPoints(axis, rvecs, tvecs, mtx, dist)
        img = draw(img,corners2,imgpts)
        cv.imshow('img',img)
        k = cv.waitKey(0) & 0xFF
        if k == ord('s'):
            cv.imwrite(fname+'_output.png', img)

cv.destroyAllWindows()

Вызывается, например, с помощью:

python3 test_cvpnp.py --folder ./images/ --ext png

Изображения для калибровки и обработки совпадают, что должно привести к хорошие результаты. Что происходит?

Исходные изображения выглядят следующим образом, на случай, если вы захотите воспроизвести поведение: enter image description here enter image description here

Редактировать: Я тестировал, открывая мою веб-камеру покадрово и обрабатывая 30-40 кадров (при 2 FPS) в разных ориентациях вместо 2, чтобы получить больше данных, но опорные кадры все еще нарисован совершенно неправильно и среднеквадратическая ошибка калибровки составляет ~ 100, по-видимому, . Улучшенный код можно найти здесь . Что не так?

Ответы [ 2 ]

1 голос
/ 14 апреля 2020

Получение среднеквадратичной ошибки в 100 пикселей означает, что ваша калибровка очень неправильная.

Ваши изображения показывают не очень плоскую цель и некоторые размытия.

Предлагаем вам следовать этим рекомендациям .

0 голосов
/ 14 апреля 2020

Мне потребовалось много-много времени, чтобы сравнить мой код с примером как-то отформатированным таким же образом, чтобы, наконец, заметить, что при вызове findChessboardCorners () я поменял местами ширину и высоту ... Проблема решена, все работает как и ожидалось, с среднеквадратической ошибкой от 0,2 до 0,6. Положение цели очень точно отслеживается, поэтому я могу подтвердить, что нам не нужны дорогие цели для получения субпиксельной точности RMS.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...