Getting weird mesh while trying 3d reconstruction

38 views Asked by At

I am trying to implement 3d reconstruction using opencv however the triangulation result is very weird. I am not able to understand where I am going wrong with this.enter image description hereenter image description here

Here is my code.

ThreeDReconstruct.py

import cv2
import os
import numpy as np
import matplotlib.pyplot as plt
import glob
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from tqdm.auto import tqdm

class ThreeDReconstruct:
    def __init__(self) -> None:
        self.matches_all=[]
        self.keypoints_and_descriptors=[]
        self.calib_images=[]
    
    def featureDetection(self):
        images=self.calib_images
        for i in tqdm(range(len(images)),desc="Features and Descriptors"):
            img=images[i]
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            sift = cv2.SIFT_create()
            keypoints = sift.detect(gray, None)
            keypoints = sorted(keypoints, key=lambda x: -x.response)[:1000]
            keypoints, descriptors = sift.compute(gray, keypoints)
            self.keypoints_and_descriptors.append([keypoints,descriptors])
            
        # Matching
        # https://stackoverflow.com/questions/42397009/what-values-does-the-algorithm-parametre-take-in-opencvs-flannbasedmatcher-cons
        FLANN_INDEX_KDTREE = 1
        index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=4)
        search_params = dict(checks=2)
        flann = cv2.FlannBasedMatcher(index_params, search_params)

        for i in tqdm(range(len(self.keypoints_and_descriptors) - 1),desc="Matching"):
            matches = flann.knnMatch(self.keypoints_and_descriptors[i][-1], self.keypoints_and_descriptors[i + 1][-1], k=2)

            # Apply ratio test as per Lowe's paper
            # https://blog.finxter.com/5-effective-ways-to-implement-flann-based-feature-matching-in-opencv-python/
            goodMatches = []
            for (m, n) in matches:
                if m.distance < 0.7 * n.distance:
                    goodMatches.append(matches)
            self.matches_all.append(goodMatches)
            
    def calibrateCamera(self,images):
         # termination criteria
        criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
        # prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
        objp = np.zeros((6*9,3), np.float32)
        objp[:,:2] = np.mgrid[0:6,0:9].T.reshape(-1,2)
        # Arrays to store object points and image points from all the images.
        objpoints = [] # 3d point in real world space
        imgpoints = [] # 2d points in image plane.
        for i in tqdm(range(len(images)),desc="Camera Calibration"):
            img = images[i]
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            # Find the chess board corners
            ret, corners = cv2.findChessboardCorners(gray, (6,9), flags=cv2.CALIB_USE_INTRINSIC_GUESS)
            # If found, add object points, image points (after refining them)
            
            if ret == True:
                objpoints.append(objp)
                corners2 = cv2.cornerSubPix(gray,corners, (11,11), (-1,-1), criteria)
                imgpoints.append(corners2)
                self.calib_images.append(img)
                
        ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1], None, None)
        return ret, mtx, dist, rvecs, tvecs 
        
    def save_ply_file(self,filename, vertices):
        header = """ply
        format ascii 1.0
        element vertex {}
        property float x
        property float y
        property float z
        end_header
        """.format(len(vertices))

        with open(filename, 'w') as f:
            f.write(header)
            for vertex in vertices:
                f.write('{} {} {}\n'.format(vertex[0], vertex[1], vertex[2]))

example_usage.py


import cv2
import os
import numpy as np
import matplotlib.pyplot as plt
import glob
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from ThreeDReconstruct import ThreeDReconstruct
from tqdm.auto import tqdm
import open3d as o3d

directory = "../images/"

image_files = sorted(glob.glob(directory + "*.jpg"))
images=[]
for image_file in image_files:
    image = cv2.imread(image_file)
    if image is not None:
        images.append(image)
    else:
        print(f"Unable to read image file: {image_file}")
    
    if len(images)>50:
        break

reconstrucClass = ThreeDReconstruct()

x_coords=[]
y_coords=[]
z_coords=[]

ret, mtx, dist, rvecs, tvecs=reconstrucClass.calibrateCamera(images)
reconstrucClass.featureDetection()


for i in tqdm(range(len(reconstrucClass.keypoints_and_descriptors)-1),desc="Triangulation"):
    
    P1 = np.hstack((cv2.Rodrigues(rvecs[i])[0], tvecs[i]))
    P2 = np.hstack((cv2.Rodrigues(rvecs[i+1])[0], tvecs[i+1]))

    src_pts = np.float32(reconstrucClass.keypoints_and_descriptors[i][-1]).reshape(-1, 1, 2)
    dst_pts = np.float32(reconstrucClass.keypoints_and_descriptors[i+1][-1]).reshape(-1, 1, 2)
    

    points_4d=cv2.triangulatePoints(P1,P2,src_pts,dst_pts)
    # Convert homogeneous coordinates to 3D coordinates
    points_3d=cv2.convertPointsFromHomogeneous(points_4d.T)
    x_coords.extend(points_3d[:, 0, 0])
    y_coords.extend(points_3d[:, 0, 1])
    z_coords.extend(points_3d[:, 0, 2])
    

print(len(x_coords),len(y_coords),len(z_coords))
# Create 3D plot

reconstrucClass.save_ply_file("wine.ply",list(zip(x_coords,y_coords,z_coords)))

# Create a point cloud
point_cloud = o3d.geometry.PointCloud()
point_cloud.points = o3d.utility.Vector3dVector(zip(x_coords, y_coords, z_coords))

# Create a mesh from the point cloud
mesh = o3d.geometry.TriangleMesh.create_from_point_cloud_alpha_shape(
    point_cloud, alpha=1.0
)

# Compute normals
mesh.compute_vertex_normals()

Save the mesh as an STL file
o3d.io.write_triangle_mesh("output_mesh.stl", mesh)

Here is an example of input image

enter image description here

I tried implementing the functions but I am surely doing something wrong. I have used chess board image for camera calibration in each image to ensure i get correct matrix for each image.

0

There are 0 answers