supervisely 6.73.321__py3-none-any.whl → 6.73.322__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- supervisely/convert/base_converter.py +1 -0
 - supervisely/convert/pointcloud_episodes/__init__.py +1 -0
 - supervisely/convert/pointcloud_episodes/kitti_360/__init__.py +0 -0
 - supervisely/convert/pointcloud_episodes/kitti_360/kitti_360_converter.py +242 -0
 - supervisely/convert/pointcloud_episodes/kitti_360/kitti_360_helper.py +386 -0
 - supervisely/nn/inference/inference.py +155 -1
 - {supervisely-6.73.321.dist-info → supervisely-6.73.322.dist-info}/METADATA +1 -1
 - {supervisely-6.73.321.dist-info → supervisely-6.73.322.dist-info}/RECORD +12 -9
 - {supervisely-6.73.321.dist-info → supervisely-6.73.322.dist-info}/LICENSE +0 -0
 - {supervisely-6.73.321.dist-info → supervisely-6.73.322.dist-info}/WHEEL +0 -0
 - {supervisely-6.73.321.dist-info → supervisely-6.73.322.dist-info}/entry_points.txt +0 -0
 - {supervisely-6.73.321.dist-info → supervisely-6.73.322.dist-info}/top_level.txt +0 -0
 
| 
         @@ -7,3 +7,4 @@ from supervisely.convert.pointcloud_episodes.lyft.lyft_converter import LyftEpis 
     | 
|
| 
       7 
7 
     | 
    
         
             
            from supervisely.convert.pointcloud_episodes.nuscenes_conv.nuscenes_converter import (
         
     | 
| 
       8 
8 
     | 
    
         
             
                NuscenesEpisodesConverter,
         
     | 
| 
       9 
9 
     | 
    
         
             
            )
         
     | 
| 
      
 10 
     | 
    
         
            +
            from supervisely.convert.pointcloud_episodes.kitti_360.kitti_360_converter import KITTI360Converter
         
     | 
| 
         
            File without changes
         
     | 
| 
         @@ -0,0 +1,242 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import os
         
     | 
| 
      
 2 
     | 
    
         
            +
            from pathlib import Path
         
     | 
| 
      
 3 
     | 
    
         
            +
            from typing import Optional, List
         
     | 
| 
      
 4 
     | 
    
         
            +
            from supervisely import PointcloudEpisodeAnnotation, ProjectMeta, is_development, logger, ObjClass, ObjClassCollection
         
     | 
| 
      
 5 
     | 
    
         
            +
            from supervisely.geometry.cuboid_3d import Cuboid3d
         
     | 
| 
      
 6 
     | 
    
         
            +
            from supervisely.api.api import Api, ApiField
         
     | 
| 
      
 7 
     | 
    
         
            +
            from supervisely.convert.base_converter import AvailablePointcloudEpisodesConverters
         
     | 
| 
      
 8 
     | 
    
         
            +
            from supervisely.convert.pointcloud_episodes.kitti_360.kitti_360_helper import *
         
     | 
| 
      
 9 
     | 
    
         
            +
            from supervisely.convert.pointcloud_episodes.pointcloud_episodes_converter import PointcloudEpisodeConverter
         
     | 
| 
      
 10 
     | 
    
         
            +
            from supervisely.io.fs import (
         
     | 
| 
      
 11 
     | 
    
         
            +
                file_exists,
         
     | 
| 
      
 12 
     | 
    
         
            +
                get_file_name,
         
     | 
| 
      
 13 
     | 
    
         
            +
                get_file_name_with_ext,
         
     | 
| 
      
 14 
     | 
    
         
            +
                list_files_recursively,
         
     | 
| 
      
 15 
     | 
    
         
            +
                silent_remove,
         
     | 
| 
      
 16 
     | 
    
         
            +
            )
         
     | 
| 
      
 17 
     | 
    
         
            +
            from supervisely.pointcloud_annotation.pointcloud_episode_frame_collection import PointcloudEpisodeFrameCollection
         
     | 
| 
      
 18 
     | 
    
         
            +
            from supervisely.pointcloud_annotation.pointcloud_episode_object_collection import PointcloudEpisodeObjectCollection
         
     | 
| 
      
 19 
     | 
    
         
            +
            from supervisely.pointcloud_annotation.pointcloud_episode_object import PointcloudEpisodeObject
         
     | 
| 
      
 20 
     | 
    
         
            +
            from supervisely.pointcloud_annotation.pointcloud_episode_frame import PointcloudEpisodeFrame
         
     | 
| 
      
 21 
     | 
    
         
            +
            from supervisely.pointcloud_annotation.pointcloud_figure import PointcloudFigure
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            class KITTI360Converter(PointcloudEpisodeConverter):
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                class Item:
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    def __init__(
         
     | 
| 
      
 28 
     | 
    
         
            +
                        self,
         
     | 
| 
      
 29 
     | 
    
         
            +
                        scene_name: str,
         
     | 
| 
      
 30 
     | 
    
         
            +
                        frame_paths: List[str],
         
     | 
| 
      
 31 
     | 
    
         
            +
                        ann_data: Annotation3D,
         
     | 
| 
      
 32 
     | 
    
         
            +
                        poses_path: str,
         
     | 
| 
      
 33 
     | 
    
         
            +
                        related_images: Optional[tuple] = None,
         
     | 
| 
      
 34 
     | 
    
         
            +
                        custom_data: Optional[dict] = None,
         
     | 
| 
      
 35 
     | 
    
         
            +
                    ):
         
     | 
| 
      
 36 
     | 
    
         
            +
                        self._scene_name = scene_name
         
     | 
| 
      
 37 
     | 
    
         
            +
                        self._frame_paths = frame_paths
         
     | 
| 
      
 38 
     | 
    
         
            +
                        self._ann_data = ann_data
         
     | 
| 
      
 39 
     | 
    
         
            +
                        self._poses_path = poses_path
         
     | 
| 
      
 40 
     | 
    
         
            +
                        self._related_images = related_images or []
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                        self._type = "point_cloud_episode"
         
     | 
| 
      
 43 
     | 
    
         
            +
                        self._custom_data = custom_data if custom_data is not None else {}
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def __init__(self, *args, **kwargs):
         
     | 
| 
      
 46 
     | 
    
         
            +
                    self._calib_path = None
         
     | 
| 
      
 47 
     | 
    
         
            +
                    super().__init__(*args, **kwargs)
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def __str__(self) -> str:
         
     | 
| 
      
 50 
     | 
    
         
            +
                    return AvailablePointcloudEpisodesConverters.KITTI360
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                @property
         
     | 
| 
      
 53 
     | 
    
         
            +
                def key_file_ext(self) -> str:
         
     | 
| 
      
 54 
     | 
    
         
            +
                    return ".bin"
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                def validate_format(self) -> bool:
         
     | 
| 
      
 57 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 58 
     | 
    
         
            +
                        import kitti360scripts
         
     | 
| 
      
 59 
     | 
    
         
            +
                    except ImportError:
         
     | 
| 
      
 60 
     | 
    
         
            +
                        logger.warn("Please run 'pip install kitti360Scripts' to import KITTI-360 data.")
         
     | 
| 
      
 61 
     | 
    
         
            +
                        return False
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    self._items = []
         
     | 
| 
      
 64 
     | 
    
         
            +
                    subdirs = os.listdir(self._input_data)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    if len(subdirs) == 1:
         
     | 
| 
      
 66 
     | 
    
         
            +
                        self._input_data = os.path.join(self._input_data, subdirs[0])
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                    # * Get calibration path
         
     | 
| 
      
 69 
     | 
    
         
            +
                    calib_dir = next(iter([(Path(path).parent).as_posix() for path in list_files_recursively(self._input_data, [".txt"], None, True) if Path(path).stem.startswith("calib")]), None)
         
     | 
| 
      
 70 
     | 
    
         
            +
                    if calib_dir is None:
         
     | 
| 
      
 71 
     | 
    
         
            +
                        return False
         
     | 
| 
      
 72 
     | 
    
         
            +
                    self._calib_path = calib_dir
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    # * Get pointcloud files paths
         
     | 
| 
      
 75 
     | 
    
         
            +
                    velodyne_files = list_files_recursively(self._input_data, [".bin"], None, True)
         
     | 
| 
      
 76 
     | 
    
         
            +
                    if len(velodyne_files) == 0:
         
     | 
| 
      
 77 
     | 
    
         
            +
                        return False
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    # * Get annotation files paths and related images
         
     | 
| 
      
 80 
     | 
    
         
            +
                    boxes_ann_files = list_files_recursively(self._input_data, [".xml"], None, True)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    if len(boxes_ann_files) == 0:
         
     | 
| 
      
 82 
     | 
    
         
            +
                        return False
         
     | 
| 
      
 83 
     | 
    
         
            +
                    rimage_files = list_files_recursively(self._input_data, [".png"], None, True)
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    kitti_anns = []
         
     | 
| 
      
 86 
     | 
    
         
            +
                    for ann_file in boxes_ann_files:
         
     | 
| 
      
 87 
     | 
    
         
            +
                        key_name = Path(ann_file).stem
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                        # * Get pointcloud files
         
     | 
| 
      
 90 
     | 
    
         
            +
                        frame_paths = []
         
     | 
| 
      
 91 
     | 
    
         
            +
                        for path in velodyne_files:
         
     | 
| 
      
 92 
     | 
    
         
            +
                            if key_name in Path(path).parts:
         
     | 
| 
      
 93 
     | 
    
         
            +
                                frame_paths.append(path)
         
     | 
| 
      
 94 
     | 
    
         
            +
                        if len(frame_paths) == 0:
         
     | 
| 
      
 95 
     | 
    
         
            +
                            logger.warn("No frames found for name: %s", key_name)
         
     | 
| 
      
 96 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                        # * Get related images
         
     | 
| 
      
 99 
     | 
    
         
            +
                        rimages = []
         
     | 
| 
      
 100 
     | 
    
         
            +
                        for rimage in rimage_files:
         
     | 
| 
      
 101 
     | 
    
         
            +
                            path = Path(rimage)
         
     | 
| 
      
 102 
     | 
    
         
            +
                            if key_name in path.parts:
         
     | 
| 
      
 103 
     | 
    
         
            +
                                cam_name = path.parts[-3]
         
     | 
| 
      
 104 
     | 
    
         
            +
                                rimages.append((cam_name, rimage))
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                        # * Get poses
         
     | 
