使用 OpenCV solvepnp() 函式解決 PnP 問題
OpenCV 庫是一個開源庫,旨在幫助完成計算機視覺任務。該庫與 Python 相容,可用於實現和解決不同的影象處理問題。
本教程將演示如何在 Python 中使用 OpenCV 庫中的 solvepnp()
函式。該函式用於解決姿態估計問題。
瞭解 PnP 問題
PnP 問題在計算機視覺中非常常見,代表 Perspective n-Points 問題。在這個問題中,我們無法在提供 2D 和 3D 座標後確定物體相對於相機的位姿。
這可以通過線上考試期間的面部跟蹤示例來理解。物體的姿態可以隨著方向的改變而改變。
以下兩種型別的運動促進了這種變化:
- 第一種運動是平移運動,可以沿三個軸中的任何一個發生。物體沿任何特定方向勻速運動,從而改變其座標。
- 第二種運動是旋轉運動,物體可以圍繞三個軸中的任何一個旋轉。
使用 opencv.solvepnp()
函式解決 PnP 問題
OpenCV 庫中的 solvepnp()
函式用於給定物件相對於相機的位姿估計,從而解決 PnP 問題。它返回旋轉和平移向量。
它使用相機矩陣的物件的 2D 和 3D 座標。提供的座標是面部的不同特徵。
這些特徵是鼻子、嘴角、下巴和雙眼。
讓我們討論使用此功能所需的所有引數。
objectPoints
引數採用前面提到的所有特徵的 3D 點。imagePoints
引數用於指定物件特徵的 2D 點。cameraMatrix
用於指定相機的內在價值。該矩陣是使用相機的中心點和焦距建立的。- 為了解決相機引起的失真,我們使用了
distCoeffs
引數。如果相機中的失真可以忽略不計,則該向量可以為 NULL。 - 我們可以使用
useExtrinsicGuess
引數將輸出結果用於初始計算,該引數可以為真或假。除此之外,只有flags
引數。
此函式返回的旋轉和平移向量可用於繪製物件姿勢的線。
例如,我們將確定下圖的位姿。
確定此影象姿勢的程式碼如下所示。
import cv2
import numpy as np
img = cv2.imread("img.jpg")
size = img.shape
image_points_2D = np.array([
(196, 141), # Nose tip
(190, 202), # Chin
(196, 124), # Left eye corner
(236, 128), # Right eye corner
(186, 175), # Left mouth
(214, 177) # Right mouth
], dtype="double")
figure_points_3D = np.array([
(0.0, 0.0, 0.0), # Nose tip
(0.0, -330.0, -65.0), # Chin
(-225.0, 170.0, -135.0), # Left eye left corner
(225.0, 170.0, -135.0), # Right eye right corne
(-150.0, -150.0, -125.0), # Left Mouth corner
(150.0, -150.0, -125.0) # Right mouth corner
])
distortion_coeffs = np.zeros((4,1))
focal_length = size[1]
center = (size[1]/2, size[0]/2)
matrix_camera = np.array(
[[focal_length, 0, center[0]],
[0, focal_length, center[1]],
[0, 0, 1]], dtype = "double"
)
success, vector_rotation, vector_translation = cv2.solvePnP(figure_points_3D, image_points_2D, matrix_camera, distortion_coeffs, flags=0)
nose_end_point2D, jacobian = cv2.projectPoints(np.array([(0.0, 0.0, 1000.0)]), vector_rotation, vector_translation, matrix_camera, distortion_coeffs)
for p in image_points_2D:
cv2.circle(img, (int(p[0]), int(p[1])), 3, (0,0,255), -1)
point1 = ( int(image_points_2D[0][0]), int(image_points_2D[0][1]))
point2 = ( int(nose_end_point2D[0][0][0]), int(nose_end_point2D[0][0][1]))
cv2.line(img, point1, point2, (255,255,255), 2)
cv2.imshow("Final",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
輸出:
上面提到的程式碼中發生了很多事情。讓我們一步一步來理解它。
首先,我們使用 imread()
函式讀取所需的影象。面部特徵的點在 2D 和 3D 中提到。
點和相機矩陣被提供給 solvepnp()
函式,該函式返回姿勢 3D 座標的旋轉和平移向量。
在我們直觀地繪製姿勢線之後。首先,我們繪製面部特徵。
我們使用 2D 點並使用 circle()
函式繪製每個點。
projectPoints()
用於確定 solvepnp()
函式返回的向量在影象平面上的投影。我們還需要在這個函式中傳遞相機引數來獲取投影。
我們使用 line()
函式從面部鼻子繪製一條與投影點對齊的線,以視覺化由 solvepnp()
方法確定的姿勢。
まとめ
本教程教我們如何使用 solvepnp()
函式來解決計算機視覺中的 PnP 問題。我們需要了解使用此方法所需的引數。
主要引數是影象的人臉特徵的 2D 和 3D 點以及相機矩陣。使用這些值,它返回確定姿勢的 3D 點的向量。
我們使用 projectPoints()
函式獲得這些點相對於相機的 2D 投影。最後,我們使用這些點繪製一條線來表示影象中確定的姿勢。
Manav is a IT Professional who has a lot of experience as a core developer in many live projects. He is an avid learner who enjoys learning new things and sharing his findings whenever possible.
LinkedIn