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
|