使用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.pngimg_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 subplotcol_index = i % num_cols # Calculate the column index for the subplotaxs[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_colscol_index = i % num_colsaxs[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.pngimg_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 mapdisparity = stereo.compute(img, img) # Normalize the disparity map for visualizationdisparity_normalized = cv2.normalize(disparity, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U) # Append the normalized disparity map to the list of depth mapsdepth_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 subplotcol_index = i % num_cols # Calculate the column index for the subplotaxs[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_colscol_index = i % num_colsaxs[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 ·
HAPPY LIFE
本文仅供学习交流使用,如有侵权请联系作者删除
相关文章:

使用Python进行二维图像的三维重建
2D图像的三维重建是从一组2D图像中创建对象或场景的三维模型的过程。这个技术广泛应用于计算机视觉、机器人技术和虚拟现实等领域。 在本文中,我们将解释如何使用Python执行从2D图像到三维重建的过程。我们将使用TempleRing数据集作为示例,逐步演示这个过…...

go-zero微服务的使用
一、入门案例 1、使用goland创建一个工程 2、新建一个user.proto syntax "proto3";package user; // 这个地方表示生成的go的包名叫user option go_package "./user";message UserInfoRequest {int64 userId 1; }message UserInfoResponse {int64 user…...

Java排序算法之基数排序
基数排序(Radix Sort)是一种线性时间复杂度的排序算法,其时间复杂度为O(d(nk)),其中d是数字的位数,k是进制数。基数排序是一种非比较排序算法,它按照数位的大小来进行排序。它可以处理正整数、负整数和小数…...

Ubuntu20.0中安装Gradle
下载Gradle到temp文件夹 wget https://services.gradle.org/distributions/gradle-8.3-bin.zip -P /tmp 然后解压文件到/opt/gradle目录 sudo unzip -d /opt/gradle /tmp/gradle-8.3.zip 配置Gradle环境变量 接下来我们会创建一个gradle.sh文件来保存Gradle的环境变量 sudo…...

