使用Python进行二维图像的三维重建

2D图像的三维重建是从一组2D图像中创建对象或场景的三维模型的过程。这个技术广泛应用于计算机视觉、机器人技术和虚拟现实等领域。

在本文中,我们将解释如何使用Python执行从2D图像到三维重建的过程。我们将使用TempleRing数据集作为示例,逐步演示这个过程。该数据集包含了在对象周围的一个环上采样的阿格里真托(Agrigento)“Dioskouroi神庙”复制品的47个视图。

三维重建的关键概念

在深入了解如何使用Python从2D图像执行三维重建的详细步骤之前,让我们首先回顾一些与这个主题相关的关键概念。

深度图

深度图是一幅图像,其中每个像素代表摄像机和场景中相应点之间的距离。深度图常用于计算机视觉和机器人技术中,用于表示场景的三维结构。

有许多不同的方法可以从2D图像计算深度图,包括立体对应、结构光和飞行时间等。在本文中,我们将使用立体对应来从示例数据集计算深度图。

Point Cloud

点云是表示对象或场景形状的三维空间中的一组点。点云常用于计算机视觉和机器人技术中,用于表示场景的三维结构。

一旦我们计算出代表场景深度的深度图,我们可以使用它来计算一个三维点云。这涉及使用有关摄像机内部和外部参数的信息,将深度图中的每个像素投影回三维空间。

网格

网格是一个由顶点、边和面连接而成的表面表示。网格常用于计算机图形学和虚拟现实中,用于表示对象或场景的形状。

一旦我们计算出代表对象或场景形状的三维点云,我们可以使用它来生成一个网格。这涉及使用诸如Marching Cubes或Poisson表面重建等算法,将表面拟合到点云上。

逐步实现

现在我们已经回顾了与2D图像的三维重建相关的一些关键概念,让我们看看如何使用Python执行这个过程。我们将使用TempleRing数据集作为示例,逐步演示这个过程。下面是一个执行Temple Ring数据集中图像的三维重建的示例代码:

安装库:

pip install numpy scipy

导入库:

#importing libraries` `import cv2``import numpy as np``import matplotlib.pyplot as plt``import os

加载TempleRing数据集的图像:

# Directory containing the dataset images``dataset_dir = '/content/drive/MyDrive/templeRing'
\# Initialize the list to store images  
images = \[\]\# Attempt to load the grayscale images and store them in the list  
for i in range(1, 48):  # Assuming images are named templeR0001.png to templeR0047.png  
    img\_path = os.path.join(dataset\_dir, f'templeR{i:04d}.png')  
    img = cv2.imread(img\_path, cv2.IMREAD\_GRAYSCALE)  
    if img is not None:  
        images.append(img)  
    else:  
        print(f"Warning: Unable to load 'templeR{i:04d}.png'")\# Visualize the input images  
num\_rows = 5  # Specify the number of rows  
num\_cols = 10  # Specify the number of columns  
fig, axs = plt.subplots(num\_rows, num\_cols, figsize=(15, 8))\# Loop through the images and display them  
for i, img in enumerate(images):  
    row\_index = i // num\_cols  # Calculate the row index for the subplot  
    col\_index = i % num\_cols   # Calculate the column index for the subplot  
    axs\[row\_index, col\_index\].imshow(img, cmap='gray')  
    axs\[row\_index, col\_index\].axis('off')\# Fill any remaining empty subplots with a white background  
for i in range(len(images), num\_rows \* num\_cols):  
    row\_index = i // num\_cols  
    col\_index = i % num\_cols  
    axs\[row\_index, col\_index\].axis('off')plt.show()

解释:这段代码加载灰度图像序列,将它们排列在网格布局中,并使用matplotlib显示它们。

为每个图像计算深度图:

# Directory containing the dataset images``dataset_dir = '/content/drive/MyDrive/templeRing'
\# Initialize the list to store images  
images = \[\]\# Attempt to load the grayscale images and store them in the list  
for i in range(1, 48):  # Assuming images are named templeR0001.png to templeR0047.png  
    img\_path = os.path.join(dataset\_dir, f'templeR{i:04d}.png')  
    img = cv2.imread(img\_path, cv2.IMREAD\_GRAYSCALE)  
    if img is not None:  
        images.append(img)  
    else:  
        print(f"Warning: Unable to load 'templeR{i:04d}.png'")\# Initialize the list to store depth maps  
depth\_maps = \[\]\# Create a StereoBM object with your preferred parameters  
stereo = cv2.StereoBM\_create(numDisparities=16, blockSize=15)\# Loop through the images to calculate depth maps  
for img in images:  
    # Compute the depth map  
    disparity = stereo.compute(img, img)    # Normalize the disparity map for visualization  
    disparity\_normalized = cv2.normalize(  
        disparity, None, 0, 255, cv2.NORM\_MINMAX, cv2.CV\_8U)    # Append the normalized disparity map to the list of depth maps  
    depth\_maps.append(disparity\_normalized)\# Visualize all the depth maps  
num\_rows = 5  # Specify the number of rows  
num\_cols = 10  # Specify the number of columns  
fig, axs = plt.subplots(num\_rows, num\_cols, figsize=(15, 8))for i, depth\_map in enumerate(depth\_maps):  
    row\_index = i // num\_cols  # Calculate the row index for the subplot  
    col\_index = i % num\_cols   # Calculate the column index for the subplot  
    axs\[row\_index, col\_index\].imshow(depth\_map, cmap='jet')  
    axs\[row\_index, col\_index\].axis('off')\# Fill any remaining empty subplots with a white background  
for i in range(len(depth\_maps), num\_rows \* num\_cols):  
    row\_index = i // num\_cols  
    col\_index = i % num\_cols  
    axs\[row\_index, col\_index\].axis('off')plt.show()

解释:这段代码负责使用Stereo Block Matching(StereoBM)算法从一系列立体图像中计算深度图。它遍历灰度立体图像列表,并为每一对相邻图像计算深度图。

可视化每个图像的深度图:

# Initialize an accumulator for the sum of depth maps``sum_depth_map = np.zeros_like(depth_maps[0], dtype=np.float64)
# Compute the sum of all depth maps``for depth_map in depth_maps:`    `sum_depth_map += depth_map.astype(np.float64)``# Calculate the mean depth map by dividing the sum by the number of depth maps``mean_depth_map = (sum_depth_map / len(depth_maps)).astype(np.uint8)``# Display the mean depth map``plt.figure(figsize=(8, 6))``plt.imshow(mean_depth_map, cmap='jet')``plt.title('Mean Depth Map')``plt.axis('off')``plt.show()

输出:

解释:这段代码通过累加深度图来计算平均深度图。然后,通过将总和除以深度图的数量来计算平均值。最后,使用jet颜色图谱显示平均深度图以进行可视化。

从平均深度图计算三维点云

# Initialize an accumulator for the sum of depth maps``sum_depth_map = np.zeros_like(depth_maps[0], dtype=np.float64)
\# Compute the sum of all depth maps  
for depth\_map in depth\_maps:  
    sum\_depth\_map += depth\_map.astype(np.float64)\# Calculate the mean depth map by dividing the sum by the number of depth maps  
mean\_depth\_map = (sum\_depth\_map / len(depth\_maps)).astype(np.uint8)\# Display the mean depth map  
plt.figure(figsize=(8, 6))  
plt.imshow(mean\_depth\_map, cmap='jet')  
plt.title('Mean Depth Map')  
plt.axis('off')  
plt.show()

解释:这段代码通过对深度图进行累加来计算平均深度图。然后,通过将总和除以深度图的数量来计算平均值。最后,使用Jet颜色映射来可视化显示平均深度图。

