OpenCV-Python 姿態估計

目標

在本章中

我們將學習利用calib3d模塊在圖像中創建一些3D效果。

基礎

這將是一小部分。在上一次相機校準的會話中,你發現了相機矩陣,失真係數等。給定圖案圖像,我們可以利用以上信息來計算其姿勢或物體在空間中的位置,例如其旋轉方式, 對於平面物體,我們可以假設Z = 0,這樣,問題就變成了如何將相機放置在空間中以查看圖案圖像。 因此,如果我們知道對象在空間中的位置,則可以在其中繪製一些2D圖以模擬3D效果。 讓我們看看如何做。

我們的問題是,我們想在棋盤的第一個角上繪製3D座標軸(X,Y,Z)。 X軸為藍色,Y軸為綠色,Z軸為紅色。 因此,實際上Z軸應該感覺像它垂直於我們的棋盤平面。

首先,讓我們從先前的校準結果中加載相機矩陣和失真係數。

<code>import numpy as np
import cv2 as cv
import glob
# 加載先前保存的數據
with np.load('B.npz') as X:
mtx, dist, _, _ = [X[i] for i in ('mtx','dist','rvecs','tvecs')]/<code>

現在讓我們創建一個函數,繪製,該函數將棋盤上的角(使用cv.findChessboardCorners()獲得)和軸點繪製為3D軸。

<code>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/<code>

然後,與前面的情況一樣,我們創建終止條件,對象點(棋盤上角的3D點)和軸點。 軸點是3D空間中用於繪製軸的點。 我們繪製長度為3的軸(由於我們根據該棋盤尺寸進行了校準,因此單位將以國際象棋正方形的尺寸為單位)。因此我們的X軸從(0,0,0)繪製為(3,0,0),因此對於Y軸。 對於Z軸,從(0,0,0)繪製為(0,0,-3)。 負號表示它被拉向相機。

<code>criteria = (cv.TERM_CRITERIA_EPS + cv.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)/<code>

現在,像往常一樣,我們加載每個圖像。搜索7x6網格。如果找到,我們將使用子角像素對其進行優化。然後使用函數cv.solvePnPRansac()計算旋轉和平移。一旦有了這些變換矩陣,就可以使用它們將軸點投影到圖像平面上。簡而言之,我們在圖像平面上找到與3D空間中(3,0,0),(0,3,0),(0,0,3)中的每一個相對應的點。一旦獲得它們,就可以使用draw()函數從第一個角到這些點中的每個點繪製線條。完畢!!!

<code>for fname in glob.glob('left*.jpg'):
img = cv.imread(fname)
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
ret, corners = cv.findChessboardCorners(gray, (7,6),None)
if ret == True:
corners2 = cv.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
# 找到旋轉和平移矢量。


ret,rvecs, tvecs = cv.solvePnP(objp, corners2, mtx, dist)
# 將3D點投影到圖像平面
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[:6]+'.png', img)
cv.destroyAllWindows()/<code>

請參閱下面的一些結果。請注意,每個軸長3個long單位。

繪製立方體

如果要繪製立方體,請如下修改draw()函數和軸點。 修改後的draw()函數:

<code>def draw(img, corners, imgpts):
imgpts = np.int32(imgpts).reshape(-1,2)
# 用綠色繪製底層
img = cv.drawContours(img, [imgpts[:4]],-1,(0,255,0),-3)
# 用藍色繪製高
for i,j in zip(range(4),range(4,8)):
img = cv.line(img, tuple(imgpts[i]), tuple(imgpts[j]),(255),3)
# 用紅色繪製頂層
img = cv.drawContours(img, [imgpts[4:]],-1,(0,0,255),3)
return img/<code>

修改的軸點。它們是3D空間中多維數據集的8個角:

<code>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] ])/<code>

查看以下結果:

如果您對圖形,增強現實等感興趣,則可以使用OpenGL渲染更復雜的圖形。