使用 OpenCV solvepnp() 函式解決 PnP 問題

Manav Narula 2023年1月30日
  1. 瞭解 PnP 問題
  2. 使用 opencv.solvepnp() 函式解決 PnP 問題
  3. まとめ
使用 OpenCV solvepnp() 函式解決 PnP 問題

OpenCV 庫是一個開源庫,旨在幫助完成計算機視覺任務。該庫與 Python 相容,可用於實現和解決不同的影象處理問題。

本教程將演示如何在 Python 中使用 OpenCV 庫中的 solvepnp() 函式。該函式用於解決姿態估計問題。

瞭解 PnP 問題

PnP 問題在計算機視覺中非常常見,代表 Perspective n-Points 問題。在這個問題中,我們無法在提供 2D 和 3D 座標後確定物體相對於相機的位姿。

這可以通過線上考試期間的面部跟蹤示例來理解。物體的姿態可以隨著方向的改變而改變。

以下兩種型別的運動促進了這種變化:

  1. 第一種運動是平移運動,可以沿三個軸中的任何一個發生。物體沿任何特定方向勻速運動,從而改變其座標。
  2. 第二種運動是旋轉運動,物體可以圍繞三個軸中的任何一個旋轉。

使用 opencv.solvepnp() 函式解決 PnP 問題

OpenCV 庫中的 solvepnp() 函式用於給定物件相對於相機的位姿估計,從而解決 PnP 問題。它返回旋轉和平移向量。

它使用相機矩陣的物件的 2D 和 3D 座標。提供的座標是面部的不同特徵。

這些特徵是鼻子、嘴角、下巴和雙眼。

讓我們討論使用此功能所需的所有引數。

  1. objectPoints 引數採用前面提到的所有特徵的 3D 點。
  2. imagePoints 引數用於指定物件特徵的 2D 點。
  3. cameraMatrix 用於指定相機的內在價值。該矩陣是使用相機的中心點和焦距建立的。
  4. 為了解決相機引起的失真,我們使用了 distCoeffs 引數。如果相機中的失真可以忽略不計,則該向量可以為 NULL。
  5. 我們可以使用 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()

輸出:

使用 solvepnp 確定的姿勢

上面提到的程式碼中發生了很多事情。讓我們一步一步來理解它。

首先,我們使用 imread() 函式讀取所需的影象。面部特徵的點在 2D 和 3D 中提到。

點和相機矩陣被提供給 solvepnp() 函式,該函式返回姿勢 3D 座標的旋轉和平移向量。

在我們直觀地繪製姿勢線之後。首先,我們繪製面部特徵。

我們使用 2D 點並使用 circle() 函式繪製每個點。

projectPoints() 用於確定 solvepnp() 函式返回的向量在影象平面上的投影。我們還需要在這個函式中傳遞相機引數來獲取投影。

我們使用 line() 函式從面部鼻子繪製一條與投影點對齊的線,以視覺化由 solvepnp() 方法確定的姿勢。

まとめ

本教程教我們如何使用 solvepnp() 函式來解決計算機視覺中的 PnP 問題。我們需要了解使用此方法所需的引數。

主要引數是影象的人臉特徵的 2D 和 3D 點以及相機矩陣。使用這些值,它返回確定姿勢的 3D 點的向量。

我們使用 projectPoints() 函式獲得這些點相對於相機的 2D 投影。最後,我們使用這些點繪製一條線來表示影象中確定的姿勢。

作者: Manav Narula
Manav Narula avatar Manav Narula avatar

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

相關文章 - Python OpenCV