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重建的不同方法和算法,我們鼓勵您進行實驗和探索,以找到最適合您需求的方法。
審核編輯:黃飛
-
機器人
+關注
關注
211文章
28418瀏覽量
207096 -
攝像機
+關注
關注
3文章
1601瀏覽量
60043 -
計算機視覺
+關注
關注
8文章
1698瀏覽量
45993 -
三維重建
+關注
關注
0文章
26瀏覽量
9922 -
python
+關注
關注
56文章
4797瀏覽量
84688
原文標題:使用Python進行二維圖像的三維重建
文章出處:【微信號:vision263com,微信公眾號:新機器視覺】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論