| 
      
 107 
     | 
    
         
            +
                        poses_filter = (
         
     | 
| 
      
 108 
     | 
    
         
            +
                            lambda x: x.endswith("cam0_to_world.txt") and key_name in Path(x).parts
         
     | 
| 
      
 109 
     | 
    
         
            +
                        )
         
     | 
| 
      
 110 
     | 
    
         
            +
                        poses_path = next(
         
     | 
| 
      
 111 
     | 
    
         
            +
                            path
         
     | 
| 
      
 112 
     | 
    
         
            +
                            for path in list_files_recursively(self._input_data, [".txt"], None, True)
         
     | 
| 
      
 113 
     | 
    
         
            +
                            if poses_filter(path)
         
     | 
| 
      
 114 
     | 
    
         
            +
                        )
         
     | 
| 
      
 115 
     | 
    
         
            +
                        if poses_path is None:
         
     | 
| 
      
 116 
     | 
    
         
            +
                            logger.warn("No poses found for name: %s", key_name)
         
     | 
| 
      
 117 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                        # * Parse annotation
         
     | 
| 
      
 120 
     | 
    
         
            +
                        ann = Annotation3D(ann_file)
         
     | 
| 
      
 121 
     | 
    
         
            +
                        kitti_anns.append(ann)
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                        self._items.append(
         
     | 
| 
      
 124 
     | 
    
         
            +
                            self.Item(key_name, frame_paths, ann, poses_path, rimages)
         
     | 
| 
      
 125 
     | 
    
         
            +
                        )
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                    # * Get object class names for meta
         
     | 
| 
      
 128 
     | 
    
         
            +
                    obj_class_names = set()
         
     | 
| 
      
 129 
     | 
    
         
            +
                    for ann in kitti_anns:
         
     | 
| 
      
 130 
     | 
    
         
            +
                        for obj in ann.get_objects():
         
     | 
| 
      
 131 
     | 
    
         
            +
                            obj_class_names.add(obj.name)
         
     | 
| 
      
 132 
     | 
    
         
            +
                    obj_classes = [ObjClass(obj_class, Cuboid3d) for obj_class in obj_class_names]
         
     | 
| 
      
 133 
     | 
    
         
            +
                    self._meta = ProjectMeta(obj_classes=ObjClassCollection(obj_classes))
         
     | 
| 
      
 134 
     | 
    
         
            +
                    return self.items_count > 0
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                def to_supervisely(
         
     | 
| 
      
 137 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 138 
     | 
    
         
            +
                    item,
         
     | 
| 
      
 139 
     | 
    
         
            +
                    meta: ProjectMeta,
         
     | 
| 
      
 140 
     | 
    
         
            +
                    renamed_classes: dict = {},
         
     | 
| 
      
 141 
     | 
    
         
            +
                    renamed_tags: dict = {},
         
     | 
| 
      
 142 
     | 
    
         
            +
                    static_transformations: StaticTransformations = None,
         
     | 
| 
      
 143 
     | 
    
         
            +
                ) -> PointcloudEpisodeAnnotation:
         
     | 
| 
      
 144 
     | 
    
         
            +
                    static_transformations.set_cam2world(item._poses_path)
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                    frame_cnt = len(item._frame_paths)
         
     | 
| 
      
 147 
     | 
    
         
            +
                    objs, frames = [], []
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                    frame_idx_to_figures = {idx: [] for idx in range(frame_cnt)}
         
     | 
| 
      
 150 
     | 
    
         
            +
                    for obj in item._ann_data.get_objects():
         
     | 
| 
      
 151 
     | 
    
         
            +
                        pcd_obj = PointcloudEpisodeObject(meta.get_obj_class(obj.name))
         
     | 
| 
      
 152 
     | 
    
         
            +
                        objs.append(pcd_obj)
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                        for idx in range(frame_cnt):
         
     | 
| 
      
 155 
     | 
    
         
            +
                            if obj.start_frame <= idx <= obj.end_frame:
         
     | 
| 
      
 156 
     | 
    
         
            +
                                tr_matrix = static_transformations.world_to_velo_transformation(obj, idx)
         
     | 
| 
      
 157 
     | 
    
         
            +
                                geom = convert_kitti_cuboid_to_supervisely_geometry(tr_matrix)
         
     | 
| 
      
 158 
     | 
    
         
            +
                                frame_idx_to_figures[idx].append(PointcloudFigure(pcd_obj, geom, idx))
         
     | 
| 
      
 159 
     | 
    
         
            +
                    for idx, figures in frame_idx_to_figures.items():
         
     | 
| 
      
 160 
     | 
    
         
            +
                        frame = PointcloudEpisodeFrame(idx, figures)
         
     | 
| 
      
 161 
     | 
    
         
            +
                        frames.append(frame)
         
     | 
| 
      
 162 
     | 
    
         
            +
                    obj_collection = PointcloudEpisodeObjectCollection(objs)
         
     | 
| 
      
 163 
     | 
    
         
            +
                    frame_collection = PointcloudEpisodeFrameCollection(frames)
         
     | 
| 
      
 164 
     | 
    
         
            +
                    return PointcloudEpisodeAnnotation(
         
     | 
| 
      
 165 
     | 
    
         
            +
                        frame_cnt, objects=obj_collection, frames=frame_collection
         
     | 
| 
      
 166 
     | 
    
         
            +
                    )
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                def upload_dataset(self, api: Api, dataset_id: int, batch_size: int = 1, log_progress=True):
         
     | 
| 
      
 169 
     | 
    
         
            +
                    meta, renamed_classes, renamed_tags = self.merge_metas_with_conflicts(api, dataset_id)
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                    dataset_info = api.dataset.get_info_by_id(dataset_id)
         
     | 
| 
      
 172 
     | 
    
         
            +
                    if log_progress:
         
     | 
| 
      
 173 
     | 
    
         
            +
                        progress, progress_cb = self.get_progress(sum([len(item._frame_paths) for item in self._items]), "Converting pointcloud episodes...")
         
     | 
| 
      
 174 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 175 
     | 
    
         
            +
                        progress_cb = None
         
     | 
| 
      
 176 
     | 
    
         
            +
                    static_transformations = StaticTransformations(self._calib_path)
         
     | 
| 
      
 177 
     | 
    
         
            +
                    scene_ds = dataset_info
         
     | 
| 
      
 178 
     | 
    
         
            +
                    multiple_items = self.items_count > 1
         
     | 
| 
      
 179 
     | 
    
         
            +
                    for item in self._items:
         
     | 
| 
      
 180 
     | 
    
         
            +
                        scene_ds = api.dataset.create(dataset_info.project_id, item._scene_name, parent_id=dataset_id) if multiple_items else dataset_info
         
     | 
| 
      
 181 
     | 
    
         
            +
                        frame_to_pcd_ids = {}
         
     | 
| 
      
 182 
     | 
    
         
            +
                        for idx, frame_path in enumerate(item._frame_paths):
         
     | 
| 
      
 183 
     | 
    
         
            +
                            # * Convert pointcloud from ".bin" to ".pcd"
         
     | 
| 
      
 184 
     | 
    
         
            +
                            pcd_path = str(Path(frame_path).with_suffix(".pcd"))
         
     | 
| 
      
 185 
     | 
    
         
            +
                            if file_exists(pcd_path):
         
     | 
| 
      
 186 
     | 
    
         
            +
                                logger.warning(f"Overwriting file with path: {pcd_path}")
         
     | 
| 
      
 187 
     | 
    
         
            +
                            convert_bin_to_pcd(frame_path, pcd_path)
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
                            # * Upload pointcloud
         
     | 
| 
      
 190 
     | 
    
         
            +
                            pcd_name = get_file_name_with_ext(pcd_path)
         
     | 
| 
      
 191 
     | 
    
         
            +
                            info = api.pointcloud_episode.upload_path(scene_ds.id, pcd_name, pcd_path, {"frame": idx})
         
     | 
| 
      
 192 
     | 
    
         
            +
                            pcd_id = info.id
         
     | 
| 
      
 193 
     | 
    
         
            +
                            frame_to_pcd_ids[idx] = pcd_id
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
                            # * Clean up
         
     | 
| 
      
 196 
     | 
    
         
            +
                            silent_remove(pcd_path)
         
     | 
| 
      
 197 
     | 
    
         
            +
             
     | 
| 
      
 198 
     | 
    
         
            +
                            if log_progress:
         
     | 
| 
      
 199 
     | 
    
         
            +
                                progress_cb(1)
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
                        # * Upload photocontext
         
     | 
| 
      
 202 
     | 
    
         
            +
                        rimage_jsons = []
         
     | 
| 
      
 203 
     | 
    
         
            +
                        cam_names = []
         
     | 
| 
      
 204 
     | 
    
         
            +
                        hashes = api.pointcloud_episode.upload_related_images(
         
     | 
| 
      
 205 
     | 
    
         
            +
                            [rimage_path for _, rimage_path in item._related_images]
         
     | 
| 
      
 206 
     | 
    
         
            +
                        )
         
     | 
| 
      
 207 
     | 
    
         
            +
                        for (cam_name, rimage_path), img, pcd_id in zip(
         
     | 
| 
      
 208 
     | 
    
         
            +
                            item._related_images, hashes, list(frame_to_pcd_ids.values())
         
     | 
| 
      
 209 
     | 
    
         
            +
                        ):
         
     | 
| 
      
 210 
     | 
    
         
            +
                            cam_num = int(cam_name[-1])
         
     | 
| 
      
 211 
     | 
    
         
            +
                            rimage_info = convert_calib_to_image_meta(
         
     | 
| 
      
 212 
     | 
    
         
            +
                                get_file_name(rimage_path), static_transformations, cam_num
         
     | 
| 
      
 213 
     | 
    
         
            +
                            )
         
     | 
| 
      
 214 
     | 
    
         
            +
                            image_json = {
         
     | 
| 
      
 215 
     | 
    
         
            +
                                ApiField.ENTITY_ID: pcd_id,
         
     | 
| 
      
 216 
     | 
    
         
            +
                                ApiField.NAME: cam_name,
         
     | 
| 
      
 217 
     | 
    
         
            +
                                ApiField.HASH: img,
         
     | 
| 
      
 218 
     | 
    
         
            +
                                ApiField.META: rimage_info[ApiField.META],
         
     | 
| 
      
 219 
     | 
    
         
            +
                            }
         
     | 
| 
      
 220 
     | 
    
         
            +
                            rimage_jsons.append(image_json)
         
     | 