【Java并发编程六】多线程越界问题
ArrayList()越界错误 import java.util.ArrayList; public class myTest implements Runnable {static ArrayList<Integer> a new ArrayList<>(10);public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(new myTest());T…...

聊聊httpclient的disableConnectionState
序 本文主要研究一下httpclient的disableConnectionState disableConnectionState org/apache/http/impl/client/HttpClientBuilder.java /*** Disables connection state tracking.*/public final HttpClientBuilder disableConnectionState() {connectionStateDisabled t…...

Tomcat web.xml文件中的mime-mapping
在Tomcat安装目录的conf/web.xml文件中,定义了大量的<mime-mapping>元素,例如: 其中<extension>指定了文件的扩展名,<mime-type>指定了mime类型,放在<mime-mapping>元素中,就是将…...

【Java 进阶篇】JQuery 事件绑定:`on` 与 `off` 的奇妙舞曲
在前端开发的舞台上,用户与页面的互动是一场精彩的表演。而 JQuery,作为 JavaScript 的一种封装库,为这场表演提供了更为便捷和优雅的事件绑定方式。其中,on 和 off 两位主角,正是这场奇妙舞曲中的核心演员。在这篇博客…...

模块化Common JS 和 ES Module
目录 历程 1.几个函数:全局变量的污染,模块间没有联系 2.对象:暴露成员,外部可修改 3.立即执行函数:闭包实现模块私有作用域 common JS module和Module 过程 模块依赖:深度优先遍历、父 -> 子 -…...

基于java web个人财务管理系统
末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:采用JSP技术开发 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目&#x…...

soc估计:DESIGN AND DEVELOPMENT OF SoC ESTIMATION MODEL USING MACHINE LEARNING
这是一篇印度那边学生的毕业论文,唯一要记录的是里面提到了一个特征构造的思想,记录如下: 论文思想: 特征选用速度、电流、电压、温度、平均电压、平均电流、平均速度,模型用cnnlstmlrlr 平均特征计算方式:…...

2、LeetCode之两数相加
给你两个非空的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储一位数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字0之外,这两个数都不会以0开头。 输入&am…...

redis三种集群方式
redis有三种集群方式:主从复制,哨兵模式和集群。 1.主从复制 主从复制原理: 从服务器连接主服务器,发送SYNC命令; 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所…...

Java --- JVM之垃圾回收相关算法
目录 一、垃圾标记算法 1.1、垃圾标记阶段:对象存活判断 1.2、引用计数算法 1.3、可达性分析算法 1.4、GC Roots 二、对象的finalization机制 2.1、生存还是死亡? 三、查看GC Roots 3.1、使用MAT查看 四、使用JProfiler分析OOM 五、清除阶段算…...

CentOS 7.9 安装 nginx
系统版本 # cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core)搜索nginx相关的软件包 yum search nginx显示已安装的与 “nginx” 相关的软件包 yum list | grep nginx列出可用的 Nginx 软件包 yum list nginx --showduplicates安装 Nginx yum install -y ng…...

Newman
近期在复习Postman的基础知识,在小破站上跟着百里老师系统复习了一遍,也做了一些笔记,希望可以给大家一点点启发。 一)如何安装Newman 1、下载并安装NodeJs 在官网下载NodeJs: Download | Node.js(官网的…...

Transformer中WordPiece/BPE等不同编码方式详解以及优缺点
❤️觉得内容不错的话,欢迎点赞收藏加关注😊😊😊,后续会继续输入更多优质内容❤️ 👉有问题欢迎大家加关注私戳或者评论(包括但不限于NLP算法相关,linux学习相关,读研读博…...

Ubuntu20.04安装Beyond Compare 4.4.7
参考链接: 1.Ubuntu20.04 Beyond Compare 4.3.7 安装 2.Ubuntu20.04安装Beyond Compare 4.3.7...

制作含有音频、视频的网页
参考代码如下 <!DOCTYPE html> <html> <head><title>视频音乐网页</title> </head> <body><!-- 视频 --><video width"320" height"240" controls><source src"movie.mp4" type"…...

QPair的介绍及用法
QPair是一个模板类,它存储一对值(key,value),可以是不同的数据类型。QPair的用法有以下几个方面: QPair的构造函数有以下几种形式: QPair():默认构造函数,创建一个空的QP…...

掌握未来技术趋势,Python编程引领人工智能时代
掌握未来技术趋势,Python编程引领人工智能时代 摘要:Python作为一种高级编程语言,在人工智能领域中扮演着越来越重要的角色。本文将通过介绍Python编程的特点、应用场景及发展前景,展望Python未来的发展趋势,并结合代…...
【自留地】后端 - PHP - MySQL - Nginx - Python - Java
PHP ThinkPHP6入门手册 【精选】【汇总】ThinkPHP6入门手册_tp6手册_Rudon滨海渔村的博客-CSDN博客文章浏览阅读5.4k次。安装安装Composer【win】https://getcomposer.org/Composer-Setup.exe【Linux & MacOS】curl -sS https://getcomposer.org/installer | phpmv compo…...

网页视频下载工具 iTubeGo mac中文版软件特色
iTubeGo YouTube Downloader mac是一款功能强大的YouTube视频下载工具。 iTubeGo YouTube Downloader mac软件特色 多种格式支持:iTubeGo YouTube Downloader可以将YouTube视频下载为多种常见的视频和音频格式,包括MP4、MP3、AVI、FLV、MOV、WMV等&…...

深度学习入门(第三天)——卷积神经网络
一、卷积神经网络应用领域 CV领域发展: 比赛中预测错误率的百分比,每年逐步下降。Human是人类肉眼的识别能力,2016年开始已经远高于人类肉眼死别能力,后面就取消了该方向的比赛了。 检测任务: 分类与检索:…...

【Unity小技巧】图片使用的一些常见问题
文章目录 前言Button不规则按钮点击空白区域不响应点击事件1. 设置资源参数2. 代码设置按钮Image的alphaHitTestMinimumThreshold3. 解释:4. 效果 Unity Image 原图比例控制方法一 Preserve Aspect1. 设置勾选Preserve Aspect(保持长宽比)&am…...

ZJU Beamer学习手册(二)
ZJU Beamer学习手册基于 Overleaf 的 ZJU Beamer模板 进行解读,本文则基于该模版进行进一步修改。 参考文献 首先在frame文件夹中增加reference.tex文件,文件内容如下。这段代码对参考文献的引用进行了预处理。 \usepackage[backendbiber]{biblatex} \…...

Shaderlab的组成部分SubShader
文档 渲染标签 渲染状态 渲染通道 Subshader 一个shader文件至少有一个subshader;多个subshader的顺序一般按照效果好到差的顺序编写显示物体的时候,设备从多个subshader中,按从前到后的顺序找到第一个符合的subshader进行执行 Subshader组成 渲染标…...

C语言 字符函数汇总,模拟实现各字符函数(炒鸡详细)
目录 求字符串长度 strlen 示例 模拟实现strlen 长度不受限制的字符串函数 strcpy 示例 模拟实现strcpy strcat 模拟实现strcat strcmp 示例 模拟实现strcmp 长度受限制的字符串函数介绍 strncpy 示例 模拟实现strncpy strncat 示例 模拟实现strncat s…...

微积分在神经网络中的本质
calculus 在一个神经网络中我们通常将每一层的输出结果表示为: a [ l ] a^{[l]} a[l] 为了方便记录,将神经网络第一层记为: [ 1 ] [1] [1] 对应的计算记录为为: a [ l ] : 第 l 层 a [ j ] : 第 j 个神经…...

CentOS部署Skywalking
一、安装Docker #yum -y install gcc #yum -y install gcc-c #yum install -y yum-utils 设置国内的镜像仓库 #yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo 更新yum软件包索引 #yum makecache fast 安装Docker引擎 #yum …...