vprdb.core.utils

  1#  Copyright (c) 2023, Ivan Moskalenko, Anastasiia Kornilova
  2#
  3#  Licensed under the Apache License, Version 2.0 (the "License");
  4#  you may not use this file except in compliance with the License.
  5#  You may obtain a copy of the License at
  6#
  7#      http://www.apache.org/licenses/LICENSE-2.0
  8#
  9#  Unless required by applicable law or agreed to in writing, software
 10#  distributed under the License is distributed on an "AS IS" BASIS,
 11#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12#  See the License for the specific language governing permissions and
 13#  limitations under the License.
 14import numpy as np
 15import open3d as o3d
 16
 17from nptyping import Float, NDArray, Shape
 18
 19from vprdb.core.database import Database
 20from vprdb.core.voxel_grid import VoxelGrid
 21
 22
 23def calculate_point_cloud_coverage(
 24    query_pcd: o3d.geometry.PointCloud,
 25    db_pcd: o3d.geometry.PointCloud,
 26    voxel_grid: VoxelGrid,
 27) -> float:
 28    """
 29    Calculates coverage for query point cloud by database point cloud
 30    :param query_pcd: query point cloud
 31    :param db_pcd: database point cloud
 32    :param voxel_grid: voxel grid for down sampling
 33    :return: coverage ∈ [0; 1]
 34    """
 35    query_pcd = voxel_grid.voxel_down_sample(query_pcd)
 36    db_pcd = voxel_grid.voxel_down_sample(db_pcd)
 37    united_map = voxel_grid.voxel_down_sample(query_pcd + db_pcd)
 38
 39    query_size = len(query_pcd.points)
 40    intersection_size = query_size + len(db_pcd.points) - len(united_map.points)
 41    return intersection_size / query_size
 42
 43
 44def calculate_iou(
 45    pcd_1: o3d.geometry.PointCloud,
 46    pcd_2: o3d.geometry.PointCloud,
 47    voxel_grid: VoxelGrid,
 48) -> float:
 49    """
 50    Calculates IoU for two point clouds
 51    :param pcd_1: First point cloud
 52    :param pcd_2: Second point cloud
 53    :param voxel_grid: Voxel grid for down sampling
 54    :return: IoU ∈ [0; 1]
 55    """
 56    pcd_1 = voxel_grid.voxel_down_sample(pcd_1)
 57    pcd_2 = voxel_grid.voxel_down_sample(pcd_2)
 58    united_map = voxel_grid.voxel_down_sample(pcd_1 + pcd_2)
 59
 60    united_map_size = len(united_map.points)
 61    intersection_size = len(pcd_1.points) + len(pcd_2.points) - united_map_size
 62    iou = intersection_size / united_map_size
 63    return iou
 64
 65
 66def match_two_databases(
 67    source_db: Database, target_db: Database, voxel_grid: VoxelGrid
 68) -> list[int]:
 69    """
 70    Builds matches between two databases. Databases should have the same coordinate system
 71    :param source_db: The database for which matches are being built
 72    :param target_db: The database on the base of which the matches will be built
 73    :param voxel_grid: VoxelGrid for down sampling
 74    :return: Output matches.
 75    The i-th value from the list is the index of the frame from the target database,
 76    which is matched with the i-th source frame
 77    """
 78    matches = []
 79    for pose, pcd_raw in zip(source_db.trajectory, source_db.point_clouds):
 80        cur_coverages = []
 81        for target_pose, target_pcd in zip(
 82            target_db.trajectory, target_db.point_clouds
 83        ):
 84            pcd_query = pcd_raw.point_cloud.transform(pose)
 85            pcd_db = target_pcd.point_cloud.transform(target_pose)
 86            coverage = calculate_point_cloud_coverage(pcd_query, pcd_db, voxel_grid)
 87            cur_coverages.append(coverage)
 88        best_match = np.argmax(cur_coverages)
 89        matches.append(int(best_match))
 90    return matches
 91
 92
 93def find_bounds_for_multiple_databases(
 94    databases: list[Database],
 95) -> tuple[NDArray[Shape["3"], Float], NDArray[Shape["3"], Float]]:
 96    """
 97    Finds bounds for multiple databases
 98    :param databases: List of databases for finding bounds
 99    :return: Min and max bounds
100    """
101    min_bounds_global, max_bounds_global = [], []
102    for db in databases:
103        min_bounds, max_bounds = db.bounds
104        min_bounds_global.append(min_bounds)
105        max_bounds_global.append(max_bounds)
106    min_bounds_global, max_bounds_global = np.asarray(min_bounds_global), np.asarray(
107        max_bounds_global
108    )
109    min_bound = np.amin(min_bounds_global, axis=0)
110    max_bound = np.amax(max_bounds_global, axis=0)
111    return min_bound, max_bound
def calculate_point_cloud_coverage( query_pcd: open3d.cpu.pybind.geometry.PointCloud, db_pcd: open3d.cpu.pybind.geometry.PointCloud, voxel_grid: vprdb.core.voxel_grid.VoxelGrid) -> float:
24def calculate_point_cloud_coverage(
25    query_pcd: o3d.geometry.PointCloud,
26    db_pcd: o3d.geometry.PointCloud,
27    voxel_grid: VoxelGrid,
28) -> float:
29    """
30    Calculates coverage for query point cloud by database point cloud
31    :param query_pcd: query point cloud
32    :param db_pcd: database point cloud
33    :param voxel_grid: voxel grid for down sampling
34    :return: coverage ∈ [0; 1]
35    """
36    query_pcd = voxel_grid.voxel_down_sample(query_pcd)
37    db_pcd = voxel_grid.voxel_down_sample(db_pcd)
38    united_map = voxel_grid.voxel_down_sample(query_pcd + db_pcd)
39
40    query_size = len(query_pcd.points)
41    intersection_size = query_size + len(db_pcd.points) - len(united_map.points)
42    return intersection_size / query_size