| 
      
 221 
     | 
    
         
            +
                            cam_names.append(cam_name)
         
     | 
| 
      
 222 
     | 
    
         
            +
                        if rimage_jsons:
         
     | 
| 
      
 223 
     | 
    
         
            +
                            api.pointcloud_episode.add_related_images(rimage_jsons, cam_names)
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
                        # * Convert annotation and upload
         
     | 
| 
      
 226 
     | 
    
         
            +
                        try:
         
     | 
| 
      
 227 
     | 
    
         
            +
                            ann = self.to_supervisely(
         
     | 
| 
      
 228 
     | 
    
         
            +
                                item, meta, renamed_classes, renamed_tags, static_transformations
         
     | 
| 
      
 229 
     | 
    
         
            +
                            )
         
     | 
| 
      
 230 
     | 
    
         
            +
                            api.pointcloud_episode.annotation.append(scene_ds.id, ann, frame_to_pcd_ids)
         
     | 
| 
      
 231 
     | 
    
         
            +
                        except Exception as e:
         
     | 
| 
      
 232 
     | 
    
         
            +
                            logger.error(
         
     | 
| 
      
 233 
     | 
    
         
            +
                                f"Failed to upload annotation for scene: {scene_ds.name}. Error: {repr(e)}",
         
     | 
| 
      
 234 
     | 
    
         
            +
                                stack_info=False,
         
     | 
| 
      
 235 
     | 
    
         
            +
                            )
         
     | 
| 
      
 236 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 237 
     | 
    
         
            +
             
     | 
| 
      
 238 
     | 
    
         
            +
                        logger.info(f"Dataset ID:{scene_ds.id} has been successfully uploaded.")
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
                    if log_progress:
         
     | 
| 
      
 241 
     | 
    
         
            +
                        if is_development():
         
     | 
| 
      
 242 
     | 
    
         
            +
                            progress.close()
         
     | 
| 
         @@ -0,0 +1,386 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            from supervisely import logger
         
     | 
| 
      
 2 
     | 
    
         
            +
            from supervisely.io.fs import get_file_name
         
     | 
| 
      
 3 
     | 
    
         
            +
            from supervisely.geometry.cuboid_3d import Cuboid3d
         
     | 
| 
      
 4 
     | 
    
         
            +
            from supervisely.geometry.point_3d import Vector3d
         
     | 
| 
      
 5 
     | 
    
         
            +
            from supervisely.geometry.point import Point
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            from collections import defaultdict
         
     | 
| 
      
 8 
     | 
    
         
            +
            import os
         
     | 
| 
      
 9 
     | 
    
         
            +
            import numpy as np
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            MAX_N = 1000
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            def local2global(semanticId, instanceId):
         
     | 
| 
      
 16 
     | 
    
         
            +
                globalId = semanticId * MAX_N + instanceId
         
     | 
| 
      
 17 
     | 
    
         
            +
                if isinstance(globalId, np.ndarray):
         
     | 
| 
      
 18 
     | 
    
         
            +
                    return globalId.astype(np.int)
         
     | 
| 
      
 19 
     | 
    
         
            +
                else:
         
     | 
| 
      
 20 
     | 
    
         
            +
                    return int(globalId)
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            def global2local(globalId):
         
     | 
| 
      
 24 
     | 
    
         
            +
                semanticId = globalId // MAX_N
         
     | 
| 
      
 25 
     | 
    
         
            +
                instanceId = globalId % MAX_N
         
     | 
| 
      
 26 
     | 
    
         
            +
                if isinstance(globalId, np.ndarray):
         
     | 
| 
      
 27 
     | 
    
         
            +
                    return semanticId.astype(int), instanceId.astype(int)
         
     | 
| 
      
 28 
     | 
    
         
            +
                else:
         
     | 
| 
      
 29 
     | 
    
         
            +
                    return int(semanticId), int(instanceId)
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            annotation2global = defaultdict()
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            # Abstract base class for annotation objects
         
     | 
| 
      
 36 
     | 
    
         
            +
            class KITTI360Object:
         
     | 
| 
      
 37 
     | 
    
         
            +
                from abc import ABCMeta
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                __metaclass__ = ABCMeta
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def __init__(self):
         
     | 
| 
      
 42 
     | 
    
         
            +
                    from matplotlib import cm
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    # the label
         
     | 
| 
      
 45 
     | 
    
         
            +
                    self.label = ""
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    # colormap
         
     | 
| 
      
 48 
     | 
    
         
            +
                    self.cmap = cm.get_cmap("Set1")
         
     | 
| 
      
 49 
     | 
    
         
            +
                    self.cmap_length = 9
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                def getColor(self, idx):
         
     | 
| 
      
 52 
     | 
    
         
            +
                    if idx == 0:
         
     | 
| 
      
 53 
     | 
    
         
            +
                        return np.array([0, 0, 0])
         
     | 
| 
      
 54 
     | 
    
         
            +
                    return np.asarray(self.cmap(idx % self.cmap_length)[:3]) * 255.0
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                # def assignColor(self):
         
     | 
| 
      
 57 
     | 
    
         
            +
                #     from kitti360scripts.helpers.labels import id2label  # pylint: disable=import-error
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                #     if self.semanticId >= 0:
         
     | 
| 
      
 60 
     | 
    
         
            +
                #         self.semanticColor = id2label[self.semanticId].color
         
     | 
| 
      
 61 
     | 
    
         
            +
                #         if self.instanceId > 0:
         
     | 
| 
      
 62 
     | 
    
         
            +
                #             self.instanceColor = self.getColor(self.instanceId)
         
     | 
| 
      
 63 
     | 
    
         
            +
                #         else:
         
     | 
| 
      
 64 
     | 
    
         
            +
                #             self.instanceColor = self.semanticColor
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
            # Class that contains the information of a single annotated object as 3D bounding box
         
     | 
| 
      
 68 
     | 
    
         
            +
            class KITTI360Bbox3D(KITTI360Object):
         
     | 
| 
      
 69 
     | 
    
         
            +
                # Constructor
         
     | 
| 
      
 70 
     | 
    
         
            +
                def __init__(self):
         
     | 
| 
      
 71 
     | 
    
         
            +
                    KITTI360Object.__init__(self)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    # the polygon as list of points
         
     | 
| 
      
 73 
     | 
    
         
            +
                    self.vertices = []
         
     | 
| 
      
 74 
     | 
    
         
            +
                    self.faces = []
         
     | 
