在 Python 中使用 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 参数。

此函数返回的旋转和平移向量可用于绘制对象姿势的线。

例如,我们将确定下图的位姿。

在 Python 中使用 OpenCV solvepnp() 函数解决 PnP 问题

确定此图像姿势的代码如下所示。

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()

输出:

在 Python 中使用 OpenCV solvepnp() 函数解决 PnP 问题

上面提到的代码中发生了很多事情。让我们一步一步来理解它。

首先,我们使用 imread() 函数读取所需的图像。面部特征的点在 2D 和 3D 中提到。

点和相机矩阵被提供给 solvepnp() 函数,该函数返回姿势 3D 坐标的旋转和平移向量。

在我们直观地绘制姿势线之后。首先,我们绘制面部特征。

我们使用 2D 点并使用 circle() 函数绘制每个点。

projectPoints() 用于确定 solvepnp() 函数返回的向量在图像平面上的投影。我们还需要在这个函数中传递相机参数来获取投影。

我们使用 line() 函数从面部鼻子绘制一条与投影点对齐的线,以可视化由 solvepnp() 方法确定的姿势。

结论

本教程教我们如何使用 solvepnp() 函数来解决计算机视觉中的 PnP 问题。我们需要了解使用此方法所需的参数。

主要参数是图像的人脸特征的 2D 和 3D 点以及相机矩阵。使用这些值,它返回确定姿势的 3D 点的向量。

我们使用 projectPoints() 函数获得这些点相对于相机的 2D 投影。最后,我们使用这些点绘制一条线来表示图像中确定的姿势。