Calculates coverage for query point cloud by database point cloud

Parameters
  • query_pcd: query point cloud
  • db_pcd: database point cloud
  • voxel_grid: voxel grid for down sampling
Returns

coverage ∈ [0; 1]

def calculate_iou( pcd_1: open3d.cpu.pybind.geometry.PointCloud, pcd_2: open3d.cpu.pybind.geometry.PointCloud, voxel_grid: vprdb.core.voxel_grid.VoxelGrid) -> float:
45def calculate_iou(
46    pcd_1: o3d.geometry.PointCloud,
47    pcd_2: o3d.geometry.PointCloud,
48    voxel_grid: VoxelGrid,
49) -> float:
50    """
51    Calculates IoU for two point clouds
52    :param pcd_1: First point cloud
53    :param pcd_2: Second point cloud
54    :param voxel_grid: Voxel grid for down sampling
55    :return: IoU ∈ [0; 1]
56    """
57    pcd_1 = voxel_grid.voxel_down_sample(pcd_1)
58    pcd_2 = voxel_grid.voxel_down_sample(pcd_2)
59    united_map = voxel_grid.voxel_down_sample(pcd_1 + pcd_2)
60
61    united_map_size = len(united_map.points)
62    intersection_size = len(pcd_1.points) + len(pcd_2.points) - united_map_size
63    iou = intersection_size / united_map_size
64    return iou

Calculates IoU for two point clouds

Parameters
  • pcd_1: First point cloud
  • pcd_2: Second point cloud
  • voxel_grid: Voxel grid for down sampling
Returns

IoU ∈ [0; 1]

def match_two_databases( source_db: vprdb.core.database.Database, target_db: vprdb.core.database.Database, voxel_grid: vprdb.core.voxel_grid.VoxelGrid) -> list[int]:
67def match_two_databases(
68    source_db: Database, target_db: Database, voxel_grid: VoxelGrid
69) -> list[int]:
70    """
71    Builds matches between two databases. Databases should have the same coordinate system
72    :param source_db: The database for which matches are being built
73    :param target_db: The database on the base of which the matches will be built
74    :param voxel_grid: VoxelGrid for down sampling
75    :return: Output matches.
76    The i-th value from the list is the index of the frame from the target database,
77    which is matched with the i-th source frame
78    """
79    matches = []
80    for pose, pcd_raw in zip(source_db.trajectory, source_db.point_clouds):
81        cur_coverages = []
82        for target_pose, target_pcd in zip(
83            target_db.trajectory, target_db.point_clouds
84        ):
85            pcd_query = pcd_raw.point_cloud.transform(pose)
86            pcd_db = target_pcd.point_cloud.transform(target_pose)
87            coverage = calculate_point_cloud_coverage(pcd_query, pcd_db, voxel_grid)
88            cur_coverages.append(coverage)
89        best_match = np.argmax(cur_coverages)
90        matches.append(int(best_match))
91    return matches

Builds matches between two databases. Databases should have the same coordinate system

Parameters
  • source_db: The database for which matches are being built
  • target_db: The database on the base of which the matches will be built
  • voxel_grid: VoxelGrid for down sampling
Returns

Output matches. The i-th value from the list is the index of the frame from the target database, which is matched with the i-th source frame

def find_bounds_for_multiple_databases( databases: list[vprdb.core.database.Database]) -> tuple[typing.NDArray, typing.NDArray]:
 94def find_bounds_for_multiple_databases(
 95    databases: list[Database],
 96) -> tuple[NDArray[Shape["3"], Float], NDArray[Shape["3"], Float]]:
 97    """
 98    Finds bounds for multiple databases
 99    :param databases: List of databases for finding bounds
100    :return: Min and max bounds
101    """
102    min_bounds_global, max_bounds_global = [], []
103    for db in databases:
104        min_bounds, max_bounds = db.bounds
105        min_bounds_global.append(min_bounds)
106        max_bounds_global.append(max_bounds)
107    min_bounds_global, max_bounds_global = np.asarray(min_bounds_global), np.asarray(
108        max_bounds_global
109    )
110    min_bound = np.amin(min_bounds_global, axis=0)
111    max_bound = np.amax(max_bounds_global, axis=0)
112    return min_bound, max_bound

Finds bounds for multiple databases

Parameters
  • databases: List of databases for finding bounds
Returns

Min and max bounds