| 
      
 75 
     | 
    
         
            +
                    self.lines = [
         
     | 
| 
      
 76 
     | 
    
         
            +
                        [0, 5],
         
     | 
| 
      
 77 
     | 
    
         
            +
                        [1, 4],
         
     | 
| 
      
 78 
     | 
    
         
            +
                        [2, 7],
         
     | 
| 
      
 79 
     | 
    
         
            +
                        [3, 6],
         
     | 
| 
      
 80 
     | 
    
         
            +
                        [0, 1],
         
     | 
| 
      
 81 
     | 
    
         
            +
                        [1, 3],
         
     | 
| 
      
 82 
     | 
    
         
            +
                        [3, 2],
         
     | 
| 
      
 83 
     | 
    
         
            +
                        [2, 0],
         
     | 
| 
      
 84 
     | 
    
         
            +
                        [4, 5],
         
     | 
| 
      
 85 
     | 
    
         
            +
                        [5, 7],
         
     | 
| 
      
 86 
     | 
    
         
            +
                        [7, 6],
         
     | 
| 
      
 87 
     | 
    
         
            +
                        [6, 4],
         
     | 
| 
      
 88 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                    # the ID of the corresponding object
         
     | 
| 
      
 91 
     | 
    
         
            +
                    self.semanticId = -1
         
     | 
| 
      
 92 
     | 
    
         
            +
                    self.instanceId = -1
         
     | 
| 
      
 93 
     | 
    
         
            +
                    self.annotationId = -1
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                    # the window that contains the bbox
         
     | 
| 
      
 96 
     | 
    
         
            +
                    self.start_frame = -1
         
     | 
| 
      
 97 
     | 
    
         
            +
                    self.end_frame = -1
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                    # timestamp of the bbox (-1 if statis)
         
     | 
| 
      
 100 
     | 
    
         
            +
                    self.timestamp = -1
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
                    # projected vertices
         
     | 
| 
      
 103 
     | 
    
         
            +
                    self.vertices_proj = None
         
     | 
| 
      
 104 
     | 
    
         
            +
                    self.meshes = []
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                    # name
         
     | 
| 
      
 107 
     | 
    
         
            +
                    self.name = ""
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                def __str__(self):
         
     | 
| 
      
 110 
     | 
    
         
            +
                    return self.name
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                # def generateMeshes(self):
         
     | 
| 
      
 113 
     | 
    
         
            +
                #     self.meshes = []
         
     | 
| 
      
 114 
     | 
    
         
            +
                #     if self.vertices_proj:
         
     | 
| 
      
 115 
     | 
    
         
            +
                #         for fidx in range(self.faces.shape[0]):
         
     | 
| 
      
 116 
     | 
    
         
            +
                #             self.meshes.append(
         
     | 
| 
      
 117 
     | 
    
         
            +
                #                 [
         
     | 
| 
      
 118 
     | 
    
         
            +
                #                     Point(self.vertices_proj[0][int(x)], self.vertices_proj[1][int(x)])
         
     | 
| 
      
 119 
     | 
    
         
            +
                #                     for x in self.faces[fidx]
         
     | 
| 
      
 120 
     | 
    
         
            +
                #                 ]
         
     | 
| 
      
 121 
     | 
    
         
            +
                #             )
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                def parseOpencvMatrix(self, node):
         
     | 
| 
      
 124 
     | 
    
         
            +
                    rows = int(node.find("rows").text)
         
     | 
| 
      
 125 
     | 
    
         
            +
                    cols = int(node.find("cols").text)
         
     | 
| 
      
 126 
     | 
    
         
            +
                    data = node.find("data").text.split(" ")
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                    mat = []
         
     | 
| 
      
 129 
     | 
    
         
            +
                    for d in data:
         
     | 
| 
      
 130 
     | 
    
         
            +
                        d = d.replace("\n", "")
         
     | 
| 
      
 131 
     | 
    
         
            +
                        if len(d) < 1:
         
     | 
| 
      
 132 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 133 
     | 
    
         
            +
                        mat.append(float(d))
         
     | 
| 
      
 134 
     | 
    
         
            +
                    mat = np.reshape(mat, [rows, cols])
         
     | 
| 
      
 135 
     | 
    
         
            +
                    return mat
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                def parseVertices(self, child):
         
     | 
| 
      
 138 
     | 
    
         
            +
                    transform = self.parseOpencvMatrix(child.find("transform"))
         
     | 
| 
      
 139 
     | 
    
         
            +
                    R = transform[:3, :3]
         
     | 
| 
      
 140 
     | 
    
         
            +
                    T = transform[:3, 3]
         
     | 
| 
      
 141 
     | 
    
         
            +
                    vertices = self.parseOpencvMatrix(child.find("vertices"))
         
     | 
| 
      
 142 
     | 
    
         
            +
                    faces = self.parseOpencvMatrix(child.find("faces"))
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                    vertices = np.matmul(R, vertices.transpose()).transpose() + T
         
     | 
| 
      
 145 
     | 
    
         
            +
                    self.vertices = vertices
         
     | 
| 
      
 146 
     | 
    
         
            +
                    self.faces = faces
         
     | 
| 
      
 147 
     | 
    
         
            +
                    self.R = R
         
     | 
| 
      
 148 
     | 
    
         
            +
                    self.T = T
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                    self.transform = transform
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                def parseBbox(self, child):
         
     | 
| 
      
 153 
     | 
    
         
            +
                    from kitti360scripts.helpers.labels import kittiId2label  # pylint: disable=import-error
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                    semanticIdKITTI = int(child.find("semanticId").text)
         
     | 
| 
      
 156 
     | 
    
         
            +
                    self.semanticId = kittiId2label[semanticIdKITTI].id
         
     | 
| 
      
 157 
     | 
    
         
            +
                    self.instanceId = int(child.find("instanceId").text)
         
     | 
| 
      
 158 
     | 
    
         
            +
                    # self.name = str(child.find('label').text)
         
     | 
| 
      
 159 
     | 
    
         
            +
                    self.name = kittiId2label[semanticIdKITTI].name
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                    self.start_frame = int(child.find("start_frame").text)
         
     | 
| 
      
 162 
     | 
    
         
            +
                    self.end_frame = int(child.find("end_frame").text)
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                    self.timestamp = int(child.find("timestamp").text)
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                    self.annotationId = int(child.find("index").text) + 1
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                    global annotation2global
         
     | 
| 
      
 169 
     | 
    
         
            +
                    annotation2global[self.annotationId] = local2global(self.semanticId, self.instanceId)
         
     | 
| 
      
 170 
     | 
    
         
            +
                    self.parseVertices(child)
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                def parseStuff(self, child):
         
     | 
| 
      
 173 
     | 
    
         
            +
                    from kitti360scripts.helpers.labels import name2label  # pylint: disable=import-error
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                    classmap = {
         
     | 
| 
      
 176 
     | 
    
         
            +
                        "driveway": "parking",
         
     | 
| 
      
 177 
     | 
    
         
            +
                        "ground": "terrain",
         
     | 
| 
      
 178 
     | 
    
         
            +
                        "unknownGround": "ground",
         
     | 
| 
      
 179 
     | 
    
         
            +
                        "railtrack": "rail track",
         
     | 
| 
      
 180 
     | 
    
         
            +
                    }
         
     | 
| 
      
 181 
     | 
    
         
            +
                    label = child.find("label").text
         
     | 
| 
      
 182 
     | 
    
         
            +
                    if label in classmap.keys():
         
     | 
| 
      
 183 
     | 
    
         
            +
                        label = classmap[label]
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
                    self.start_frame = int(child.find("start_frame").text)
         
     | 
| 
      
 186 
     | 
    
         
            +
                    self.end_frame = int(child.find("end_frame").text)
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                    self.semanticId = name2label[label].id
         
     | 
| 
      
 189 
     | 
    
         
            +
                    self.instanceId = 0
         
     | 
| 
      
 190 
     | 
    
         
            +
                    self.parseVertices(child)
         
     | 
| 
      
 191 
     | 
    
         
            +
             
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
            # Class that contains the information of the point cloud a single frame
         
     | 
| 
      
 194 
     | 
    
         
            +
            class KITTI360Point3D(KITTI360Object):
         
     | 
| 
      
 195 
     | 
    
         
            +
                # Constructor
         
     | 
| 
      
 196 
     | 
    
         
            +
                def __init__(self):
         
     | 
| 
      
 197 
     | 
    
         
            +
                    KITTI360Object.__init__(self)
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
                    self.vertices = []
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
                    self.vertices_proj = None
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
                    # the ID of the corresponding object
         
     | 
| 
      
 204 
     | 
    
         
            +
                    self.semanticId = -1
         
     | 
| 
      
 205 
     | 
    
         
            +
                    self.instanceId = -1
         
     | 
| 
      
 206 
     | 
    
         
            +
                    self.annotationId = -1
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
                    # name
         
     | 
| 
      
 209 
     | 
    
         
            +
                    self.name = ""
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
                    # color
         
     | 
| 
      
 212 
     | 
    
         
            +
                    self.semanticColor = None
         
     | 
| 
      
 213 
     | 
    
         
            +
                    self.instanceColor = None
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
                def __str__(self):
         
     | 
| 
      
 216 
     | 
    
         
            +
                    return self.name
         
     | 
| 
      
 217 
     | 
    
         
            +
             
     | 
| 
      
 218 
     | 
    
         
            +
                # def generateMeshes(self):
         
     | 
| 
      
 219 
     | 
    
         
            +
                #     pass
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
            # Meta class for KITTI360Bbox3D
         
     | 
| 
      
 223 
     | 
    
         
            +
            class Annotation3D:
         
     | 
| 
      
 224 
     | 
    
         
            +
                def __init__(self, labelPath):
         
     | 
| 
      
 225 
     | 
    
         
            +
                    from kitti360scripts.helpers.labels import labels  # pylint: disable=import-error
         
     | 
| 
      
 226 
     | 
    
         
            +
                    import xml.etree.ElementTree as ET
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
                    key_name = get_file_name(labelPath)
         
     | 
| 
      
 229 
     | 
    
         
            +
                    # load annotation
         
     | 
| 
      
 230 
     | 
    
         
            +
                    tree = ET.parse(labelPath)
         
     | 
| 
      
 231 
     | 
    
         
            +
                    root = tree.getroot()
         
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
      
 233 
     | 
    
         
            +
                    self.objects = defaultdict(dict)
         
     | 
| 
      
 234 
     | 
    
         
            +
             
     | 
| 
      
 235 
     | 
    
         
            +
                    self.num_bbox = 0
         
     | 
| 
      
 236 
     | 
    
         
            +
                    for child in root:
         
     | 
| 
      
 237 
     | 
    
         
            +
                        if child.find("transform") is None:
         
     | 
| 
      
 238 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 239 
     | 
    
         
            +
                        obj = KITTI360Bbox3D()
         
     | 
| 
      
 240 
     | 
    
         
            +
                        obj.parseBbox(child)
         
     | 
| 
      
 241 
     | 
    
         
            +
                        globalId = local2global(obj.semanticId, obj.instanceId)
         
     | 
| 
      
 242 
     | 
    
         
            +
                        self.objects[globalId][obj.timestamp] = obj
         
     | 
| 
      
 243 
     | 
    
         
            +
                        self.num_bbox += 1
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
      
 245 
     | 
    
         
            +
                    globalIds = np.asarray(list(self.objects.keys()))
         
     | 
| 
      
 246 
     | 
    
         
            +
                    semanticIds, instanceIds = global2local(globalIds)
         
     | 
| 
      
 247 
     | 
    
         
            +
                    for label in labels:
         
     | 
| 
      
 248 
     | 
    
         
            +
                        if label.hasInstances:
         
     | 
| 
      
 249 
     | 
    
         
            +
                            print(f"{label.name:<30}:\t {(semanticIds==label.id).sum()}")
         
     | 
| 
      
 250 
     | 
    
         
            +
                    print(f"Loaded {len(globalIds)} instances")
         
     | 
| 
      
 251 
     | 
    
         
            +
                    print(f"Loaded {self.num_bbox} boxes")
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
                def __call__(self, semanticId, instanceId, timestamp=None):
         
     | 
| 
      
 254 
     | 
    
         
            +
                    globalId = local2global(semanticId, instanceId)
         
     | 
| 
      
 255 
     | 
    
         
            +
                    if globalId in self.objects.keys():
         
     | 
| 
      
 256 
     | 
    
         
            +
                        # static object
         
     | 
| 
      
 257 
     | 
    
         
            +
                        if len(self.objects[globalId].keys()) == 1:
         
     | 
| 
      
 258 
     | 
    
         
            +
                            if -1 in self.objects[globalId].keys():
         
     | 
| 
      
 259 
     | 
    
         
            +
                                return self.objects[globalId][-1]
         
     | 
| 
      
 260 
     | 
    
         
            +
                            else:
         
     | 
| 
      
 261 
     | 
    
         
            +
                                return None
         
     | 
| 
      
 262 
     | 
    
         
            +
                        # dynamic object
         
     | 
| 
      
 263 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 264 
     | 
    
         
            +
                            return self.objects[globalId][timestamp]
         
     | 
| 
      
 265 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 266 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 267 
     | 
    
         
            +
             
     | 
| 
      
 268 
     | 
    
         
            +
                def get_objects(self):
         
     | 
| 
      
 269 
     | 
    
         
            +
                    return [list(obj.values())[0] for obj in self.objects.values()]
         
     | 
| 
      
 270 
     | 
    
         
            +
             
     | 
| 
      
 271 
     | 
    
         
            +
            class StaticTransformations:
         
     | 
| 
      
 272 
     | 
    
         
            +
                def __init__(self, calibrations_path):
         
     | 
| 
      
 273 
     | 
    
         
            +
                    import kitti360scripts.devkits.commons.loadCalibration as lc  # pylint: disable=import-error
         
     | 
| 
      
 274 
     | 
    
         
            +
             
     | 
| 
      
 275 
     | 
    
         
            +
                    cam2velo_path = os.path.join(calibrations_path, "calib_cam_to_velo.txt")
         
     | 
| 
      
 276 
     | 
    
         
            +
                    self.cam2velo = lc.loadCalibrationRigid(cam2velo_path)
         
     | 
| 
      
 277 
     | 
    
         
            +
                    perspective_path = os.path.join(calibrations_path, "perspective.txt")
         
     | 
| 
      
 278 
     | 
    
         
            +
                    self.intrinsic_calibrations = lc.loadPerspectiveIntrinsic(perspective_path)
         
     | 
| 
      
 279 
     | 
    
         
            +
                    self.cam2world = None
         
     | 
| 
      
 280 
     | 
    
         
            +
             
     | 
| 
      
 281 
     | 
    
         
            +
                def set_cam2world(self, cam2world_path):
         
     | 
| 
      
 282 
     | 
    
         
            +
                    if not os.path.isfile(cam2world_path):
         
     | 
| 
      
 283 
     | 
    
         
            +
                        logger.warn("Camera to world calibration file was not found")
         
     | 
| 
      
 284 
     | 
    
         
            +
                        return
         
     | 
| 
      
 285 
     | 
    
         
            +
             
     | 
| 
      
 286 
     | 
    
         
            +
                    cam2world_rows = np.loadtxt(cam2world_path)
         
     | 
| 
      
 287 
     | 
    
         
            +
                    cam2world_rigid = np.reshape(cam2world_rows[:, 1:], (-1, 4, 4))
         
     | 
| 
      
 288 
     | 
    
         
            +
                    frames_numbers = list(np.reshape(cam2world_rows[:, :1], (-1)).astype(int))
         
     | 
| 
      
 289 
     | 
    
         
            +
                    cam2world = {}
         
     | 
| 
      
 290 
     | 
    
         
            +
             
     | 
| 
      
 291 
     | 
    
         
            +
                    current_rigid = cam2world_rigid[0]
         
     | 
| 
      
 292 
     | 
    
         
            +
             
     | 
| 
      
 293 
     | 
    
         
            +
                    for frame_index in range(0, frames_numbers[-1]):
         
     | 
| 
      
 294 
     | 
    
         
            +
                        if frame_index in frames_numbers:
         
     | 
| 
      
 295 
     | 
    
         
            +
                            mapped_index = frames_numbers.index(frame_index)
         
     | 
| 
      
 296 
     | 
    
         
            +
                            current_rigid = cam2world_rigid[mapped_index]
         
     | 
| 
      
 297 
     | 
    
         
            +
             
     | 
| 
      
 298 
     | 
    
         
            +
                        # (Tr(cam -> world))
         
     | 
| 
      
 299 
     | 
    
         
            +
                        cam2world[frame_index] = current_rigid
         
     | 
| 
      
 300 
     | 
    
         
            +
                    self.cam2world = cam2world
         
     | 
| 
      
 301 
     | 
    
         
            +
             
     | 
| 
      
 302 
     | 
    
         
            +
                def world_to_velo_transformation(self, obj, frame_index):
         
     | 
| 
      
 303 
     | 
    
         
            +
                    # rotate_z = Rotation.from_rotvec(np.pi * np.array([0, 0, 1])).as_matrix()
         
     | 
| 
      
 304 
     | 
    
         
            +
                    # rotate_z = np.hstack((rotate_z, np.asarray([[0, 0, 0]]).T))
         
     | 
| 
      
 305 
     | 
    
         
            +
             
     | 
| 
      
 306 
     | 
    
         
            +
                    # tr0(local -> fixed_coordinates_local)
         
     | 
| 
      
 307 
     | 
    
         
            +
                    tr0 = np.asarray([[0, -1, 0, 0], [1, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])
         
     | 
| 
      
 308 
     | 
    
         
            +
             
     | 
| 
      
 309 
     | 
    
         
            +
                    # tr0(fixed_coordinates_local -> world)
         
     | 
| 
      
 310 
     | 
    
         
            +
                    tr1 = obj.transform
         
     | 
| 
      
 311 
     | 
    
         
            +
             
     | 
| 
      
 312 
     | 
    
         
            +
                    # tr2(world -> cam)
         
     | 
| 
      
 313 
     | 
    
         
            +
                    tr2 = np.linalg.inv(self.cam2world[frame_index])
         
     | 
| 
      
 314 
     | 
    
         
            +
             
     | 
| 
      
 315 
     | 
    
         
            +
                    # tr3(world -> cam)
         
     | 
| 
      
 316 
     | 
    
         
            +
                    tr3 = self.cam2velo
         
     | 
| 
      
 317 
     | 
    
         
            +
             
     | 
| 
      
 318 
     | 
    
         
            +
                    return tr3 @ tr2 @ tr1 @ tr0
         
     | 
| 
      
 319 
     | 
    
         
            +
             
     | 
| 
      
 320 
     | 
    
         
            +
                def get_extrinsic_matrix(self):
         
     | 
| 
      
 321 
     | 
    
         
            +
                    return np.linalg.inv(self.cam2velo)[:3, :4]
         
     | 
| 
      
 322 
     | 
    
         
            +
             
     | 
| 
      
 323 
     | 
    
         
            +
                def get_intrinsics_matrix(self, camera_num):
         
     | 
| 
      
 324 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 325 
     | 
    
         
            +
                        matrix = self.intrinsic_calibrations[f"P_rect_0{camera_num}"][:3, :3]
         
     | 
| 
      
 326 
     | 
    
         
            +
                        return matrix
         
     | 
| 
      
 327 
     | 
    
         
            +
                    except KeyError:
         
     | 
| 
      
 328 
     | 
    
         
            +
                        logger.warn(f"Camera {camera_num} intrinsic matrix was not found")
         
     | 
| 
      
 329 
     | 
    
         
            +
                    return
         
     | 
| 
      
 330 
     | 
    
         
            +
             
     | 
| 
      
 331 
     | 
    
         
            +
            def convert_kitti_cuboid_to_supervisely_geometry(tr_matrix):
         
     | 
| 
      
 332 
     | 
    
         
            +
                import transforms3d  # pylint: disable=import-error
         
     | 
| 
      
 333 
     | 
    
         
            +
                from scipy.spatial.transform.rotation import Rotation
         
     | 
| 
      
 334 
     | 
    
         
            +
             
     | 
| 
      
 335 
     | 
    
         
            +
                Tdash, Rdash, Zdash, _ = transforms3d.affines.decompose44(tr_matrix)
         
     | 
| 
      
 336 
     | 
    
         
            +
             
     | 
| 
      
 337 
     | 
    
         
            +
                x, y, z = Tdash[0], Tdash[1], Tdash[2]
         
     | 
| 
      
 338 
     | 
    
         
            +
                position = Vector3d(x, y, z)
         
     | 
| 
      
 339 
     | 
    
         
            +
             
     | 
| 
      
 340 
     | 
    
         
            +
                rotation_angles = Rotation.from_matrix(Rdash).as_euler("xyz", degrees=False)
         
     | 
| 
      
 341 
     | 
    
         
            +
                r_x, r_y, r_z = rotation_angles[0], rotation_angles[1], rotation_angles[2]
         
     | 
| 
      
 342 
     | 
    
         
            +
             
     | 
| 
      
 343 
     | 
    
         
            +
                # Invert the bbox by adding π to the yaw while maintaining its degree relative to the world
         
     | 
| 
      
 344 
     | 
    
         
            +
                rotation = Vector3d(r_x, r_y, r_z + np.pi)
         
     | 
| 
      
 345 
     | 
    
         
            +
             
     | 
| 
      
 346 
     | 
    
         
            +
                w, h, l = Zdash[0], Zdash[1], Zdash[2]
         
     | 
| 
      
 347 
     | 
    
         
            +
                dimension = Vector3d(w, h, l)
         
     | 
| 
      
 348 
     | 
    
         
            +
             
     | 
| 
      
 349 
     | 
    
         
            +
                return Cuboid3d(position, rotation, dimension)
         
     | 
| 
      
 350 
     | 
    
         
            +
             
     | 
| 
      
 351 
     | 
    
         
            +
            def convert_bin_to_pcd(src, dst):
         
     | 
| 
      
 352 
     | 
    
         
            +
                import open3d as o3d  # pylint: disable=import-error
         
     | 
| 
      
 353 
     | 
    
         
            +
             
     | 
| 
      
 354 
     | 
    
         
            +
                try:
         
     | 
| 
      
 355 
     | 
    
         
            +
                    bin = np.fromfile(src, dtype=np.float32).reshape(-1, 4)
         
     | 
| 
      
 356 
     | 
    
         
            +
                except ValueError as e:
         
     | 
| 
      
 357 
     | 
    
         
            +
                    raise Exception(
         
     | 
| 
      
 358 
     | 
    
         
            +
                        f"Incorrect data in the KITTI 3D pointcloud file: {src}. "
         
     | 
| 
      
 359 
     | 
    
         
            +
                        f"There was an error while trying to reshape the data into a 4-column matrix: {e}. "
         
     | 
| 
      
 360 
     | 
    
         
            +
                        "Please ensure that the binary file contains a multiple of 4 elements to be "
         
     | 
| 
      
 361 
     | 
    
         
            +
                        "successfully reshaped into a (N, 4) array.\n"
         
     | 
| 
      
 362 
     | 
    
         
            +
                    )
         
     | 
| 
      
 363 
     | 
    
         
            +
                points = bin[:, 0:3]
         
     | 
| 
      
 364 
     | 
    
         
            +
                intensity = bin[:, -1]
         
     | 
| 
      
 365 
     | 
    
         
            +
                intensity_fake_rgb = np.zeros((intensity.shape[0], 3))
         
     | 
| 
      
 366 
     | 
    
         
            +
                intensity_fake_rgb[:, 0] = intensity
         
     | 
| 
      
 367 
     | 
    
         
            +
                pc = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(points))
         
     | 
| 
      
 368 
     | 
    
         
            +
                pc.colors = o3d.utility.Vector3dVector(intensity_fake_rgb)
         
     | 
| 
      
 369 
     | 
    
         
            +
                o3d.io.write_point_cloud(dst, pc)
         
     | 
| 
      
 370 
     | 
    
         
            +
             
     | 
| 
      
 371 
     | 
    
         
            +
             
     | 
| 
      
 372 
     | 
    
         
            +
            def convert_calib_to_image_meta(image_name, static, cam_num):
         
     | 
| 
      
 373 
     | 
    
         
            +
                intrinsic_matrix = static.get_intrinsics_matrix(cam_num)
         
     | 
| 
      
 374 
     | 
    
         
            +
                extrinsic_matrix = static.get_extrinsic_matrix()
         
     | 
| 
      
 375 
     | 
    
         
            +
             
     | 
| 
      
 376 
     | 
    
         
            +
                data = {
         
     | 
| 
      
 377 
     | 
    
         
            +
                    "name": image_name,
         
     | 
| 
      
 378 
     | 
    
         
            +
                    "meta": {
         
     | 
| 
      
 379 
     | 
    
         
            +
                        "deviceId": cam_num,
         
     | 
| 
      
 380 
     | 
    
         
            +
                        "sensorsData": {
         
     | 
| 
      
 381 
     | 
    
         
            +
                            "extrinsicMatrix": list(extrinsic_matrix.flatten().astype(float)),
         
     | 
| 
      
 382 
     | 
    
         
            +
                            "intrinsicMatrix": list(intrinsic_matrix.flatten().astype(float)),
         
     | 
| 
      
 383 
     | 
    
         
            +
                        },
         
     | 
| 
      
 384 
     | 
    
         
            +
                    },
         
     | 
| 
      
 385 
     | 
    
         
            +
                }
         
     | 
| 
      
 386 
     | 
    
         
            +
                return data
         
     | 
| 
         @@ -1347,6 +1347,7 @@ class Inference: 
     | 
|
| 
       1347 
1347 
     | 
    
         
             
                            source=images_np,
         
     | 
| 
       1348 
1348 
     | 
    
         
             
                            settings=settings,
         
     | 
| 
       1349 
1349 
     | 
    
         
             
                        )
         
     | 