计算平均深度图的三维点云

#converting into point cloud` `points_3D = cv2.reprojectImageTo3D(mean_depth_map.astype(np.float32), np.eye(4))

解释:该代码将包含点云中点的三维坐标,并且您可以使用这些坐标进行三维重建。

从****点云生成网格

安装库

!pip install numpy scipy

导入库

#importing libraries` `from scipy.spatial import Delaunay``from skimage import measure``from skimage.measure import marching_cubes

生成网格

verts, faces, normals, values = measure.marching_cubes(points_3D)

解释:该代码将Marching Cubes算法应用于3D点云以生成网格。它返回定义结果3D网格的顶点、面、顶点法线和标量值。

可视化网格

fig = plt.figure()``ax = fig.add_subplot(111, projection='3d')``ax.plot_trisurf(verts[:, 0], verts[:, 1], verts[:, 2], triangles=faces)``plt.show()

输出:

**解释:**该代码使用matplotlib可视化网格。它创建一个3D图并使用ax.plot_trisurf方法将网格添加到其中。

这段代码从Temple Ring数据集加载图像,并使用块匹配(block matching)进行每个图像的深度图计算,然后通过平均所有深度图来计算平均深度图,并使用它来计算每个像素的三维点云。最后,它使用Marching Cubes算法从点云生成网格并进行可视化。

结果比较

# importing the libraries``import matplotlib.pyplot as plt``from mpl_toolkits.mplot3d import Axes3D
\# Create a figure with two subplots  
fig, axs = plt.subplots(1, 2, figsize=(10, 5))\# Visualize the original image in the first subplot  
axs\[0\].imshow(images\[0\], cmap='gray')  
axs\[0\].axis('off')  
axs\[0\].set\_title('Original')\# Visualize the reconstructed mesh in the second subplot  
ax = fig.add\_subplot(1, 2, 2, projection='3d')  
ax.plot\_trisurf(verts\[:, 0\], verts\[:, 1\], verts\[:, 2\], triangles=faces)  
ax.set\_title('Reconstructed')\# Show the figure  
plt.show()

解释:在此代码中,使用matplotlib创建了包含两个子图的图形。在第一个图中,显示了来自数据集的原始图像。在第二个图中,使用3D三角形表面图可视化了重建的3D网格。

方法2

以下是执行来自TempleRing数据集图像的3D重建的另一个示例代码:

引入模块:

import cv2``import numpy as np``import matplotlib.pyplot as plt``from google.colab.patches import cv2_imshow

加载两个Temple Ring数据集图像:

# Load the PNG images (replace with your actual file paths)``image1 = cv2.imread('/content/drive/MyDrive/templeRing/templeR0001.png')``image2 = cv2.imread('/content/drive/MyDrive/templeRing/templeR0002.png'

解释:该代码使用OpenCV的cv2.imread函数从TempleRing数据集加载两个图像。

转换为灰度图:

# Convert images to grayscale``gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)``gray2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)

该代码使用OpenCV将两个图像转换为灰度图像。它们以单通道表示,其中每个像素的值表示其强度,并且没有颜色通道。

查找SIFT关键点和描述符:

# Initialize the SIFT detector``sift = cv2.SIFT_create()
# Detect keypoints and compute descriptors for both images``kp1, des1 = sift.detectAndCompute(gray1, None)``kp2, des2 = sift.detectAndCompute(gray2, None)

该代码使用尺度不变特征变换(SIFT)算法在两个图像中查找关键点和描述符。它使用OpenCV的cv2.SIFT_create()函数创建一个SIFT对象,并调用其detectAndCompute方法来计算关键点和描述符。

使用FLANN匹配器匹配描述符:

# Create a FLANN-based Matcher object``flann = cv2.FlannBasedMatcher({'algorithm': 0, 'trees': 5}, {})
\# Match the descriptors using KNN (k-nearest neighbors)  
matches = flann.knnMatch(des1, des2, k=2)

