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.
- supervisely/app/widgets/__init__.py +2 -0
- supervisely/app/widgets/input_tag/input_tag.py +102 -15
- supervisely/app/widgets/input_tag_list/__init__.py +0 -0
- supervisely/app/widgets/input_tag_list/input_tag_list.py +274 -0
- supervisely/app/widgets/input_tag_list/template.html +70 -0
- supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +16 -25
- supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +17 -29
- supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +143 -104
- supervisely/nn/tracker/__init__.py +5 -6
- supervisely/nn/tracker/botsort/tracker/mc_bot_sort.py +1 -1
- supervisely/nn/tracker/botsort_tracker.py +9 -2
- supervisely/nn/tracker/calculate_metrics.py +264 -0
- supervisely/nn/tracker/utils.py +274 -0
- supervisely/nn/tracker/visualize.py +519 -0
- supervisely/template/experiment/experiment.html.jinja +26 -33
- {supervisely-6.73.427.dist-info → supervisely-6.73.429.dist-info}/METADATA +1 -1
- {supervisely-6.73.427.dist-info → supervisely-6.73.429.dist-info}/RECORD +21 -15
- {supervisely-6.73.427.dist-info → supervisely-6.73.429.dist-info}/LICENSE +0 -0
- {supervisely-6.73.427.dist-info → supervisely-6.73.429.dist-info}/WHEEL +0 -0
- {supervisely-6.73.427.dist-info → supervisely-6.73.429.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.427.dist-info → supervisely-6.73.429.dist-info}/top_level.txt +0 -0
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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.
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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__(
|
|
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
|
-
|
|
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
|
-
|
|
191
|
+
Generates image info based on the camera data.
|
|
243
192
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
7
|
-
|
|
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
|
|