| 
      
 1350 
     | 
    
         
            +
                        anns = self._exclude_duplicated_predictions(api, anns, settings, dataset_id, ids)
         
     | 
| 
       1350 
1351 
     | 
    
         
             
                        results.extend(self._format_output(anns, slides_data))
         
     | 
| 
       1351 
1352 
     | 
    
         
             
                    return results
         
     | 
| 
       1352 
1353 
     | 
    
         | 
| 
         @@ -1395,6 +1396,10 @@ class Inference: 
     | 
|
| 
       1395 
1396 
     | 
    
         
             
                            )
         
     | 
| 
       1396 
1397 
     | 
    
         
             
                            self.cache.set_project_meta(output_project_id, output_project_meta)
         
     | 
| 
       1397 
1398 
     | 
    
         | 
| 
      
 1399 
     | 
    
         
            +
                        ann = self._exclude_duplicated_predictions(
         
     | 
| 
      
 1400 
     | 
    
         
            +
                            api, anns, settings, ds_info.id, [image_id], output_project_meta
         
     | 
| 
      
 1401 
     | 
    
         
            +
                        )[0]
         
     | 
| 
      
 1402 
     | 
    
         
            +
             
     | 
| 
       1398 
1403 
     | 
    
         
             
                        logger.debug(
         
     | 
| 
       1399 
1404 
     | 
    
         
             
                            "Uploading annotation...",
         
     | 
| 
       1400 
1405 
     | 
    
         
             
                            extra={
         
     | 
| 
         @@ -1404,6 +1409,10 @@ class Inference: 
     | 
|
| 
       1404 
1409 
     | 
    
         
             
                            },
         
     | 
| 
       1405 
1410 
     | 
    
         
             
                        )
         
     | 
