Skip to content

Commit 19bdf66

Browse files
committed
Merge branch 'main' into releases/v2.5.0
2 parents 0b760a0 + 3bff8ea commit 19bdf66

File tree

6 files changed

+106
-25
lines changed

6 files changed

+106
-25
lines changed

geti_sdk/geti.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -713,10 +713,12 @@ def create_single_task_project_from_dataset(
713713
workspace_id=self.workspace_id,
714714
annotation_reader=annotation_reader,
715715
)
716-
annotation_client.upload_annotations_for_images(images)
716+
annotation_client.upload_annotations_for_images(images, max_threads=max_threads)
717717

718718
if len(videos) > 0:
719-
annotation_client.upload_annotations_for_videos(videos)
719+
annotation_client.upload_annotations_for_videos(
720+
videos, max_threads=max_threads
721+
)
720722

721723
configuration_client.set_project_auto_train(auto_train=enable_auto_train)
722724
return project
@@ -854,7 +856,9 @@ def create_task_chain_project_from_dataset(
854856
annotation_reader=reader,
855857
)
856858
annotation_client.upload_annotations_for_images(
857-
images=images, append_annotations=append_annotations
859+
images=images,
860+
append_annotations=append_annotations,
861+
max_threads=max_threads,
858862
)
859863
append_annotations = True
860864
previous_task_type = task_type

