supervisely 6.73.427__py3-none-any.whl → 6.73.429__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.
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  from os import path as osp
3
3
  from pathlib import Path
4
- from typing import Dict, Optional
4
+ from typing import Dict, List, Optional
5
5
 
6
6
  import supervisely.convert.pointcloud_episodes.nuscenes_conv.nuscenes_helper as helpers
7
7
  import supervisely.io.fs as fs
@@ -9,6 +9,7 @@ from supervisely._utils import is_development
9
9
  from supervisely.annotation.obj_class import ObjClass
10
10
  from supervisely.annotation.tag_meta import TagMeta, TagValueType
11
11
  from supervisely.api.api import Api, ApiField
12
+ from supervisely.api.dataset_api import DatasetInfo
12
13
  from supervisely.convert.base_converter import AvailablePointcloudConverters
13
14
  from supervisely.convert.pointcloud_episodes.pointcloud_episodes_converter import (
14
15
  PointcloudEpisodeConverter,
@@ -49,7 +50,6 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
49
50
  remote_files_map: Optional[Dict[str, str]] = None,
50
51
  ):
51
52
  super().__init__(input_data, labeling_interface, upload_as_links, remote_files_map)
52
- self._nuscenes = None
53
53
 
54
54
  def __str__(self) -> str:
55
55
  return AvailablePointcloudConverters.NUSCENES
@@ -83,8 +83,7 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
83
83
  version = osp.basename(ann_dir)
84
84
  try:
85
85
  t = TinyTimer()
86
- nuscenes = NuScenes(version=version, dataroot=input_path, verbose=False)
87
- self._nuscenes: NuScenes = nuscenes
86
+ self._nuscenes: NuScenes = NuScenes(version=version, dataroot=input_path, verbose=False)
88
87
  logger.debug(f"NuScenes initialization took {t.get_sec():.3f} sec")
89
88
  except Exception as e:
90
89
  logger.debug(f"Failed to initialize NuScenes: {e}")
@@ -94,7 +93,7 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
94
93
 