| 
       1406 
1411 
     | 
    
         
             
                        api.annotation.upload_ann(image_id, ann)
         
     | 
| 
      
 1412 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 1413 
     | 
    
         
            +
                        ann = self._exclude_duplicated_predictions(
         
     | 
| 
      
 1414 
     | 
    
         
            +
                            api, anns, settings, image_info.dataset_id, [image_id]
         
     | 
| 
      
 1415 
     | 
    
         
            +
                        )[0]
         
     | 
| 
       1407 
1416 
     | 
    
         | 
| 
       1408 
1417 
     | 
    
         
             
                    result = self._format_output(anns, slides_data)[0]
         
     | 
| 
       1409 
1418 
     | 
    
         
             
                    if async_inference_request_uuid is not None and ann is not None:
         
     | 
| 
         @@ -1786,6 +1795,15 @@ class Inference: 
     | 
|
| 
       1786 
1795 
     | 
    
         
             
                            batch_results = []
         
     | 
| 
       1787 
1796 
     | 
    
         
             
                            for i, ann in enumerate(anns):
         
     | 
| 
       1788 
1797 
     | 
    
         
             
                                image_info: ImageInfo = images_infos_dict[image_ids_batch[i]]
         
     | 
| 
      
 1798 
     | 
    
         
            +
                                ds_info = dataset_infos_dict[image_info.dataset_id]
         
     | 
| 
      
 1799 
     | 
    
         
            +
                                meta = output_project_metas_dict.get(ds_info.project_id, None)
         
     | 
| 
      
 1800 
     | 
    
         
            +
                                iou = settings.get("existing_objects_iou_thresh")
         
     | 
| 
      
 1801 
     | 
    
         
            +
                                if meta is None and isinstance(iou, float) and iou > 0:
         
     | 
| 
      
 1802 
     | 
    
         
            +
                                    meta = ProjectMeta.from_json(api.project.get_meta(ds_info.project_id))
         
     | 
| 
      
 1803 
     | 
    
         
            +
                                    output_project_metas_dict[ds_info.project_id] = meta
         
     | 
| 
      
 1804 
     | 
    
         
            +
                                ann = self._exclude_duplicated_predictions(
         
     | 
| 
      
 1805 
     | 
    
         
            +
                                    api, [ann], settings, ds_info.id, [image_info.id], meta
         
     | 
| 
      
 1806 
     | 
    
         
            +
                                )[0]
         
     | 
| 
       1789 
