supervisely 6.73.410__py3-none-any.whl → 6.73.470__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.
Potentially problematic release.
This version of supervisely might be problematic. Click here for more details.
- supervisely/__init__.py +136 -1
- supervisely/_utils.py +81 -0
- supervisely/annotation/json_geometries_map.py +2 -0
- supervisely/annotation/label.py +80 -3
- supervisely/api/annotation_api.py +9 -9
- supervisely/api/api.py +67 -43
- supervisely/api/app_api.py +72 -5
- supervisely/api/dataset_api.py +108 -33
- supervisely/api/entity_annotation/figure_api.py +113 -49
- supervisely/api/image_api.py +82 -0
- supervisely/api/module_api.py +10 -0
- supervisely/api/nn/deploy_api.py +15 -9
- supervisely/api/nn/ecosystem_models_api.py +201 -0
- supervisely/api/nn/neural_network_api.py +12 -3
- supervisely/api/pointcloud/pointcloud_api.py +38 -0
- supervisely/api/pointcloud/pointcloud_episode_annotation_api.py +3 -0
- supervisely/api/project_api.py +213 -6
- supervisely/api/task_api.py +11 -1
- supervisely/api/video/video_annotation_api.py +4 -2
- supervisely/api/video/video_api.py +79 -1
- supervisely/api/video/video_figure_api.py +24 -11
- supervisely/api/volume/volume_api.py +38 -0
- supervisely/app/__init__.py +1 -1
- supervisely/app/content.py +14 -6
- supervisely/app/fastapi/__init__.py +1 -0
- supervisely/app/fastapi/custom_static_files.py +1 -1
- supervisely/app/fastapi/multi_user.py +88 -0
- supervisely/app/fastapi/subapp.py +175 -42
- supervisely/app/fastapi/templating.py +1 -1
- supervisely/app/fastapi/websocket.py +77 -9
- supervisely/app/singleton.py +21 -0
- supervisely/app/v1/app_service.py +18 -2
- supervisely/app/v1/constants.py +7 -1
- supervisely/app/widgets/__init__.py +11 -1
- supervisely/app/widgets/agent_selector/template.html +1 -0
- supervisely/app/widgets/card/card.py +20 -0
- supervisely/app/widgets/dataset_thumbnail/dataset_thumbnail.py +11 -2
- supervisely/app/widgets/dataset_thumbnail/template.html +3 -1
- supervisely/app/widgets/deploy_model/deploy_model.py +750 -0
- supervisely/app/widgets/dialog/dialog.py +12 -0
- supervisely/app/widgets/dialog/template.html +2 -1
- supervisely/app/widgets/dropdown_checkbox_selector/__init__.py +0 -0
- supervisely/app/widgets/dropdown_checkbox_selector/dropdown_checkbox_selector.py +87 -0
- supervisely/app/widgets/dropdown_checkbox_selector/template.html +12 -0
- supervisely/app/widgets/ecosystem_model_selector/__init__.py +0 -0
- supervisely/app/widgets/ecosystem_model_selector/ecosystem_model_selector.py +195 -0
- supervisely/app/widgets/experiment_selector/experiment_selector.py +454 -263
- supervisely/app/widgets/fast_table/fast_table.py +713 -126
- supervisely/app/widgets/fast_table/script.js +492 -95
- supervisely/app/widgets/fast_table/style.css +54 -0
- supervisely/app/widgets/fast_table/template.html +45 -5
- supervisely/app/widgets/heatmap/__init__.py +0 -0
- supervisely/app/widgets/heatmap/heatmap.py +523 -0
- supervisely/app/widgets/heatmap/script.js +378 -0
- supervisely/app/widgets/heatmap/style.css +227 -0
- supervisely/app/widgets/heatmap/template.html +21 -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/app/widgets/radio_table/radio_table.py +10 -2
- supervisely/app/widgets/radio_tabs/radio_tabs.py +18 -2
- supervisely/app/widgets/radio_tabs/template.html +1 -0
- supervisely/app/widgets/select/select.py +6 -4
- supervisely/app/widgets/select_dataset/select_dataset.py +6 -0
- supervisely/app/widgets/select_dataset_tree/select_dataset_tree.py +83 -7
- supervisely/app/widgets/table/table.py +68 -13
- supervisely/app/widgets/tabs/tabs.py +22 -6
- supervisely/app/widgets/tabs/template.html +5 -1
- supervisely/app/widgets/transfer/style.css +3 -0
- supervisely/app/widgets/transfer/template.html +3 -1
- supervisely/app/widgets/transfer/transfer.py +48 -45
- supervisely/app/widgets/tree_select/tree_select.py +2 -0
- supervisely/convert/image/csv/csv_converter.py +24 -15
- supervisely/convert/pointcloud/nuscenes_conv/nuscenes_converter.py +43 -41
- supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_converter.py +75 -51
- supervisely/convert/pointcloud_episodes/nuscenes_conv/nuscenes_helper.py +137 -124
- supervisely/convert/video/video_converter.py +2 -2
- supervisely/geometry/polyline_3d.py +110 -0
- supervisely/io/env.py +161 -1
- supervisely/nn/artifacts/__init__.py +1 -1
- supervisely/nn/artifacts/artifacts.py +10 -2
- supervisely/nn/artifacts/detectron2.py +1 -0
- supervisely/nn/artifacts/hrda.py +1 -0
- supervisely/nn/artifacts/mmclassification.py +20 -0
- supervisely/nn/artifacts/mmdetection.py +5 -3
- supervisely/nn/artifacts/mmsegmentation.py +1 -0
- supervisely/nn/artifacts/ritm.py +1 -0
- supervisely/nn/artifacts/rtdetr.py +1 -0
- supervisely/nn/artifacts/unet.py +1 -0
- supervisely/nn/artifacts/utils.py +3 -0
- supervisely/nn/artifacts/yolov5.py +2 -0
- supervisely/nn/artifacts/yolov8.py +1 -0
- supervisely/nn/benchmark/semantic_segmentation/metric_provider.py +18 -18
- supervisely/nn/experiments.py +9 -0
- supervisely/nn/inference/cache.py +37 -17
- supervisely/nn/inference/gui/serving_gui_template.py +39 -13
- supervisely/nn/inference/inference.py +953 -211
- supervisely/nn/inference/inference_request.py +15 -8
- supervisely/nn/inference/instance_segmentation/instance_segmentation.py +1 -0
- supervisely/nn/inference/object_detection/object_detection.py +1 -0
- supervisely/nn/inference/predict_app/__init__.py +0 -0
- supervisely/nn/inference/predict_app/gui/__init__.py +0 -0
- supervisely/nn/inference/predict_app/gui/classes_selector.py +160 -0
- supervisely/nn/inference/predict_app/gui/gui.py +915 -0
- supervisely/nn/inference/predict_app/gui/input_selector.py +344 -0
- supervisely/nn/inference/predict_app/gui/model_selector.py +77 -0
- supervisely/nn/inference/predict_app/gui/output_selector.py +179 -0
- supervisely/nn/inference/predict_app/gui/preview.py +93 -0
- supervisely/nn/inference/predict_app/gui/settings_selector.py +881 -0
- supervisely/nn/inference/predict_app/gui/tags_selector.py +110 -0
- supervisely/nn/inference/predict_app/gui/utils.py +399 -0
- supervisely/nn/inference/predict_app/predict_app.py +176 -0
- supervisely/nn/inference/session.py +47 -39
- supervisely/nn/inference/tracking/bbox_tracking.py +5 -1
- supervisely/nn/inference/tracking/point_tracking.py +5 -1
- supervisely/nn/inference/tracking/tracker_interface.py +4 -0
- supervisely/nn/inference/uploader.py +9 -5
- supervisely/nn/model/model_api.py +44 -22
- supervisely/nn/model/prediction.py +15 -1
- supervisely/nn/model/prediction_session.py +70 -14
- supervisely/nn/prediction_dto.py +7 -0
- supervisely/nn/tracker/__init__.py +6 -8
- supervisely/nn/tracker/base_tracker.py +54 -0
- supervisely/nn/tracker/botsort/__init__.py +1 -0
- supervisely/nn/tracker/botsort/botsort_config.yaml +30 -0
- supervisely/nn/tracker/botsort/osnet_reid/__init__.py +0 -0
- supervisely/nn/tracker/botsort/osnet_reid/osnet.py +566 -0
- supervisely/nn/tracker/botsort/osnet_reid/osnet_reid_interface.py +88 -0
- supervisely/nn/tracker/botsort/tracker/__init__.py +0 -0
- supervisely/nn/tracker/{bot_sort → botsort/tracker}/basetrack.py +1 -2
- supervisely/nn/tracker/{utils → botsort/tracker}/gmc.py +51 -59
- supervisely/nn/tracker/{deep_sort/deep_sort → botsort/tracker}/kalman_filter.py +71 -33
- supervisely/nn/tracker/botsort/tracker/matching.py +202 -0
- supervisely/nn/tracker/{bot_sort/bot_sort.py → botsort/tracker/mc_bot_sort.py} +68 -81
- supervisely/nn/tracker/botsort_tracker.py +273 -0
- supervisely/nn/tracker/calculate_metrics.py +264 -0
- supervisely/nn/tracker/utils.py +273 -0
- supervisely/nn/tracker/visualize.py +520 -0
- supervisely/nn/training/gui/gui.py +152 -49
- supervisely/nn/training/gui/hyperparameters_selector.py +1 -1
- supervisely/nn/training/gui/model_selector.py +8 -6
- supervisely/nn/training/gui/train_val_splits_selector.py +144 -71
- supervisely/nn/training/gui/training_artifacts.py +3 -1
- supervisely/nn/training/train_app.py +225 -46
- supervisely/project/pointcloud_episode_project.py +12 -8
- supervisely/project/pointcloud_project.py +12 -8
- supervisely/project/project.py +221 -75
- supervisely/template/experiment/experiment.html.jinja +105 -55
- supervisely/template/experiment/experiment_generator.py +258 -112
- supervisely/template/experiment/header.html.jinja +31 -13
- supervisely/template/experiment/sly-style.css +7 -2
- supervisely/versions.json +3 -1
- supervisely/video/sampling.py +42 -20
- supervisely/video/video.py +41 -12
- supervisely/video_annotation/video_figure.py +38 -4
- supervisely/volume/stl_converter.py +2 -0
- supervisely/worker_api/agent_rpc.py +24 -1
- supervisely/worker_api/rpc_servicer.py +31 -7
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/METADATA +22 -14
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/RECORD +167 -148
- supervisely_lib/__init__.py +6 -1
- supervisely/app/widgets/experiment_selector/style.css +0 -27
- supervisely/app/widgets/experiment_selector/template.html +0 -61
- supervisely/nn/tracker/bot_sort/__init__.py +0 -21
- supervisely/nn/tracker/bot_sort/fast_reid_interface.py +0 -152
- supervisely/nn/tracker/bot_sort/matching.py +0 -127
- supervisely/nn/tracker/bot_sort/sly_tracker.py +0 -401
- supervisely/nn/tracker/deep_sort/__init__.py +0 -6
- supervisely/nn/tracker/deep_sort/deep_sort/__init__.py +0 -1
- supervisely/nn/tracker/deep_sort/deep_sort/detection.py +0 -49
- supervisely/nn/tracker/deep_sort/deep_sort/iou_matching.py +0 -81
- supervisely/nn/tracker/deep_sort/deep_sort/linear_assignment.py +0 -202
- supervisely/nn/tracker/deep_sort/deep_sort/nn_matching.py +0 -176
- supervisely/nn/tracker/deep_sort/deep_sort/track.py +0 -166
- supervisely/nn/tracker/deep_sort/deep_sort/tracker.py +0 -145
- supervisely/nn/tracker/deep_sort/deep_sort.py +0 -301
- supervisely/nn/tracker/deep_sort/generate_clip_detections.py +0 -90
- supervisely/nn/tracker/deep_sort/preprocessing.py +0 -70
- supervisely/nn/tracker/deep_sort/sly_tracker.py +0 -273
- supervisely/nn/tracker/tracker.py +0 -285
- supervisely/nn/tracker/utils/kalman_filter.py +0 -492
- supervisely/nn/tracking/__init__.py +0 -1
- supervisely/nn/tracking/boxmot.py +0 -114
- supervisely/nn/tracking/tracking.py +0 -24
- /supervisely/{nn/tracker/utils → app/widgets/deploy_model}/__init__.py +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/LICENSE +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/WHEEL +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/entry_points.txt +0 -0
- {supervisely-6.73.410.dist-info → supervisely-6.73.470.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
from typing import List, Union, Dict, Tuple
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from collections import defaultdict
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
import supervisely as sly
|
|
7
|
+
from supervisely.nn.model.prediction import Prediction
|
|
8
|
+
from supervisely.annotation.label import LabelingStatus
|
|
9
|
+
from supervisely import VideoAnnotation
|
|
10
|
+
from supervisely import logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def predictions_to_video_annotation(
|
|
14
|
+
predictions: List[Prediction],
|
|
15
|
+
) -> VideoAnnotation:
|
|
16
|
+
"""
|
|
17
|
+
Convert list of Prediction objects to VideoAnnotation.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
predictions: List of Prediction objects, one per frame
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
VideoAnnotation object with tracked objects
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
if not predictions:
|
|
28
|
+
raise ValueError("Empty predictions list provided")
|
|
29
|
+
|
|
30
|
+
frame_shape = predictions[0].annotation.img_size
|
|
31
|
+
img_h, img_w = frame_shape
|
|
32
|
+
video_objects = {}
|
|
33
|
+
frames = []
|
|
34
|
+
|
|
35
|
+
for pred in predictions:
|
|
36
|
+
frame_figures = []
|
|
37
|
+
frame_idx = pred.frame_index
|
|
38
|
+
|
|
39
|
+
# Get data using public properties
|
|
40
|
+
boxes = pred.boxes # Public property - np.array (N, 4) in tlbr format
|
|
41
|
+
classes = pred.classes # Public property - list of class names
|
|
42
|
+
track_ids = pred.track_ids # Public property - can be None
|
|
43
|
+
|
|
44
|
+
# Skip frame if no detections
|
|
45
|
+
if len(boxes) == 0:
|
|
46
|
+
frames.append(sly.Frame(frame_idx, []))
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
for bbox, class_name, track_id in zip(boxes, classes, track_ids):
|
|
50
|
+
# Clip bbox to image boundaries
|
|
51
|
+
# Note: pred.boxes returns tlbr format (top, left, bottom, right)
|
|
52
|
+
top, left, bottom, right = bbox
|
|
53
|
+
dims = np.array([img_h, img_w, img_h, img_w]) - 1
|
|
54
|
+
top, left, bottom, right = np.clip([top, left, bottom, right], 0, dims)
|
|
55
|
+
|
|
56
|
+
# Convert to integer coordinates
|
|
57
|
+
top, left, bottom, right = int(top), int(left), int(bottom), int(right)
|
|
58
|
+
|
|
59
|
+
# Get or create VideoObject
|
|
60
|
+
if track_id not in video_objects:
|
|
61
|
+
# Find obj_class from prediction annotation
|
|
62
|
+
obj_class = None
|
|
63
|
+
for label in pred.annotation.labels:
|
|
64
|
+
if label.obj_class.name == class_name:
|
|
65
|
+
obj_class = label.obj_class
|
|
66
|
+
break
|
|
67
|
+
|
|
68
|
+
if obj_class is None:
|
|
69
|
+
# Create obj_class if not found (fallback)
|
|
70
|
+
obj_class = sly.ObjClass(class_name, sly.Rectangle)
|
|
71
|
+
|
|
72
|
+
video_objects[track_id] = sly.VideoObject(obj_class)
|
|
73
|
+
|
|
74
|
+
video_object = video_objects[track_id]
|
|
75
|
+
rect = sly.Rectangle(top=top, left=left, bottom=bottom, right=right)
|
|
76
|
+
frame_figures.append(sly.VideoFigure(video_object, rect, frame_idx, track_id=str(track_id), status=LabelingStatus.AUTO))
|
|
77
|
+
|
|
78
|
+
frames.append(sly.Frame(frame_idx, frame_figures))
|
|
79
|
+
|
|
80
|
+
objects = list(video_objects.values())
|
|
81
|
+
return VideoAnnotation(
|
|
82
|
+
img_size=frame_shape,
|
|
83
|
+
frames_count=len(predictions),
|
|
84
|
+
objects=sly.VideoObjectCollection(objects),
|
|
85
|
+
frames=sly.FrameCollection(frames)
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def video_annotation_to_mot(
|
|
89
|
+
annotation: VideoAnnotation,
|
|
90
|
+
output_path: Union[str, Path] = None,
|
|
91
|
+
class_to_id_mapping: Dict[str, int] = None
|
|
92
|
+
) -> Union[str, List[str]]:
|
|
93
|
+
"""
|
|
94
|
+
Convert Supervisely VideoAnnotation to MOT format.
|
|
95
|
+
MOT format: frame_id,track_id,left,top,width,height,confidence,class_id,visibility
|
|
96
|
+
"""
|
|
97
|
+
mot_lines = []
|
|
98
|
+
|
|
99
|
+
# Create default class mapping if not provided
|
|
100
|
+
if class_to_id_mapping is None:
|
|
101
|
+
unique_classes = set()
|
|
102
|
+
for frame in annotation.frames:
|
|
103
|
+
for figure in frame.figures:
|
|
104
|
+
unique_classes.add(figure.video_object.obj_class.name)
|
|
105
|
+
class_to_id_mapping = {cls_name: idx + 1 for idx, cls_name in enumerate(sorted(unique_classes))}
|
|
106
|
+
|
|
107
|
+
# Extract tracks
|
|
108
|
+
for frame in annotation.frames:
|
|
109
|
+
frame_id = frame.index + 1 # MOT uses 1-based frame indexing
|
|
110
|
+
|
|
111
|
+
for figure in frame.figures:
|
|
112
|
+
# Get track ID from VideoFigure.track_id (official API)
|
|
113
|
+
if figure.track_id is not None:
|
|
114
|
+
track_id = int(figure.track_id)
|
|
115
|
+
else:
|
|
116
|
+
track_id = figure.video_object.key().int
|
|
117
|
+
|
|
118
|
+
# Get bounding box
|
|
119
|
+
if isinstance(figure.geometry, sly.Rectangle):
|
|
120
|
+
bbox = figure.geometry
|
|
121
|
+
else:
|
|
122
|
+
bbox = figure.geometry.to_bbox()
|
|
123
|
+
|
|
124
|
+
left = bbox.left
|
|
125
|
+
top = bbox.top
|
|
126
|
+
width = bbox.width
|
|
127
|
+
height = bbox.height
|
|
128
|
+
|
|
129
|
+
# Get class ID
|
|
130
|
+
class_name = figure.video_object.obj_class.name
|
|
131
|
+
class_id = class_to_id_mapping.get(class_name, 1)
|
|
132
|
+
|
|
133
|
+
# Get confidence (default)
|
|
134
|
+
confidence = 1.0
|
|
135
|
+
|
|
136
|
+
# Visibility (assume visible)
|
|
137
|
+
visibility = 1
|
|
138
|
+
|
|
139
|
+
# Create MOT line
|
|
140
|
+
mot_line = f"{frame_id},{track_id},{left:.2f},{top:.2f},{width:.2f},{height:.2f},{confidence:.3f},{class_id},{visibility}"
|
|
141
|
+
mot_lines.append(mot_line)
|
|
142
|
+
|
|
143
|
+
# Save to file if path provided
|
|
144
|
+
if output_path:
|
|
145
|
+
output_path = Path(output_path)
|
|
146
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
147
|
+
|
|
148
|
+
with open(output_path, 'w') as f:
|
|
149
|
+
for line in mot_lines:
|
|
150
|
+
f.write(line + '\n')
|
|
151
|
+
|
|
152
|
+
logger.info(f"Saved MOT format to: {output_path} ({len(mot_lines)} detections)")
|
|
153
|
+
return str(output_path)
|
|
154
|
+
|
|
155
|
+
return mot_lines
|
|
156
|
+
|
|
157
|
+
def mot_to_video_annotation(
|
|
158
|
+
mot_file_path: Union[str, Path],
|
|
159
|
+
img_size: Tuple[int, int] = (1080, 1920),
|
|
160
|
+
class_mapping: Dict[int, str] = None,
|
|
161
|
+
default_class_name: str = "person"
|
|
162
|
+
) -> VideoAnnotation:
|
|
163
|
+
"""
|
|
164
|
+
Convert MOT format tracking data to Supervisely VideoAnnotation.
|
|
165
|
+
MOT format: frame_id,track_id,left,top,width,height,confidence,class_id,visibility
|
|
166
|
+
"""
|
|
167
|
+
mot_file_path = Path(mot_file_path)
|
|
168
|
+
|
|
169
|
+
if not mot_file_path.exists():
|
|
170
|
+
raise FileNotFoundError(f"MOT file not found: {mot_file_path}")
|
|
171
|
+
|
|
172
|
+
logger.info(f"Loading MOT data from: {mot_file_path}")
|
|
173
|
+
logger.info(f"Image size: {img_size} (height, width)")
|
|
174
|
+
|
|
175
|
+
# Default class mapping
|
|
176
|
+
if class_mapping is None:
|
|
177
|
+
class_mapping = {1: default_class_name}
|
|
178
|
+
|
|
179
|
+
# Parse MOT file
|
|
180
|
+
video_objects = {} # track_id -> VideoObject
|
|
181
|
+
frames_data = defaultdict(list) # frame_idx -> list of figures
|
|
182
|
+
max_frame_idx = 0
|
|
183
|
+
img_h, img_w = img_size
|
|
184
|
+
|
|
185
|
+
with open(mot_file_path, 'r') as f:
|
|
186
|
+
for line_num, line in enumerate(f, 1):
|
|
187
|
+
line = line.strip()
|
|
188
|
+
if not line or line.startswith('#'):
|
|
189
|
+
continue
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
parts = line.split(',')
|
|
193
|
+
if len(parts) < 6: # Minimum required fields
|
|
194
|
+
continue
|
|
195
|
+
|
|
196
|
+
frame_id = int(parts[0])
|
|
197
|
+
track_id = int(parts[1])
|
|
198
|
+
left = float(parts[2])
|
|
199
|
+
top = float(parts[3])
|
|
200
|
+
width = float(parts[4])
|
|
201
|
+
height = float(parts[5])
|
|
202
|
+
|
|
203
|
+
# Optional fields
|
|
204
|
+
confidence = float(parts[6]) if len(parts) > 6 and parts[6] != '-1' else 1.0
|
|
205
|
+
class_id = int(parts[7]) if len(parts) > 7 and parts[7] != '-1' else 1
|
|
206
|
+
visibility = float(parts[8]) if len(parts) > 8 and parts[8] != '-1' else 1.0
|
|
207
|
+
|
|
208
|
+
frame_idx = frame_id - 1 # Convert to 0-based indexing
|
|
209
|
+
max_frame_idx = max(max_frame_idx, frame_idx)
|
|
210
|
+
|
|
211
|
+
# Skip low confidence detections
|
|
212
|
+
if confidence < 0.1:
|
|
213
|
+
continue
|
|
214
|
+
|
|
215
|
+
# Calculate coordinates with safer clipping
|
|
216
|
+
right = left + width
|
|
217
|
+
bottom = top + height
|
|
218
|
+
|
|
219
|
+
# Clip to image boundaries
|
|
220
|
+
left = max(0, int(left))
|
|
221
|
+
top = max(0, int(top))
|
|
222
|
+
right = min(int(right), img_w - 1)
|
|
223
|
+
bottom = min(int(bottom), img_h - 1)
|
|
224
|
+
|
|
225
|
+
# Skip invalid boxes
|
|
226
|
+
if right <= left or bottom <= top:
|
|
227
|
+
continue
|
|
228
|
+
|
|
229
|
+
# Get class name
|
|
230
|
+
class_name = class_mapping.get(class_id, default_class_name)
|
|
231
|
+
|
|
232
|
+
# Create VideoObject if not exists
|
|
233
|
+
if track_id not in video_objects:
|
|
234
|
+
obj_class = sly.ObjClass(class_name, sly.Rectangle)
|
|
235
|
+
video_objects[track_id] = sly.VideoObject(obj_class)
|
|
236
|
+
|
|
237
|
+
video_object = video_objects[track_id]
|
|
238
|
+
|
|
239
|
+
# Create rectangle and figure with track_id
|
|
240
|
+
rect = sly.Rectangle(top=top, left=left, bottom=bottom, right=right)
|
|
241
|
+
figure = sly.VideoFigure(video_object, rect, frame_idx, track_id=str(track_id))
|
|
242
|
+
|
|
243
|
+
frames_data[frame_idx].append(figure)
|
|
244
|
+
|
|
245
|
+
except (ValueError, IndexError) as e:
|
|
246
|
+
logger.warning(f"Skipped invalid MOT line {line_num}: {line} - {e}")
|
|
247
|
+
continue
|
|
248
|
+
|
|
249
|
+
# Create frames
|
|
250
|
+
frames = []
|
|
251
|
+
if frames_data:
|
|
252
|
+
frames_count = max(frames_data.keys()) + 1
|
|
253
|
+
|
|
254
|
+
for frame_idx in range(frames_count):
|
|
255
|
+
figures = frames_data.get(frame_idx, [])
|
|
256
|
+
frames.append(sly.Frame(frame_idx, figures))
|
|
257
|
+
else:
|
|
258
|
+
frames_count = 1
|
|
259
|
+
frames = [sly.Frame(0, [])]
|
|
260
|
+
|
|
261
|
+
# Create VideoAnnotation
|
|
262
|
+
objects = list(video_objects.values())
|
|
263
|
+
|
|
264
|
+
annotation = VideoAnnotation(
|
|
265
|
+
img_size=img_size,
|
|
266
|
+
frames_count=frames_count,
|
|
267
|
+
objects=sly.VideoObjectCollection(objects),
|
|
268
|
+
frames=sly.FrameCollection(frames)
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
logger.info(f"Created VideoAnnotation with {len(objects)} tracks and {frames_count} frames")
|
|
272
|
+
|
|
273
|
+
return annotation
|