geti_sdk/import_export/import_export_module.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ def upload_project_data(
238238
if len(images) > 0:
239239
annotation_client.upload_annotations_for_images(
240240
images=images,
241+
max_threads=max_threads,
241242
)
242243
if len(videos) > 0:
243244
are_videos_processed = False
@@ -253,7 +254,7 @@ def upload_project_data(
253254
are_videos_processed = uploaded_ids.issubset(project_video_ids)
254255
time.sleep(1)
255256
annotation_client.upload_annotations_for_videos(
256-
videos=videos,
257+
videos=videos, max_threads=max_threads
257258
)
258259

259260
configuration_file = os.path.join(target_folder, "configuration.json")

geti_sdk/rest_clients/annotation_clients/annotation_client.py

+44-12
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,19 @@ def get_latest_annotations_for_video(self, video: Video) -> List[AnnotationScene
5858
return annotation_scenes
5959

6060
def upload_annotations_for_video(
61-
self, video: Video, append_annotations: bool = False
61+
self, video: Video, append_annotations: bool = False, max_threads: int = 5
6262
):
6363
"""
6464
Upload annotations for a video. If append_annotations is set to True,
6565
annotations will be appended to the existing annotations for the video in the
6666
project. If set to False, existing annotations will be overwritten.
6767
6868
:param video: Video to upload annotations for
69-
:param append_annotations:
69+
:param append_annotations: True to append annotations from the local disk to
70+
the existing annotations on the server, False to overwrite the server
71+
annotations by those on the local disk.
72+
:param max_threads: Maximum number of threads to use for uploading. Defaults to 5.
73+
Set to -1 to use all available threads.
7074
:return:
7175
"""
7276
annotation_filenames = self.annotation_reader.get_data_filenames()
@@ -83,27 +87,38 @@ def upload_annotations_for_video(
8387
]
8488
)
8589
upload_count = self._upload_annotations_for_2d_media_list(
86-
media_list=video_frames, append_annotations=append_annotations
90+
media_list=video_frames,
91+
append_annotations=append_annotations,
92+
max_threads=max_threads,
8793
)
8894
return upload_count
8995

9096
def upload_annotations_for_videos(
91-
self, videos: Sequence[Video], append_annotations: bool = False
97+
self,
98+
videos: Sequence[Video],
99+
append_annotations: bool = False,
100+
max_threads: int = 5,
92101
):
93102
"""
94103
Upload annotations for a list of videos. If append_annotations is set to True,
95104
annotations will be appended to the existing annotations for the video in the
96105
project. If set to False, existing annotations will be overwritten.
97106
98107
:param videos: List of videos to upload annotations for
99-
:param append_annotations:
108+
:param append_annotations: True to append annotations from the local disk to
109+
the existing annotations on the server, False to overwrite the server
110+
annotations by those on the local disk.
111+
:param max_threads: Maximum number of threads to use for uploading. Defaults to 5.
112+
Set to -1 to use all available threads.
100113
:return:
101114
"""
102115
logging.info("Starting video annotation upload...")
103116
upload_count = 0
104117
for video in videos:
105118
upload_count += self.upload_annotations_for_video(
106-
video=video, append_annotations=append_annotations
119+
video=video,
120+
append_annotations=append_annotations,
121+
max_threads=max_threads,
107122
)
108123
if upload_count > 0:
109124
logging.info(
@@ -113,20 +128,29 @@ def upload_annotations_for_videos(
113128
logging.info("No new video frame annotations were found.")
114129

115130
def upload_annotations_for_images(
116-
self, images: Sequence[Image], append_annotations: bool = False
131+
self,
132+
images: Sequence[Image],
133+
append_annotations: bool = False,
134+
max_threads: int = 5,
117135
):
118136
"""
119137
Upload annotations for a list of images. If append_annotations is set to True,
120138
annotations will be appended to the existing annotations for the image in the
121139
project. If set to False, existing annotations will be overwritten.
122140
123141
:param images: List of images to upload annotations for
124-
:param append_annotations:
142+
:param append_annotations: True to append annotations from the local disk to
143+
the existing annotations on the server, False to overwrite the server
144+
annotations by those on the local disk.
145+
:param max_threads: Maximum number of threads to use for uploading. Defaults to 5.
146+
Set to -1 to use all available threads.
125147
:return:
126148
"""
127149
logging.info("Starting image annotation upload...")
128150
upload_count = self._upload_annotations_for_2d_media_list(
129-
media_list=images, append_annotations=append_annotations
151+
media_list=images,
152+
append_annotations=append_annotations,
153+
max_threads=max_threads,
130154
)
131155
if upload_count > 0:
132156
logging.info(
@@ -271,7 +295,9 @@ def download_all_annotations(
271295
max_threads=max_threads,
272296
)
273297

274-
def upload_annotations_for_all_media(self, append_annotations: bool = False):
298+
def upload_annotations_for_all_media(
299+
self, append_annotations: bool = False, max_threads: int = 5
300+
):
275301
"""
276302
Upload annotations for all media in the project, If append_annotations is set
277303
to True, annotations will be appended to the existing annotations for the
@@ -280,16 +306,22 @@ def upload_annotations_for_all_media(self, append_annotations: bool = False):
280306
:param append_annotations: True to append annotations from the local disk to
281307
the existing annotations on the server, False to overwrite the server
282308
annotations by those on the local disk. Defaults to False.
309+
:param max_threads: Maximum number of threads to use for uploading. Defaults to 5.
310+
Set to -1 to use all available threads.
283311
"""
284312
image_list = self._get_all_media_by_type(media_type=Image)
285313
video_list = self._get_all_media_by_type(media_type=Video)
286314
if len(image_list) > 0:
287315
self.upload_annotations_for_images(
288-
images=image_list, append_annotations=append_annotations
316+
images=image_list,
317+
append_annotations=append_annotations,
318+
max_threads=max_threads,
289319
)
290320
if len(video_list) > 0:
291321
self.upload_annotations_for_videos(
292-
videos=video_list, append_annotations=append_annotations
322+
videos=video_list,
323+
append_annotations=append_annotations,
324+
max_threads=max_threads,
293325
)
294326

295327
def upload_annotation(

geti_sdk/rest_clients/annotation_clients/base_annotation_client.py

+48-6
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
from geti_sdk.data_models.containers.media_list import MediaList
3737
from geti_sdk.data_models.label import Label
3838
from geti_sdk.data_models.media import MediaInformation, MediaItem
39-
from geti_sdk.http_session import GetiSession
39+
from geti_sdk.http_session import GetiRequestException, GetiSession
4040
from geti_sdk.rest_clients.dataset_client import DatasetClient
4141
from geti_sdk.rest_converters import AnnotationRESTConverter
4242

@@ -300,7 +300,10 @@ def _append_annotation_for_2d_media_item(
300300
return annotation_scene
301301

302302
def _upload_annotations_for_2d_media_list(
303-
self, media_list: Sequence[MediaItem], append_annotations: bool
303+
self,
304+
media_list: Sequence[MediaItem],
305+
append_annotations: bool,
306+
max_threads: int = 5,
304307
) -> int:
305308
"""
306309
Upload annotations to the server.
@@ -310,12 +313,21 @@ def _upload_annotations_for_2d_media_list(
310313
:param append_annotations: True to append annotations from the local disk to
311314
the existing annotations on the server, False to overwrite the server
312315
annotations by those on the local disk.
316+
:param max_threads: Maximum number of threads to use for uploading. Defaults to 5.
317+
Set to -1 to use all available threads.
313318
:return: Returns the number of uploaded annotations.
314319
"""
320+
if max_threads <= 0:
321+
# ThreadPoolExecutor will use minimum 5 threads for 1 core cpu
322+
# and maximum 32 threads for multi-core cpu.
323+
max_threads = None
315324
upload_count = 0
325+
skip_count = 0
316326
tqdm_prefix = "Uploading media annotations"
317-
with logging_redirect_tqdm(tqdm_class=tqdm):
318-
for media_item in tqdm(media_list, desc=tqdm_prefix):
327+
328+
def upload_annotation(media_item: MediaItem) -> None:
329+
nonlocal upload_count, skip_count
330+
try:
319331
if not append_annotations:
320332
response = self._upload_annotation_for_2d_media_item(
321333
media_item=media_item
@@ -324,8 +336,38 @@ def _upload_annotations_for_2d_media_list(
324336
response = self._append_annotation_for_2d_media_item(
325337
media_item=media_item
326338
)
327-
if response.annotations:
328-
upload_count += 1
339+
except GetiRequestException as error:
340+
skip_count += 1
341+
if error.status_code == 500:
342+
logging.error(
343+
f"Failed to upload annotation for {media_item.name}. "
344+
)
345+
return
346+
else:
347+
raise error
348+
if response is not None:
349+
upload_count += 1
350+
351+
t_start = time.time()
352+
with ThreadPoolExecutor(max_workers=max_threads) as executor:
353+
with logging_redirect_tqdm(tqdm_class=tqdm):
354+
list(
355+
tqdm(
356+
executor.map(upload_annotation, media_list),
357+
total=len(media_list),
358+
desc=tqdm_prefix,
359+
)
360+
)
361+
362+
t_elapsed = time.time() - t_start
363+
if upload_count > 0:
364+
logging.info(
365+
f"Uploaded {upload_count} annotations in {t_elapsed:.1f} seconds."
366+
)
367+
if skip_count > 0:
368+
logging.info(
369+
f"Skipped {skip_count} media items, unable to upload annotations."
370+
)
329371
return upload_count
330372

331373
def annotation_scene_from_rest_response(

tests/helpers/project_service.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ def add_annotated_media(
435435
]
436436
# Upload annotations
437437
self.annotation_client.upload_annotations_for_images(
438-
images=images, append_annotations=task_index > 0
438+
images=images, append_annotations=task_index > 0, max_threads=1
439439
)
440440

441441
def set_auto_train(self, auto_train: bool = True) -> None:

tests/pre-merge/integration/rest_clients/test_annotation_client.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def test_upload_and_retrieve_annotations_for_video(
8888
if fxt_test_mode != SdkTestMode.OFFLINE:
8989
time.sleep(1)
9090

91-
annotation_client.upload_annotations_for_video(video=video)
91+
annotation_client.upload_annotations_for_video(video=video, max_threads=1)
9292

9393
if fxt_test_mode != SdkTestMode.OFFLINE:
9494
time.sleep(1)
@@ -198,7 +198,9 @@ def test_upload_and_retrieve_annotations_for_videos(
198198
annotation_client = fxt_project_service.annotation_client
199199
annotation_client.annotation_reader = annotation_reader
200200

201-
annotation_client.upload_annotations_for_videos(videos=[video_1, video_2])
201+
annotation_client.upload_annotations_for_videos(
202+
videos=[video_1, video_2], max_threads=1
203+
)
202204

203205
if fxt_test_mode != SdkTestMode.OFFLINE:
204206
time.sleep(10)

0 commit comments

Comments
 (0)