1807 
     | 
    
         
             
                                batch_results.append(
         
     | 
| 
       1790 
1808 
     | 
    
         
             
                                    {
         
     | 
| 
       1791 
1809 
     | 
    
         
             
                                        "annotation": ann.to_json(),
         
     | 
| 
         @@ -2086,6 +2104,19 @@ class Inference: 
     | 
|
| 
       2086 
2104 
     | 
    
         
             
                                    source=images_nps,
         
     | 
| 
       2087 
2105 
     | 
    
         
             
                                    settings=settings,
         
     | 
| 
       2088 
2106 
     | 
    
         
             
                                )
         
     | 
| 
      
 2107 
     | 
    
         
            +
                                iou = settings.get("existing_objects_iou_thresh")
         
     | 
| 
      
 2108 
     | 
    
         
            +
                                if output_project_meta is None and isinstance(iou, float) and iou > 0:
         
     | 
| 
      
 2109 
     | 
    
         
            +
                                    output_project_meta = ProjectMeta.from_json(
         
     | 
| 
      
 2110 
     | 
    
         
            +
                                        api.project.get_meta(project_info.id)
         
     | 
| 
      
 2111 
     | 
    
         
            +
                                    )
         
     | 
| 
      
 2112 
     | 
    
         
            +
                                anns = self._exclude_duplicated_predictions(
         
     | 
| 
      
 2113 
     | 
    
         
            +
                                    api,
         
     | 
| 
      
 2114 
     | 
    
         
            +
                                    anns,
         
     | 
| 
      
 2115 
     | 
    
         
            +
                                    settings,
         
     | 
| 
      
 2116 
     | 
    
         
            +
                                    dataset_info.id,
         
     | 
| 
      
 2117 
     | 
    
         
            +
                                    [ii.id for ii in images_infos_batch],
         
     | 
| 
      
 2118 
     | 
    
         
            +
                                    output_project_meta,
         
     | 
| 
      
 2119 
     | 
    
         
            +
                                )
         
     | 
| 
       2089 
2120 
     | 
    
         
             
                                batch_results = []
         
     | 
| 
       2090 
2121 
     | 
    
         
             
                                for i, ann in enumerate(anns):
         
     | 
| 
       2091 
2122 
     | 
    
         
             
                                    batch_results.append(
         
     | 
| 
         @@ -2935,7 +2966,9 @@ class Inference: 
     | 
|
| 
       2935 
2966 
     | 
    
         
             
                    parser = argparse.ArgumentParser(description="Run Inference Serving")
         
     | 
| 
       2936 
2967 
     | 
    
         | 
| 
       2937 
2968 
     | 
    
         
             
                    # Positional args
         
     | 
| 
       2938 
     | 
    
         
            -
                    parser.add_argument( 
     | 
| 
      
 2969 
     | 
    
         
            +
                    parser.add_argument(
         
     | 
| 
      
 2970 
     | 
    
         
            +
                        "mode", nargs="?", type=str, help="Mode of operation: 'deploy' or 'predict'"
         
     | 
| 
      
 2971 
     | 
    
         
            +
                    )
         
     | 
| 
       2939 
2972 
     | 
    
         
             
                    parser.add_argument("input", nargs="?", type=str, help="Local path to input data")
         
     | 
| 
       2940 
2973 
     | 
    
         | 
| 
       2941 
2974 
     | 
    
         
             
                    # Deploy args
         
     | 
| 
         @@ -3459,6 +3492,127 @@ class Inference: 
     | 
|
| 
       3459 
3492 
     | 
    
         
             
                            f"Checkpoint {checkpoint_url} not found in Team Files. Cannot set workflow input"
         
     | 
| 
       3460 
3493 
     | 
    
         
             
                        )
         
     | 
| 
       3461 
3494 
     | 
    
         | 
| 
      
 3495 
     | 
    
         
            +
                def _exclude_duplicated_predictions(
         
     | 
| 
      
 3496 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 3497 
     | 
    
         
            +
                    api: Api,
         
     | 
| 
      
 3498 
     | 
    
         
            +
                    pred_anns: List[Annotation],
         
     | 
| 
      
 3499 
     | 
    
         
            +
                    settings: dict,
         
     | 
| 
      
 3500 
     | 
    
         
            +
                    dataset_id: int,
         
     | 
| 
      
 3501 
     | 
    
         
            +
                    gt_image_ids: List[int],
         
     | 
| 
      
 3502 
     | 
    
         
            +
                    meta: Optional[ProjectMeta] = None,
         
     | 
| 
      
 3503 
     | 
    
         
            +
                ):
         
     | 
| 
      
 3504 
     | 
    
         
            +
                    """
         
     | 
| 
      
 3505 
     | 
    
         
            +
                    Filter out predictions that significantly overlap with ground truth (GT) objects.
         
     | 
| 
      
 3506 
     | 
    
         
            +
             
     | 
| 
      
 3507 
     | 
    
         
            +
                    This is a wrapper around the `_filter_duplicated_predictions_from_ann` method that does the following:
         
     | 
| 
      
 3508 
     | 
    
         
            +
                    - Checks inference settings for the IoU threshold (`existing_objects_iou_thresh`)
         
     | 
| 
      
 3509 
     | 
    
         
            +
                    - Gets ProjectMeta object if not provided
         
     | 
| 
      
 3510 
     | 
    
         
            +
                    - Downloads GT annotations for the specified image IDs
         
     | 
| 
      
 3511 
     | 
    
         
            +
                    - Filters out predictions that have an IoU greater than or equal to the specified threshold with any GT object
         
     | 
| 
      
 3512 
     | 
    
         
            +
             
     | 
| 
      
 3513 
     | 
    
         
            +
                    :param api: Supervisely API object
         
     | 
| 
      
 3514 
     | 
    
         
            +
                    :type api: Api
         
     | 
| 
      
 3515 
     | 
    
         
            +
                    :param pred_anns: List of Annotation objects containing predictions
         
     | 
| 
      
 3516 
     | 
    
         
            +
                    :type pred_anns: List[Annotation]
         
     | 
| 
      
 3517 
     | 
    
         
            +
                    :param settings: Inference settings
         
     | 
| 
      
 3518 
     | 
    
         
            +
                    :type settings: dict
         
     | 
| 
      
 3519 
     | 
    
         
            +
                    :param dataset_id: ID of the dataset containing the images
         
     | 
| 
      
 3520 
     | 
    
         
            +
                    :type dataset_id: int
         
     | 
| 
      
 3521 
     | 
    
         
            +
                    :param gt_image_ids: List of image IDs to filter predictions. All images should belong to the same dataset
         
     | 
| 
      
 3522 
     | 
    
         
            +
                    :type gt_image_ids: List[int]
         
     | 
| 
      
 3523 
     | 
    
         
            +
                    :param meta: ProjectMeta object
         
     | 
| 
      
 3524 
     | 
    
         
            +
                    :type meta: Optional[ProjectMeta]
         
     | 
| 
      
 3525 
     | 
    
         
            +
                    :return: List of Annotation objects containing filtered predictions
         
     | 
| 
      
 3526 
     | 
    
         
            +
                    :rtype: List[Annotation]
         
     | 
| 
      
 3527 
     | 
    
         
            +
             
     | 
| 
      
 3528 
     | 
    
         
            +
                    Notes:
         
     | 
| 
      
 3529 
     | 
    
         
            +
                    ------
         
     | 
| 
      
 3530 
     | 
    
         
            +
                    - Requires PyTorch and torchvision for IoU calculations
         
     | 
| 
      
 3531 
     | 
    
         
            +
                    - This method is useful for identifying new objects that aren't already annotated in the ground truth
         
     | 
| 
      
 3532 
     | 
    
         
            +
                    """
         
     | 
| 
      
 3533 
     | 
    
         
            +
                    iou = settings.get("existing_objects_iou_thresh")
         
     | 
| 
      
 3534 
     | 
    
         
            +
                    if isinstance(iou, float) and 0 < iou <= 1:
         
     | 
| 
      
 3535 
     | 
    
         
            +
                        if meta is None:
         
     | 
| 
      
 3536 
     | 
    
         
            +
                            ds = api.dataset.get_info_by_id(dataset_id)
         
     | 
| 
      
 3537 
     | 
    
         
            +
                            meta = ProjectMeta.from_json(api.project.get_meta(ds.project_id))
         
     | 
| 
      
 3538 
     | 
    
         
            +
                        gt_anns = api.annotation.download_json_batch(dataset_id, gt_image_ids)
         
     | 
| 
      
 3539 
     | 
    
         
            +
                        gt_anns = [Annotation.from_json(ann, meta) for ann in gt_anns]
         
     | 
| 
      
 3540 
     | 
    
         
            +
                        for i in range(0, len(pred_anns)):
         
     | 
| 
      
 3541 
     | 
    
         
            +
                            before = len(pred_anns[i].labels)
         
     | 
| 
      
 3542 
     | 
    
         
            +
                            with Timer() as timer:
         
     | 
| 
      
 3543 
     | 
    
         
            +
                                pred_anns[i] = self._filter_duplicated_predictions_from_ann(
         
     | 
| 
      
 3544 
     | 
    
         
            +
                                    gt_anns[i], pred_anns[i], iou
         
     | 
| 
      
 3545 
     | 
    
         
            +
                                )
         
     | 
| 
      
 3546 
     | 
    
         
            +
                            after = len(pred_anns[i].labels)
         
     | 
| 
      
 3547 
     | 
    
         
            +
                            logger.debug(
         
     | 
| 
      
 3548 
     | 
    
         
            +
                                f"{[i]}: applied NMS with IoU={iou}. Before: {before}, After: {after}. Time: {timer.get_time():.3f}ms"
         
     | 
| 
      
 3549 
     | 
    
         
            +
                            )
         
     | 
| 
      
 3550 
     | 
    
         
            +
                    return pred_anns
         
     | 
| 
      
 3551 
     | 
    
         
            +
             
     | 
| 
      
 3552 
     | 
    
         
            +
                def _filter_duplicated_predictions_from_ann(
         
     | 
| 
      
 3553 
     | 
    
         
            +
                    self, gt_ann: Annotation, pred_ann: Annotation, iou_threshold: float
         
     | 
| 
      
 3554 
     | 
    
         
            +
                ) -> Annotation:
         
     | 
| 
      
 3555 
     | 
    
         
            +
                    """
         
     | 
| 
      
 3556 
     | 
    
         
            +
                    Filter out predictions that significantly overlap with ground truth annotations.
         
     | 
| 
      
 3557 
     | 
    
         
            +
             
     | 
| 
      
 3558 
     | 
    
         
            +
                    This function compares each prediction with ground truth annotations of the same class
         
     | 
| 
      
 3559 
     | 
    
         
            +
                    and removes predictions that have an IoU (Intersection over Union) greater than or equal
         
     | 
| 
      
 3560 
     | 
    
         
            +
                    to the specified threshold with any ground truth annotation. This is useful for identifying
         
     | 
| 
      
 3561 
     | 
    
         
            +
                    new objects that aren't already annotated in the ground truth.
         
     | 
| 
      
 3562 
     | 
    
         
            +
             
     | 
| 
      
 3563 
     | 
    
         
            +
                    :param gt_ann: Annotation object containing ground truth labels
         
     | 
| 
      
 3564 
     | 
    
         
            +
                    :type gt_ann: Annotation
         
     | 
| 
      
 3565 
     | 
    
         
            +
                    :param pred_ann: Annotation object containing prediction labels to be filtered
         
     | 
| 
      
 3566 
     | 
    
         
            +
                    :type pred_ann: Annotation
         
     | 
| 
      
 3567 
     | 
    
         
            +
                    :param iou_threshold:   IoU threshold (0.0-1.0). Predictions with IoU >= threshold with any
         
     | 
| 
      
 3568 
     | 
    
         
            +
                                            ground truth box of the same class will be removed
         
     | 
| 
      
 3569 
     | 
    
         
            +
                    :type iou_threshold: float
         
     | 
| 
      
 3570 
     | 
    
         
            +
                    :return: A new annotation object containing only predictions that don't significantly
         
     | 
| 
      
 3571 
     | 
    
         
            +
                             overlap with ground truth annotations
         
     | 
| 
      
 3572 
     | 
    
         
            +
                    :rtype: Annotation
         
     | 
| 
      
 3573 
     | 
    
         
            +
             
     | 
| 
      
 3574 
     | 
    
         
            +
             
     | 
| 
      
 3575 
     | 
    
         
            +
                    Notes:
         
     | 
| 
      
 3576 
     | 
    
         
            +
                    ------
         
     | 
| 
      
 3577 
     | 
    
         
            +
                    - Predictions with classes not present in ground truth will be kept
         
     | 
| 
      
 3578 
     | 
    
         
            +
                    - Requires PyTorch and torchvision for IoU calculations
         
     | 
| 
      
 3579 
     | 
    
         
            +
                    """
         
     | 
| 
      
 3580 
     | 
    
         
            +
             
     | 
| 
      
 3581 
     | 
    
         
            +
                    try:
         
     | 
| 
      
 3582 
     | 
    
         
            +
                        import torch
         
     | 
| 
      
 3583 
     | 
    
         
            +
                        from torchvision.ops import box_iou
         
     | 
| 
      
 3584 
     | 
    
         
            +
             
     | 
| 
      
 3585 
     | 
    
         
            +
                    except ImportError:
         
     | 
| 
      
 3586 
     | 
    
         
            +
                        raise ImportError("Please install PyTorch and torchvision to use this feature.")
         
     | 
| 
      
 3587 
     | 
    
         
            +
             
     | 
| 
      
 3588 
     | 
    
         
            +
                    def _to_tensor(geom):
         
     | 
| 
      
 3589 
     | 
    
         
            +
                        return torch.tensor([geom.left, geom.top, geom.right, geom.bottom]).float()
         
     | 
| 
      
 3590 
     | 
    
         
            +
             
     | 
| 
      
 3591 
     | 
    
         
            +
                    new_labels = []
         
     | 
| 
      
 3592 
     | 
    
         
            +
                    pred_cls_bboxes = defaultdict(list)
         
     | 
| 
      
 3593 
     | 
    
         
            +
                    for label in pred_ann.labels:
         
     | 
| 
      
 3594 
     | 
    
         
            +
                        pred_cls_bboxes[label.obj_class.name].append(label)
         
     | 
| 
      
 3595 
     | 
    
         
            +
             
     | 
| 
      
 3596 
     | 
    
         
            +
                    gt_cls_bboxes = defaultdict(list)
         
     | 
| 
      
 3597 
     | 
    
         
            +
                    for label in gt_ann.labels:
         
     | 
| 
      
 3598 
     | 
    
         
            +
                        if label.obj_class.name not in pred_cls_bboxes:
         
     | 
| 
      
 3599 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 3600 
     | 
    
         
            +
                        gt_cls_bboxes[label.obj_class.name].append(label)
         
     | 
| 
      
 3601 
     | 
    
         
            +
             
     | 
| 
      
 3602 
     | 
    
         
            +
                    for name, pred in pred_cls_bboxes.items():
         
     | 
| 
      
 3603 
     | 
    
         
            +
                        gt = gt_cls_bboxes[name]
         
     | 
| 
      
 3604 
     | 
    
         
            +
                        if len(gt) == 0:
         
     | 
| 
      
 3605 
     | 
    
         
            +
                            new_labels.extend(pred)
         
     | 
| 
      
 3606 
     | 
    
         
            +
                            continue
         
     | 
| 
      
 3607 
     | 
    
         
            +
                        pred_bboxes = torch.stack([_to_tensor(l.geometry.to_bbox()) for l in pred]).float()
         
     | 
| 
      
 3608 
     | 
    
         
            +
                        gt_bboxes = torch.stack([_to_tensor(l.geometry.to_bbox()) for l in gt]).float()
         
     | 
| 
      
 3609 
     | 
    
         
            +
                        iou_matrix = box_iou(pred_bboxes, gt_bboxes)
         
     | 
| 
      
 3610 
     | 
    
         
            +
                        iou_matrix = iou_matrix.cpu().numpy()
         
     | 
| 
      
 3611 
     | 
    
         
            +
                        keep_indices = np.where(np.all(iou_matrix < iou_threshold, axis=1))[0]
         
     | 
| 
      
 3612 
     | 
    
         
            +
                        new_labels.extend([pred[i] for i in keep_indices])
         
     | 
| 
      
 3613 
     | 
    
         
            +
             
     | 
| 
      
 3614 
     | 
    
         
            +
                    return pred_ann.clone(labels=new_labels)
         
     | 
| 
      
 3615 
     | 
    
         
            +
             
     | 
| 
       3462 
3616 
     | 
    
         | 
| 
       3463 
3617 
     | 
    
         
             
            def _get_log_extra_for_inference_request(inference_request_uuid, inference_request: dict):
         
     | 
| 
       3464 
3618 
     | 
    
         
             
                log_extra = {
         
     | 
| 
         @@ -565,7 +565,7 @@ supervisely/collection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3 
     | 
|
| 
       565 
565 
     | 
    
         
             
            supervisely/collection/key_indexed_collection.py,sha256=x2UVlkprspWhhae9oLUzjTWBoIouiWY9UQSS_MozfH0,37643
         
     | 
| 
       566 
566 
     | 
    
         
             
            supervisely/collection/str_enum.py,sha256=Zp29yFGvnxC6oJRYNNlXhO2lTSdsriU1wiGHj6ahEJE,1250
         
     | 
| 
       567 
567 
     | 
    
         
             
            supervisely/convert/__init__.py,sha256=ropgB1eebG2bfLoJyf2jp8Vv9UkFujaW3jVX-71ho1g,1353
         
     | 
| 
       568 
     | 
    
         
            -
            supervisely/convert/base_converter.py,sha256= 
     | 
| 
      
 568 
     | 
    
         
            +
            supervisely/convert/base_converter.py,sha256=rRMIxY3h7cX5WAu_qn7w9vzRBcDB_jLZm5u_XQh7QG4,18563
         
     | 
| 
       569 
569 
     | 
    
         
             
            supervisely/convert/converter.py,sha256=tWxTDfFv7hwzQhUQrBxzfr6WP8FUGFX_ewg5T2HbUYo,8959
         
     | 
| 
       570 
570 
     | 
    
         
             
            supervisely/convert/image/__init__.py,sha256=JEuyaBiiyiYmEUYqdn8Mog5FVXpz0H1zFubKkOOm73I,1395
         
     | 
| 
       571 
571 
     | 
    
         
             
            supervisely/convert/image/image_converter.py,sha256=8vak8ZoKTN1ye2ZmCTvCZ605-Rw1AFLIEo7bJMfnR68,10426
         
     | 
| 
         @@ -634,10 +634,13 @@ supervisely/convert/pointcloud/ply/ply_helper.py,sha256=YfLiV9m6a4NNEMs0J32dmMTL 
     | 
|
| 
       634 
634 
     | 
    
         
             
            supervisely/convert/pointcloud/sly/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
       635 
635 
     | 
    
         
             
            supervisely/convert/pointcloud/sly/sly_pointcloud_converter.py,sha256=r56Rwil-55cRnd0sIePFGrf_xXa-lKQSfwhEUrjOquk,5070
         
     | 
| 
       636 
636 
     | 
    
         
             
            supervisely/convert/pointcloud/sly/sly_pointcloud_helper.py,sha256=kOluL97FfCFfIvnUE_FeN8iQLMlwdiMR5gayorOGDXw,3968
         
     | 
| 
       637 
     | 
    
         
            -
            supervisely/convert/pointcloud_episodes/__init__.py,sha256= 
     | 
| 
      
 637 
     | 
    
         
            +
            supervisely/convert/pointcloud_episodes/__init__.py,sha256=LePLQFEjXwhXap2zOY9SVTbW_NMbxKYZKBjBdRLimKE,557
         
     | 
| 
       638 
638 
     | 
    
         
             
            supervisely/convert/pointcloud_episodes/pointcloud_episodes_converter.py,sha256=qULUzO96BvWgNVmyxSQ0pUPBPG3WHgUJuK_U7Z8NM-g,9428
         
     | 
| 
       639 
639 
     | 
    
         
             
            supervisely/convert/pointcloud_episodes/bag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
       640 
640 
     | 
    
         
             
            supervisely/convert/pointcloud_episodes/bag/bag_converter.py,sha256=jzWKXoFUWu11d5WlPfT1hphCubYpq_lhQZmhh07xZdQ,1659
         
     | 
| 
      
 641 
     | 
    
         
            +
            supervisely/convert/pointcloud_episodes/kitti_360/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
      
 642 
     | 
    
         
            +
            supervisely/convert/pointcloud_episodes/kitti_360/kitti_360_converter.py,sha256=ls3Pgf9WYTtaTzf6nLCL3gMjG6zZ_EAVKE5OJSFAOPc,10033
         
     | 
| 
      
 643 
     | 
    
         
            +
            supervisely/convert/pointcloud_episodes/kitti_360/kitti_360_helper.py,sha256=EHyJTRfIpUC3lETJOCTI_OY4ddmT0eTFLMMhOvSeCm0,12372
         
     | 
| 
       641 
644 
     | 
    
         
             
            supervisely/convert/pointcloud_episodes/lyft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
       642 
645 
     | 
    
         
             
            supervisely/convert/pointcloud_episodes/lyft/lyft_converter.py,sha256=QXreWUJ-QhoWgLPqRxCayatYCCCuSV6Z2XCZKScrD3o,10419
         
     | 
| 
       643 
646 
     | 
    
         
             
            supervisely/convert/pointcloud_episodes/nuscenes_conv/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         
     | 
| 
         @@ -876,7 +879,7 @@ supervisely/nn/benchmark/visualization/widgets/table/__init__.py,sha256=47DEQpj8 
     | 
|
| 
       876 
879 
     | 
    
         
             
            supervisely/nn/benchmark/visualization/widgets/table/table.py,sha256=atmDnF1Af6qLQBUjLhK18RMDKAYlxnsuVHMSEa5a-e8,4319
         
     | 
| 
       877 
880 
     | 
    
         
             
            supervisely/nn/inference/__init__.py,sha256=QFukX2ip-U7263aEPCF_UCFwj6EujbMnsgrXp5Bbt8I,1623
         
     | 
| 
       878 
881 
     | 
    
         
             
            supervisely/nn/inference/cache.py,sha256=q4F7ZRzZghNWSVFClXEIHNMNW4PK6xddYckCFUgyhCo,32027
         
     | 
| 
       879 
     | 
    
         
            -
            supervisely/nn/inference/inference.py,sha256= 
     | 
| 
      
 882 
     | 
    
         
            +
            supervisely/nn/inference/inference.py,sha256=SqfIgohv0U3USQpHerzkrnfIeC7JKGeQA49Tocliu1k,165877
         
     | 
| 
       880 
883 
     | 
    
         
             
            supervisely/nn/inference/session.py,sha256=jmkkxbe2kH-lEgUU6Afh62jP68dxfhF5v6OGDfLU62E,35757
         
     | 
| 
       881 
884 
     | 
    
         
             
            supervisely/nn/inference/video_inference.py,sha256=8Bshjr6rDyLay5Za8IB8Dr6FURMO2R_v7aELasO8pR4,5746
         
     | 
| 
       882 
885 
     | 
    
         
             
            supervisely/nn/inference/gui/__init__.py,sha256=wCxd-lF5Zhcwsis-wScDA8n1Gk_1O00PKgDviUZ3F1U,221
         
     | 
| 
         @@ -1075,9 +1078,9 @@ supervisely/worker_proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ 
     | 
|
| 
       1075 
1078 
     | 
    
         
             
            supervisely/worker_proto/worker_api_pb2.py,sha256=VQfi5JRBHs2pFCK1snec3JECgGnua3Xjqw_-b3aFxuM,59142
         
     | 
| 
       1076 
1079 
     | 
    
         
             
            supervisely/worker_proto/worker_api_pb2_grpc.py,sha256=3BwQXOaP9qpdi0Dt9EKG--Lm8KGN0C5AgmUfRv77_Jk,28940
         
     | 
| 
       1077 
1080 
     | 
    
         
             
            supervisely_lib/__init__.py,sha256=7-3QnN8Zf0wj8NCr2oJmqoQWMKKPKTECvjH9pd2S5vY,159
         
     | 
| 
       1078 
     | 
    
         
            -
            supervisely-6.73. 
     | 
| 
       1079 
     | 
    
         
            -
            supervisely-6.73. 
     | 
| 
       1080 
     | 
    
         
            -
            supervisely-6.73. 
     | 
| 
       1081 
     | 
    
         
            -
            supervisely-6.73. 
     | 
| 
       1082 
     | 
    
         
            -
            supervisely-6.73. 
     | 
| 
       1083 
     | 
    
         
            -
            supervisely-6.73. 
     | 
| 
      
 1081 
     | 
    
         
            +
            supervisely-6.73.322.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
         
     | 
| 
      
 1082 
     | 
    
         
            +
            supervisely-6.73.322.dist-info/METADATA,sha256=tXlMoMRbbXrc18yQVTx6Ti09xSaTCC4TyKgLUoNIC_U,33596
         
     | 
| 
      
 1083 
     | 
    
         
            +
            supervisely-6.73.322.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
         
     | 
| 
      
 1084 
     | 
    
         
            +
            supervisely-6.73.322.dist-info/entry_points.txt,sha256=U96-5Hxrp2ApRjnCoUiUhWMqijqh8zLR03sEhWtAcms,102
         
     | 
| 
      
 1085 
     | 
    
         
            +
            supervisely-6.73.322.dist-info/top_level.txt,sha256=kcFVwb7SXtfqZifrZaSE3owHExX4gcNYe7Q2uoby084,28
         
     | 
| 
      
 1086 
     | 
    
         
            +
            supervisely-6.73.322.dist-info/RECORD,,
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     |