ZiadMansourM / photogrammetry

Photogrammetry: Close Range 3D scanning. Our graduation project 🎓
https://docs.scanmate.sreboy.com/
GNU General Public License v3.0
4 stars 0 forks source link

Debugging No Output Issue. #24

Open ZiadMansourM opened 1 year ago

ZiadMansourM commented 1 year ago
""" Step Four: Evaluation
Inputs:
- matches_ids: list[list[tuple[int, float]]]
- relevant_images: list[list[int]]

Outputs:
- precision: float
- recall: float

Main Function:
1. evaluate_matches

Sub Utils Functions:
1. precision_at_k
2. recall_at_k
"""

def evaluate_matches(matches_ids, relevant_images) -> Tuple[float, float]:
    """
    Args:
        matches_ids: a list of lists, where each list contains the top 10 most similar images to the i-th image
        relevant_images: a list of lists, where each list contains the relevant images for the i-th image

    Returns:
        A tuple of floats, representing the precision and recall of the matching algorithm.
    """
    k = 10
    precisions = []
    recalls = []
    for i, relevant in enumerate(relevant_images):
        matches = [match[0] for match in matches_ids[i][:k]]
        relevant_set = set(relevant)
        precision = precision_at_k(matches, relevant_set, k)
        recall = recall_at_k(matches, relevant_set)
        precisions.append(precision)
        recalls.append(recall)
    return np.mean(precisions), np.mean(recalls)

def precision_at_k(matches, relevant, k):
    return len(set(matches[:k]) & relevant) / k

def recall_at_k(matches, relevant):
    return len(set(matches) & relevant) / len(relevant) if relevant else 0.0
ZiadMansourM commented 1 year ago

🔧 Remove outliers:

import numpy as np
from sklearn.cluster import DBSCAN

# Generate random point cloud
N = 17_264_381
points_cloud = np.random.rand(N, 3)

# Cluster the points using DBSCAN algorithm
dbscan = DBSCAN(eps=0.5, min_samples=10).fit(points_cloud)

# Get the cluster labels for each point
labels = dbscan.labels_

# Get the indices of the core points (i.e., points that are part of a dense region)
core_indices = np.where(labels != -1)[0]

# Get the coordinates of the core points
core_points = points_cloud[core_indices, :]

# Get the indices of the outlier points (i.e., points that are not part of any dense region)
outlier_indices = np.where(labels == -1)[0]

# Get the coordinates of the outlier points
outlier_points = points_cloud[outlier_indices, :]

# Print the number of clusters and the number of outlier points
print("Number of clusters:", len(np.unique(labels)) - 1)
print("Number of outlier points:", len(outlier_indices))
ZiadMansourM commented 1 year ago
def triangulate(feature_matches_list, K_matrix):
  """
  Triangulation step for 3D object.

  Args:
    feature_matches_list (list): List of feature matches between images. each is a list contains 
        the index of the first image, the index of the second image, 
        and a list of OpenCV.DMatch objects.
    K_matrix (numpy.ndarray): 3x3 camera calibration matrix.

  Returns:
    points_cloud (numpy.ndarray): (N, 3) where N is number of points and 3 represents the three axis x, y, z
  """

  # Get the number of images.
  num_images = len(feature_matches_list)

  # Create a list to store the 3D points.
  points_cloud = []

  # For each pair of images...
  for i in range(num_images):
    for j in range(i + 1, num_images):

      # Get the feature matches between the two images.
      matches = feature_matches_list[i][j]

      # If there are no feature matches, continue to the next pair of images.
      if len(matches) == 0:
        continue

      # Get the 2D coordinates of the feature matches in the two images.
      x1 = matches[:, 0].reshape(-1, 2)
      x2 = matches[:, 1].reshape(-1, 2)

      # Use triangulation to compute the 3D coordinates of the feature matches.
      points_3d = cv2.triangulatePoints(K_matrix, K_matrix, x1, x2)

      # Add the 3D points to the list.
      points_cloud.append(points_3d)

  # Return the 3D points.
  return np.array(points_cloud)
ZiadMansourM commented 1 year ago

Sure, I can help you fix the code. The problem is that the triangulatePoints() function is dividing the output by the fourth element of the output, which is the depth of the point. However, the depth of the point is not always positive. In fact, it can be negative, which is why you are getting negative values for the Z axis.

To fix this, we need to change the way that we are dividing the output. Instead of dividing by the fourth element, we need to check if the fourth element is positive. If it is positive, then we can divide by it. If it is negative, then we can set it to zero.

Here is the updated code:

def triangulatePoints(P1, P2, pts1, pts2):
    """
    Triangulates the given matching points from two images using the given camera matrices.

    Parameters:
    P1 (numpy.ndarray): 3x4 camera matrix of the first image.
    P2 (numpy.ndarray): 3x4 camera matrix of the second image.
    pts1 (numpy.ndarray): Nx2 matrix containing the coordinates of matching points in the first image.
    pts2 (numpy.ndarray): Nx2 matrix containing the coordinates of matching points in the second image.

    Returns:
    numpy.ndarray: Nx3 matrix containing the triangulated 3D points.
    """
    pts4D = OpenCV.triangulatePoints(P1, P2, pts1.T, pts2.T)
    if pts4D[3] > 0:
        pts3D = pts4D / pts4D[3]
    else:
        pts3D = pts4D[:3]
    return pts3D.T