ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [OpenCV] Pose Estimation
    Image Geometry 2018. 2. 1. 18:56

    자세 추정(pose estimation)


    목표

    이미지에서 3D 효과를 생성하기위해 calib3d 모듈을 활용하는 방법을 배웁니다.


    기본이론

    이것은 작은 섹션이 될 것입니다. 카메라 캘리브레이션(camera calibration)에 대한 지난 섹션에서, 카메라 행렬(camera matrix), 왜곡계수(distortion coefficients)를 찾았습니다. 패턴 이미지가 주어지면, 이 정보를 이용하여 카메라의 자세, 또는 공간에서 객체가 어떻게 위치해 있는지, 어떻게 회전되었는지, 어떻게 옮겨지는지등을 계산할 수 있습니다. 평면 객체에 대해 Z=0으로 가정하면, 문제는 보기 위해서 패턴 이미지가 어떻게 공간에 배치 되는가 입니다.

    따라서, 만약 공간내에 객체가 어떻게 있는지 안다면, 우리는 3D 효과를 시뮬레이션하기위해 2D 다이어그램을 그것에 그릴 수 있습니다. 

    어떻게 진행하는지 살펴봅시다.



    문제는, 체스보드의 첫번째 코너에 3D 좌표 축(X, Y, Z 축)을 그려야 한다는 것입니다. X축은 파란색, Y축은 초록색, Z축은 빨간색입니다. Z축은 체스보드 평면에 수직인것 처럼 느껴져야 합니다.


    먼저, 카메라 행렬과 왜곡 계수를 이전 캘리브레이션 결과에서 읽어옵니다.


    import cv2
    import numpy as np
    import glob
    
    # Load previously saved data
    with np.load('B.npz') as X:
        mtx, dist, _, _ = [X[i] for i in ('mtx','dist','rvecs','tvecs')]


    3D 축을 그리기위해 axis points 와 체스보드에서 모서리를 획득하는(cv2.findChessboardCorners()를 사용하여 얻어진)  draw 함수를 만듭니다.


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


    그 후, 이전의 경우처럼, 우리는 종료 조건, object points(체스보드 모서리의 3D 지점), axis points를 생성합니다. axis point는 축을 그리기 위한 3D 공간의 지점이다. 우리는 길이3의 축을 그린다.(단위는 우리가 사이즈에 기반하여 캘리브레이션한 체스 정사각형의 항이다). 

    따라서, 우리의 X축은 (0, 0, 0)에서 (3, 0, 0)으로 그려지고, Y축도 마찬가지입니다. Z축은 (0, 0, 0)에서 (0, 0, -3)으로 그려진다. 음수는 카메라 방향으로 그려지는 것을 의미한다. 


    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
    objp = np.zeros((6*7,3), np.float32)
    objp[:,:2] = np.mgrid[0:7,0:6].T.reshape(-1,2)
    
    axis = np.float32([[3,0,0], [0,3,0], [0,0,-3]]).reshape(-1,3)


    이제 평상시처럼 각 이미지를 읽어들이고, 7 x 6 격자를 찾습니다. 찾아지면 서브코너 픽셀로 수정합니다. 그리고 회전과 이동을 계산하기위해, 우리는 cv2.solvePnPRansac() 함수를 사용합니다. 


    간단히 말하면, 우리는 3D 공간에서 (3, 0, 0), (0, 3, 0), (0, 0, 3)각각에 대응하는 이미지평면상의 지점을 찾습니다. 그것들을 얻으면, 우리는 첫번째 모서리에서 이러한 점들의 각 지점으로 선을 그립니다. draw() 함수를 사용하여. 완료되었습니다.


    for fname in glob.glob('left*.jpg'):
        img = cv2.imread(fname)
        gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
        ret, corners = cv2.findChessboardCorners(gray, (7,6),None)
    
        if ret == True:
            corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
    
            # Find the rotation and translation vectors.
            rvecs, tvecs, inliers = cv2.solvePnPRansac(objp, corners2, mtx, dist)
    
            # project 3D points to image plane
            imgpts, jac = cv2.projectPoints(axis, rvecs, tvecs, mtx, dist)
    
            img = draw(img,corners2,imgpts)
            cv2.imshow('img',img)
            k = cv2.waitKey(0) & 0xff
            if k == 's':
                cv2.imwrite(fname[:6]+'.png', img)
    
    cv2.destroyAllWindows()


    결과를 아래에서 보여줍니다. 각 축이 3 정사각형 길이임을 보세요.




    큐브 렌더

    만약 큐브를 그리기 원한다면, draw()함수와 축 포인트를 아래와 같이 수정해야합니다.

    수정된 draw() 함수:


    def draw(img, corners, imgpts):
        imgpts = np.int32(imgpts).reshape(-1,2)
    
        # draw ground floor in green
        img = cv2.drawContours(img, [imgpts[:4]],-1,(0,255,0),-3)
    
        # draw pillars in blue color
        for i,j in zip(range(4),range(4,8)):
            img = cv2.line(img, tuple(imgpts[i]), tuple(imgpts[j]),(255),3)
    
        # draw top layer in red color
        img = cv2.drawContours(img, [imgpts[4:]],-1,(0,0,255),3)
    
        return img

    수정된 축 포인트. 3D 공간에서 한 큐브의 8코너 입니다.


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




    만약 그래픽스, 증강현실등에 관심이 있으면 OpenGL을 사용하여 더 복잡한 그림을 렌더링할 수 있습니다.



    Reference

    [1] https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_calib3d/py_pose/py_pose.html#pose-estimation

Designed by Tistory.