95
94
  def to_supervisely(
96
95
  self,
97
- scene_samples,
96
+ scene_samples: List[helpers.Sample],
98
97
  meta: ProjectMeta,
99
98
  renamed_classes: dict = {},
100
99
  renamed_tags: dict = {},
@@ -142,23 +141,14 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
142
141
  obj_classes = []
143
142
  for category in nuscenes.category:
144
143
  color = nuscenes.colormap[category["name"]]
145
- description = category["description"]
146
- if len(description) > 255:
147
- # * Trim description to fit into 255 characters limit
148
- sentences = description.split(".")
149
- trimmed_description = ""
150
- for sentence in sentences:
151
- if len(trimmed_description) + len(sentence) + 1 > 255:
152
- break
153
- trimmed_description += sentence + "."
154
- description = trimmed_description.strip()
144
+ description = helpers.trim_description(category.get("description", ""))
155
145
  obj_classes.append(ObjClass(category["name"], Cuboid3d, color, description=description))
156
146
 
157
147
  self._meta = ProjectMeta(obj_classes, tag_metas)
158
148
  meta, renamed_classes, renamed_tags = self.merge_metas_with_conflicts(api, dataset_id)
159
149
 
160
150
  dataset_info = api.dataset.get_info_by_id(dataset_id)
161
- scene_name_to_dataset = {}
151
+ scene_name_to_dataset: Dict[str, DatasetInfo] = {}
162
152
 
163
153
  scene_names = [scene["name"] for scene in nuscenes.scene]
164
154
  scene_cnt = len(scene_names)
@@ -193,7 +183,7 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
193
183
  sample_token = scene["first_sample_token"]
194
184
 
195
185
  # * Extract scene's samples
196
- scene_samples = []
186
+ scene_samples: List[helpers.Sample] = []
197
187
  for i in range(scene["nbr_samples"]):
198
188
  sample = nuscenes.get("sample", sample_token)
199
189
  lidar_path, boxes, _ = nuscenes.get_sample_data(sample["data"]["LIDAR_TOP"])
@@ -207,7 +197,6 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
207
197
  current_instance_token = inst_token["token"]
208
198
  parent_token = inst_token["prev"]
209
199
 
210
- # get category, attributes and visibility
211
200
  ann = nuscenes.get("sample_annotation", current_instance_token)
212
201
  category = ann["category_name"]
213
202
  attributes = [
@@ -215,17 +204,16 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
215
204
  ]
216
205
  visibility = nuscenes.get("visibility", ann["visibility_token"])["level"]
217
206
 
218
- anns.append(
219
- helpers.AnnotationObject(
220
- name,
221
- box,
222
- current_instance_token,
223
- parent_token,
224
- category,
225
- attributes,
226
- visibility,
227
- )
207
+ ann = helpers.AnnotationObject(
208
+ name=name,
209
+ bbox=box,
210
+ instance_token=current_instance_token,
211
+ parent_token=parent_token,
212
+ category=category,
213
+ attributes=attributes,
214
+ visibility=visibility,
228
215
  )
216
+ anns.append(ann)
229
217
 
230
218
  # get camera data
231
219
  sample_data = nuscenes.get("sample_data", sample["data"]["LIDAR_TOP"])
@@ -247,7 +235,7 @@ class NuscenesEpisodesConverter(PointcloudEpisodeConverter):
247
235
  for idx, sample in enumerate(scene_samples):
248
236
  pcd_path = sample.convert_lidar_to_supervisely()
249
237
 
250
- pcd_name = fs.get_file_name(pcd_path)
238
+ pcd_name = fs.get_file_name_with_ext(pcd_path)
251
239
  pcd_meta = {
252
240
  "frame": idx,
253
241
  "vehicle": log["vehicle"],
@@ -1,7 +1,7 @@
1
1
  from datetime import datetime
2
2
  from os import path as osp
3
3
  from pathlib import Path
4
- from typing import List
4
+ from typing import Dict, Generator, List, Tuple
5
5
 
6
6
  import numpy as np
7
7
 
@@ -40,91 +40,36 @@ TABLE_NAMES = [
40
40
  ]
41
41
 
42
42
 
43
- class Sample:
44
- """
45
- A class to represent a sample from the NuScenes dataset.
46
- """
47
-
48
- def __init__(self, timestamp, lidar_path, anns, cam_data):
49
- self.timestamp = datetime.utcfromtimestamp(timestamp / 1e6).isoformat()
50
- self.lidar_path = lidar_path
51
- self.anns = anns
52
- self.cam_data = cam_data
53
-
54
- @staticmethod
55
- def generate_boxes(nuscenes, boxes):
56
- """
57
- Generate ground truth boxes for a given set of boxes.
58
-
59
- Yields:
60
- tuple: A tuple containing:
61
- - gt_box (np.ndarray): A numpy array representing the ground truth box with concatenated location,
62
- dimensions, and rotation.
63
- - name (str): The name of the object.
64
- - instance_token (str): The instance token associated with the box.
65
- """
66
- locs = np.array([b.center for b in boxes]).reshape(-1, 3)
67
- dims = np.array([b.wlh for b in boxes]).reshape(-1, 3)
68
- rots = np.array([b.orientation.yaw_pitch_roll[0] for b in boxes]).reshape(-1, 1)
69
-
70
- gt_boxes = np.concatenate([locs, dims, -rots - np.pi / 2], axis=1)
71
- names = np.array([b.name for b in boxes])
72
- instance_tokens = [nuscenes.get("sample_annotation", box.token) for box in boxes]
73
-
74
- yield from zip(gt_boxes, names, instance_tokens)
75
-
76
- def convert_lidar_to_supervisely(self):
77
- """
78
- Converts a LiDAR point cloud file to the Supervisely format and saves it as a .pcd file.
79
-
80
- Returns:
81
- str: The file path of the saved .pcd file.
82
- """
83
- import open3d as o3d # pylint: disable=import-error
84
-
85
- bin_file = Path(self.lidar_path)
86
- save_path = str(bin_file.with_suffix(".pcd"))
87
-
88
- b = np.fromfile(bin_file, dtype=np.float32).reshape(-1, 5)
89
- points = b[:, 0:3]
90
- intensity = b[:, 3]
91
- ring_index = b[:, 4]
92
- intensity_fake_rgb = np.zeros((intensity.shape[0], 3))
93
- intensity_fake_rgb[:, 0] = (
94
- intensity # red The intensity measures the reflectivity of the objects
95
- )
96
- intensity_fake_rgb[:, 1] = (
97
- ring_index # green ring index is the index of the laser ranging from 0 to 31
98
- )
99
- try:
100
- pc = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(points))
101
- pc.colors = o3d.utility.Vector3dVector(intensity_fake_rgb)
102
- o3d.io.write_point_cloud(save_path, pc)
103
- except Exception as e:
104
- logger.warning(f"Error converting lidar to supervisely format: {e}")
105
- return save_path
43
+ def trim_description(description: str, max_length: int = 255) -> str:
44
+ if len(description) > max_length:
45
+ sentences = description.split(".")
46
+ trimmed_description = ""
47
+ for sentence in sentences:
48
+ if len(trimmed_description) + len(sentence) + 1 > max_length:
49
+ break
50
+ trimmed_description += sentence + "."
51
+ description = trimmed_description.strip()
52
+ return description
106
53
 
107
54
 
108
55
  class AnnotationObject:
109
56
  """
110
57
  A class to represent an annotation object in the NuScenes dataset.
111
58
 
112
- Attributes:
113
- -----------
114
- name : str
115
- The name of the annotation object.
116
- bbox : np.ndarray
117
- The bounding box coordinates.
118
- instance_token : str
119
- The instance token associated with the annotation object.
120
- parent_token : str
121
- The token of instance preceding the current object instance.
122
- category : str
123
- The class name of the annotation object.
124
- attributes : List[str]
125
- The attribute names associated with the annotation object.
126
- visibility : str
127
- The visibility level of the annotation object.
59
+ :param name: The name of the annotation object
60
+ :type name: str
61
+ :param bbox: The bounding box coordinates in NuScenes format
62
+ :type bbox: np.ndarray
63
+ :param instance_token: The instance token associated with the annotation object
64
+ :type instance_token: str
65
+ :param parent_token: The token of instance preceding the current object instance
66
+ :type parent_token: str
67
+ :param category: The class name of the annotation object
68
+ :type category: str
69
+ :param attributes: The attribute names associated with the annotation object
70
+ :type attributes: List[str]
71
+ :param visibility: The visibility level of the annotation object
72
+ :type visibility: str
128
73
  """
129
74
 
130
75
  def __init__(
@@ -146,7 +91,7 @@ class AnnotationObject:
146
91
  self.attributes = attributes
147
92
  self.visibility = visibility
148
93
 
149
- def to_supervisely(self):
94
+ def to_supervisely(self) -> Cuboid3d:
150
95
  box = self.convert_nuscenes_to_BEVBox3D()
151
96
 
152
97
  bbox = box.to_xyzwhlr()
@@ -176,29 +121,33 @@ class AnnotationObject:
176
121
 
177
122
  class CamData:
178
123
  """
179
- A class to represent camera data and perform transformations between different coordinate systems.
180
-
181
- Attributes:
182
- -----------
183
- name : str
184
- The name of the sensor.
185
- path : str
186
- The path to the image file.
187
- imsize : tuple
188
- The size of the image (width, height).
189
- extrinsic : np.ndarray
190
- The extrinsic matrix (4x4) representing the transformation from the lidar to the camera coordinate system.
191
- intrinsic : np.ndarray
192
- The intrinsic matrix (3x3) representing the camera's intrinsic parameters.
124
+ This class handles camera sensor data from the nuScenes dataset, including coordinate system
125
+ transformations from lidar to camera space and extraction of camera calibration parameters.
126
+
127
+ :param nuscenes: The nuScenes dataset instance
128
+ :type nuscenes: NuScenes
129
+ :param sensor_name: The name of the camera sensor
130
+ :type sensor_name: str
131
+ :param sensor_token: The token identifying the specific sensor sample
132
+ :type sensor_token: str
133
+ :param cs_record: The calibrated sensor record for the lidar
134
+ :type cs_record: dict
135
+ :param ego_record: The ego pose record for the lidar
136
+ :type ego_record: dict
193
137
  """
194
138
 
195
- def __init__(self, nuscenes, sensor_name, sensor_token, cs_record, ego_record):
139
+ def __init__(
140
+ self, nuscenes, sensor_name: str, sensor_token: str, cs_record: dict, ego_record: dict
141
+ ):
142
+ from nuscenes import NuScenes # pylint: disable=import-error
196
143
  from nuscenes.utils.data_classes import ( # pylint: disable=import-error
197
144
  transform_matrix,
198
145
  )
199
146
  from pyquaternion import Quaternion # pylint: disable=import-error
200
147
 
201
- img_path, boxes, cam_intrinsic = nuscenes.get_sample_data(sensor_token)
148
+ nuscenes: NuScenes = nuscenes
149
+
150
+ img_path, _, _ = nuscenes.get_sample_data(sensor_token)
202
151
  if not osp.exists(img_path):
203
152
  return None
204
153
 
@@ -237,15 +186,14 @@ class CamData:
237
186
  self.extrinsic = np.hstack((velo_to_cam_rot, velo_to_cam_trans.reshape(3, 1)))
238
187
  self.intrinsic = np.asarray(cs_record_cam["camera_intrinsic"])
239
188
 
240
- def get_info(self, timestamp):
189
+ def get_info(self, timestamp: str) -> Tuple[str, Dict]:
241
190
  """
242
- Retrieves information about the image and its metadata.
191
+ Generates image info based on the camera data.
243
192
 
244
- Args:
245
- timestamp (int): The timestamp associated with the image.
246
-
247
- Returns:
248
- tuple: A tuple containing the image path and a dictionary with image metadata.
193
+ :param timestamp: The timestamp associated with the image
194
+ :type timestamp: str
195
+ :return: A tuple containing the image path and a dictionary with image metadata.
196
+ :rtype: tuple
249
197
  """
250
198
  sensors_to_skip = ["_intrinsic", "_extrinsic", "_imsize"]
251
199
  if not any([self.name.endswith(s) for s in sensors_to_skip]):
@@ -263,3 +211,94 @@ class CamData:
263
211
  },
264
212
  }
265
213
  return (sly_path_img, img_info)
214
+
215
+
216
+ class Sample:
217
+ """
218
+ A class to represent a sample from the NuScenes dataset.
219
+ """
220
+
221
+ def __init__(
222
+ self,
223
+ timestamp: float,
224
+ lidar_path: str,
225
+ anns: List[AnnotationObject],
226
+ cam_data: List[CamData],
227
+ ):
228
+ self._timestamp = datetime.utcfromtimestamp(timestamp / 1e6).isoformat()
229
+ self._lidar_path = lidar_path
230
+ self._anns = anns
231
+ self._cam_data = cam_data
232
+
233
+ @property
234
+ def timestamp(self) -> str:
235
+ return self._timestamp
236
+
237
+ @property
238
+ def lidar_path(self) -> str:
239
+ return self._lidar_path
240
+
241
+ @property
242
+ def anns(self) -> List[AnnotationObject]:
243
+ return self._anns
244
+
245
+ @property
246
+ def cam_data(self) -> List[CamData]:
247
+ return self._cam_data
248
+
249
+ @staticmethod
250
+ def generate_boxes(nuscenes, boxes: List) -> Generator:
251
+ """
252
+ Generate ground truth boxes for a given set of boxes.
253
+
254
+ :param nuscenes: The nuScenes dataset instance
255
+ :type nuscenes: NuScenes
256
+ :param boxes: A list of boxes to generate ground truth for
257
+ :type boxes: List
258
+ :return: A generator that yields tuples containing the ground truth box, name, and instance token.
259
+ :rtype: generator
260
+ """
261
+ from nuscenes.utils.data_classes import Box # pylint: disable=import-error
262
+
263
+ boxes: List[Box] = boxes
264
+
265
+ locs = np.array([b.center for b in boxes]).reshape(-1, 3)
266
+ dims = np.array([b.wlh for b in boxes]).reshape(-1, 3)
267
+ rots = np.array([b.orientation.yaw_pitch_roll[0] for b in boxes]).reshape(-1, 1)
268
+
269
+ gt_boxes = np.concatenate([locs, dims, -rots - np.pi / 2], axis=1)
270
+ names = np.array([b.name for b in boxes])
271
+ instance_tokens = [nuscenes.get("sample_annotation", box.token) for box in boxes]
272
+
273
+ yield from zip(gt_boxes, names, instance_tokens)
274
+
275
+ def convert_lidar_to_supervisely(self) -> str:
276
+ """
277
+ Converts a LiDAR point cloud file to the Supervisely format and saves it as a .pcd file.
278
+
279
+ :return: The file path of the saved .pcd file.
280
+ :rtype: str
281
+ """
282
+ import open3d as o3d # pylint: disable=import-error
283
+
284
+ bin_file = Path(self.lidar_path)
285
+ save_path = str(bin_file.with_suffix(".pcd"))
286
+
287
+ b = np.fromfile(bin_file, dtype=np.float32).reshape(-1, 5)
288
+ points = b[:, 0:3]
289
+ intensity = b[:, 3]
290
+ ring_index = b[:, 4]
291
+ intensity_fake_rgb = np.zeros((intensity.shape[0], 3))
292
+ intensity_fake_rgb[:, 0] = (
293
+ intensity # red The intensity measures the reflectivity of the objects
294
+ )
295
+ intensity_fake_rgb[:, 1] = (
296
+ ring_index # green ring index is the index of the laser ranging from 0 to 31
297
+ )
298
+ try:
299
+ pc = o3d.geometry.PointCloud(o3d.utility.Vector3dVector(points))
300
+ pc.colors = o3d.utility.Vector3dVector(intensity_fake_rgb)
301
+ o3d.io.write_point_cloud(save_path, pc)
302
+ except Exception as e:
303
+ logger.warning(f"Error converting lidar to supervisely format: {e}")
304
+ return save_path
@@ -1,9 +1,8 @@
1
- from supervisely.sly_logger import logger
2
-
3
1
  try:
4
2
  from supervisely.nn.tracker.botsort_tracker import BotSortTracker
3
+ from supervisely.nn.tracker.calculate_metrics import TrackingEvaluator, evaluate
4
+ TRACKING_LIBS_INSTALLED = True
5
5
  except ImportError:
6
- logger.error(
7
- "Failed to import tracker modules. Please try install extras with 'pip install supervisely[tracking]'"
8
- )
9
- raise
6
+ TRACKING_LIBS_INSTALLED = False
7
+
8
+ from supervisely.nn.tracker.visualize import TrackingVisualizer, visualize
@@ -426,8 +426,8 @@ class BoTSORT(object):
426
426
  if track.score < self.new_track_thresh:
427
427
  continue
428
428
  # Fill track_id for new detection
429
- detection_track_map[orig_idx]["track_id"] = int(track.track_id)
430
429
  track.activate(self.kalman_filter, self.frame_id)
430
+ detection_track_map[orig_idx]["track_id"] = int(track.track_id)
431
431
  activated_starcks.append(track)
432
432
 
433
433
  """ Step 5: Update state"""
@@ -1,6 +1,5 @@
1
1
  import supervisely as sly
2
2
  from supervisely.nn.tracker.base_tracker import BaseTracker
3
- from supervisely.nn.tracker.botsort.tracker.mc_bot_sort import BoTSORT
4
3
  from supervisely import Annotation, VideoAnnotation
5
4
 
6
5
  from dataclasses import dataclass
@@ -11,6 +10,7 @@ import yaml
11
10
  import os
12
11
  from pathlib import Path
13
12
  from supervisely import logger
13
+ from supervisely.nn.tracker.botsort.tracker.mc_bot_sort import BoTSORT
14
14
 
15
15
 
16
16
  @dataclass
@@ -38,6 +38,13 @@ class BotSortTracker(BaseTracker):
38
38
 
39
39
  def __init__(self, settings: dict = None, device: str = None):
40
40
  super().__init__(settings=settings, device=device)
41
+
42
+ from supervisely.nn.tracker import TRACKING_LIBS_INSTALLED
43
+ if not TRACKING_LIBS_INSTALLED:
44
+ raise ImportError(
45
+ "Tracking dependencies are not installed. "
46
+ "Please install supervisely with `pip install supervisely[tracking]`."
47
+ )
41
48
 
42
49
  # Load default settings from YAML file
43
50
  self.settings = self._load_default_settings()
@@ -232,7 +239,7 @@ class BotSortTracker(BaseTracker):
232
239
 
233
240
  video_object = video_objects[track_id]
234
241
  rect = sly.Rectangle(top=y1, left=x1, bottom=y2, right=x2)
235
- frame_figures.append(sly.VideoFigure(video_object, rect, frame_idx))
242
+ frame_figures.append(sly.VideoFigure(video_object, rect, frame_idx, track_id=str(track_id)))
236
243
 
237
244
  frames.append(sly.Frame(frame_idx, frame_figures))
238
245