从2d图像的3d点重建

fla*_*ite 14 python image-processing computer-vision 3d-reconstruction

我正在尝试从2d立体图像中理解3d点重建的基础知识.到目前为止我所理解的可归纳如下:

对于3d点(深度图)重建,我们需要来自2个不同视图的同一对象的2个图像,给定这样的图像对我们还需要相机矩阵(比如P1,P2)

  • 我们使用SIFT或SURF等方法在两个图像中找到相应的点.

  • 在得到相应的关键点后,我们发现使用最少8个关键点(用于8点算法)找到基本矩阵(比如K)

  • 鉴于我们在相机1,计算相机2的参数使用基本矩阵返回4个可能的相机参数

  • 最后,我们使用三角测量法使用相应的点和两个相机参数进行三维点估计.

在完成理论部分后,作为我的第一个实验,我尝试运行此处提供的代码,其工作正常.在example.py代码中进行了一些修改后,我尝试在所有连续图像对上运行此示例,并合并三维点云以进行对象(dino)的三维重建,如下所示:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import cv2

from camera import Camera
import structure
import processor
import features

def dino():
    # Dino
    img1 = cv2.imread('imgs/dinos/viff.003.ppm')
    img2 = cv2.imread('imgs/dinos/viff.001.ppm')
    pts1, pts2 = features.find_correspondence_points(img1, img2)
    points1 = processor.cart2hom(pts1)
    points2 = processor.cart2hom(pts2)

    fig, ax = plt.subplots(1, 2)
    ax[0].autoscale_view('tight')
    ax[0].imshow(cv2.cvtColor(img1, cv2.COLOR_BGR2RGB))
    ax[0].plot(points1[0], points1[1], 'r.')
    ax[1].autoscale_view('tight')
    ax[1].imshow(cv2.cvtColor(img2, cv2.COLOR_BGR2RGB))
    ax[1].plot(points2[0], points2[1], 'r.')
    fig.show()

    height, width, ch = img1.shape
    intrinsic = np.array([  # for dino
        [2360, 0, width / 2],
        [0, 2360, height / 2],
        [0, 0, 1]])

    return points1, points2, intrinsic


points3d = np.empty((0,0))
files = glob.glob("imgs/dinos/*.ppm")
len = len(files)

for item in range(len-1):
    print(files[item], files[(item+1)%len])
    #dino() function takes 2 images as input
    #and outputs the keypoint point matches(corresponding points in two different views) along the camera intrinsic parameters.
    points1, points2, intrinsic = dino(files[item], files[(item+1)%len])
    #print(('Length', len(points1))
    # Calculate essential matrix with 2d points.
    # Result will be up to a scale
    # First, normalize points
    points1n = np.dot(np.linalg.inv(intrinsic), points1)
    points2n = np.dot(np.linalg.inv(intrinsic), points2)
    E = structure.compute_essential_normalized(points1n, points2n)
    print('Computed essential matrix:', (-E / E[0][1]))

    # Given we are at camera 1, calculate the parameters for camera 2
    # Using the essential matrix returns 4 possible camera paramters
    P1 = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0]])
    P2s = structure.compute_P_from_essential(E)

    ind = -1
    for i, P2 in enumerate(P2s):
        # Find the correct camera parameters
        d1 = structure.reconstruct_one_point(
            points1n[:, 0], points2n[:, 0], P1, P2)

        # Convert P2 from camera view to world view
        P2_homogenous = np.linalg.inv(np.vstack([P2, [0, 0, 0, 1]]))
        d2 = np.dot(P2_homogenous[:3, :4], d1)

        if d1[2] > 0 and d2[2] > 0:
            ind = i

    P2 = np.linalg.inv(np.vstack([P2s[ind], [0, 0, 0, 1]]))[:3, :4]
    #tripoints3d = structure.reconstruct_points(points1n, points2n, P1, P2)
    tripoints3d = structure.linear_triangulation(points1n, points2n, P1, P2)

    if not points3d.size:
        points3d = tripoints3d
    else:
        points3d = np.concatenate((points3d, tripoints3d), 1)


fig = plt.figure()
fig.suptitle('3D reconstructed', fontsize=16)
ax = fig.gca(projection='3d')
ax.plot(points3d[0], points3d[1], points3d[2], 'b.')
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
ax.set_zlabel('z axis')
ax.view_init(elev=135, azim=90)
plt.show()
Run Code Online (Sandbox Code Playgroud)

但是我的结果非常出乎意料.如果上面的方法是正确的,请建议我如何合并多个三维点云来构建单个三维结构.

Jom*_*t17 5

另一种可能的理解途径是查看来自运动或 SLAM 的结构的开源实现。请注意,这些系统可能会变得非常复杂。然而,OpenSfM 是用 Python 编写的,我认为它很容易导航和理解。我经常用它作为我自己工作的参考。

只是为您提供更多入门信息(如果您选择走这条路)。运动结构是一种算法,用于获取 2D 图像集合并从中创建 3D 模型(点云),其中它还解决了每个相机相对于该点云的位置(即所有返回的相机姿势都在世界上)帧,点云也是)。

OpenSfM 的高层步骤:

  1. 读取图像 exif 以获取您可以使用的任何先验信息(例如焦距)

  2. 提取特征点(例如SIFT)

  3. 匹配特征点

  4. 将这些特征点匹配转换为轨迹(例如,如果您在图像 1,2 和 3 中看到一个特征点,那么您可以将其连接到轨迹而不是 match(1,2)、match(2,3) 等。 ..)

  5. 增量重建(请注意,还有一种全局方法)。此过程将使用轨迹以增量方式将图像添加到重建中,对新点进行三角测量,并使用称为束调整的过程来优化姿势/点位置。

希望这有帮助。


BCo*_*nic -1

总体思路如下。

在代码的每次迭代中,您都会计算右相机相对于左相机的相对姿势。然后,对 2D 点进行三角测量,并将生成的 3D 点连接到一个大数组中。但连接的点不在同一坐标系中。

相反,您需要做的是累积估计的相对姿势,以维持绝对姿势估计。然后,您可以像以前一样对 2D 点进行三角测量,但在连接结果点之前,您需要将它们映射到第一个摄像机的坐标系。

以下是如何执行此操作。

首先,在循环之前,初始化一个累积矩阵absolute_P1

points3d = np.empty((0,0))
files = glob.glob("imgs/dinos/*.ppm")
len = len(files)
absolute_P1 = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])

for item in range(len-1):
    # ...
Run Code Online (Sandbox Code Playgroud)

然后,在特征三角测量之后,将3D点映射到第一个相机的坐标系并更新累积的姿态:

# ...
P2 = np.linalg.inv(np.vstack([P2s[ind], [0, 0, 0, 1]]))
tripoints3d = structure.linear_triangulation(points1n, points2n, P1, P2[:3, :4])

abs_tripoints3d = np.matmul(absolute_P1, np.vstack([tripoints3d, np.ones(np.shape(tripoints3d)[1])]))
absolute_P1 = np.matmul(absolute_P1, np.linalg.inv(P2)) # P2 needs to be 4x4 here!

if not points3d.size:
    points3d = abs_tripoints3d
else:
    points3d = np.concatenate((points3d, abs_tripoints3d), 1)

# ...
Run Code Online (Sandbox Code Playgroud)