J'essaie de comprendre les bases de la reconstruction de points 3D à partir d'images stéréo 2D. Ce que j'ai compris jusqu'à présent peut être résumé comme suit:
Pour la reconstruction de points 3D (carte de profondeur), nous avons besoin de 2 images du même objet à partir de 2 vues différentes, étant donné cette paire d'images, nous avons également besoin d'une matrice de caméra (par exemple P1, P2)
Nous trouvons les points correspondants dans les deux images en utilisant des méthodes comme SIFT ou SURF etc.
Après avoir obtenu le point clé correspondant, nous trouvons trouver la matrice essentielle (disons K) en utilisant au moins 8 points clés (utilisés dans l'algorithme à 8 points)
Étant donné que nous sommes à la caméra 1, calculer les paramètres de la caméra 2 L'utilisation de la matrice essentielle renvoie 4 paramètres de caméra possibles
Finalement, nous utilisons les points correspondants et les deux paramètres de la caméra pour l'estimation de points 3D en utilisant la méthode de triangulation.
Après être passé par la section théorie, comme ma première expérience, j'ai essayé d'exécuter le code disponible ici , qui a fonctionné comme prévu. Avec quelques modifications dans le example.py
code J'ai essayé d'exécuter cet exemple sur toutes les paires d'images consécutives et de fusionner les nuages de points 3D pour la reconstruction 3D de l'objet (dino
) comme ci-dessous:
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()
Mais j'obtiens un résultat très inattendu. Veuillez me suggérer si la méthode ci-dessus est correcte ou comment puis-je fusionner plusieurs nuages de points 3D pour construire une seule structure 3D.
Une autre voie possible de compréhension pour vous serait d'examiner une implémentation open source de la structure de mouvement ou SLAM. Notez que ces systèmes peuvent devenir assez compliqués. Cependant, OpenSfM est écrit en Python et je pense qu'il est facile à naviguer et à comprendre. Je l'utilise souvent comme référence pour mon propre travail.
Juste pour vous donner un peu plus d'informations pour commencer (si vous choisissez de suivre cette voie). La structure du mouvement est un algorithme pour prendre une collection d'images 2D et créer un modèle 3D (nuage de points) à partir de là où il résout également la position de chaque caméra par rapport à ce nuage de points (c'est-à-dire que toutes les poses de caméra retournées sont dans le monde cadre et est donc le nuage de points).
Les étapes d'OpenSfM à un niveau élevé:
Lisez l'image exif pour toute information préalable que vous pouvez utiliser (par exemple la distance focale)
Extraire des points de fonctionnalité (par exemple SIFT)
Faire correspondre les points de fonctionnalité
Transformez ces correspondances de points de fonctionnalité en pistes (par exemple, si vous avez vu un point de fonctionnalité dans les images 1,2 et 3, vous pouvez alors le connecter en une piste au lieu de correspondance (1,2), correspondance (2,3), etc. ..)
Reconstruction incrémentale (notez qu'il existe également une approche globale). Ce processus utilisera les pistes pour ajouter progressivement des images à la reconstruction, trianguler de nouveaux points et affiner les poses/positions des points à l'aide d'un processus appelé Bundle Adjustment.
J'espère que cela aide.