解释:该代码使用Fast Library for Approximate Nearest Neighbors(FLANN)匹配器对描述符进行匹配。它使用OpenCV的cv2.FlannBasedMatcher函数创建FLANN匹配器对象,并调用其knnMatch方法来找到每个描述符的k个最近邻。

使用Lowe的比率测试筛选出好的匹配项

# Apply Lowe's ratio test to select good matches``good_matches = []``for m, n in matches:`    `if m.distance < 0.7 * n.distance:`        `good_matches.append(m)

解释:该代码使用Lowe的比率测试筛选出好的匹配项。它使用最近邻和次近邻之间距离比的阈值来确定匹配是否良好。

提取匹配的关键点

# Extract matched keypoints``src_pts = np.float32(`    `[kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)``dst_pts = np.float32(`    `[kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)

解释:该代码从两组关键点中提取匹配的关键点,这些关键点将用于估算对齐两个图像的变换。这些关键点的坐标存储在’src_pts’和’dst_pts’中。

使用RANSAC找到单应矩阵

# Find the homography matrix using RANSAC``H, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)

在这段代码中,它使用RANSAC算法基于匹配的关键点计算描述两个图像之间的变换的单应矩阵。单应矩阵后来可以用于拉伸或变换一个图像,使其与另一个图像对齐。

使用单应矩阵将第一个图像进行变换

# Perform perspective transformation to warp image1 onto image2``height, width = image2.shape[:2]``result = cv2.warpPerspective(image1, H, (width, height))
# Display the result``cv2_imshow(result)

解释:该代码使用单应矩阵和OpenCV的cv2.warpPerspective函数将第一个图像进行变换。它指定输出图像的大小足够大,可以容纳两个图像,然后呈现结果图像。

显示原始图像和重建图像

# Display the original images and the reconstructed image side by side``fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(12, 4))``ax1.imshow(cv2.cvtColor(image1, cv2.COLOR_BGR2RGB))``ax1.set_title('Image 1')``ax1.axis('off')``ax2.imshow(cv2.cvtColor(image2, cv2.COLOR_BGR2RGB))``ax2.set_title('Image 2')``ax2.axis('off')``ax3.imshow(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))``ax3.set_title('Reconstructed Image')``ax3.axis('off')
plt.show()

输出:

解释:这段代码展示了在一个具有三个子图的单一图形中可视化原始图像和重建图像的过程。它使用matplotlib库显示图像,并为每个子图设置标题和轴属性。

不同的可能方法

有许多不同的方法和算法可用于从2D图像执行3D重建。选择的方法取决于诸如输入图像的质量、摄像机校准信息的可用性以及重建的期望准确性和速度等因素。

一些常见的从2D图像执行3D重建的方法包括立体对应、运动结构和多视图立体。每种方法都有其优点和缺点,对于特定应用来说,最佳方法取决于具体的要求和约束。

结论

总的来说,本文概述了使用Python从2D图像进行3D重建的过程。我们讨论了深度图、点云和网格等关键概念,并使用TempleRing数据集演示了使用两种不同方法逐步进行的过程。我们希望本文能帮助您更好地理解从2D图像进行3D重建以及如何使用Python实现这一过程。有许多可用于执行3D重建的不同方法和算法,我们鼓励您进行实验和探索,以找到最适合您需求的方法。

· END ·
点击下方安全链接前往获取

CSDN大礼包:《Python入门&进阶学习资源包》免费分享

👉Python实战案例👈

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

图片

图片

👉Python书籍和视频合集👈

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

图片

👉Python副业创收路线👈

图片

这些资料都是非常不错的,朋友们如果有需要《Python学习路线&学习资料》,点击下方安全链接前往获取

CSDN大礼包:《Python入门&进阶学习资源包》免费分享

本文转自网络,如有侵权,请联系删除。

  • 13
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值