Skip to content

Commit dc039a3

Browse files
authored
Merge pull request #226 from superannotateai/f-193
F 193
2 parents a756370 + dbd9732 commit dc039a3

File tree

4 files changed

+106
-46
lines changed

4 files changed

+106
-46
lines changed

src/superannotate/lib/app/interface/sdk_interface.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2249,6 +2249,8 @@ def attach_image_urls_to_project(
22492249
logger.warning(
22502250
constances.ALREADY_EXISTING_FILES_WARNING.format(len(duplicate_images))
22512251
)
2252+
logger.info(constances.ATTACHING_FILES_MESSAGE.format(len(images_to_upload)))
2253+
22522254
use_case = controller.interactive_attach_urls(
22532255
project_name=project_name,
22542256
folder_name=folder_name,
@@ -2392,11 +2394,14 @@ def upload_annotations_from_folder_to_project(
23922394
annotation_paths=annotation_paths, # noqa: E203
23932395
client_s3_bucket=from_s3_bucket,
23942396
)
2395-
with tqdm(
2396-
total=len(annotation_paths), desc="Uploading annotations"
2397-
) as progress_bar:
2398-
for _ in use_case.execute():
2399-
progress_bar.update(1)
2397+
if use_case.is_valid():
2398+
with tqdm(
2399+
total=len(use_case.annotations_to_upload), desc="Uploading annotations"
2400+
) as progress_bar:
2401+
for _ in use_case.execute():
2402+
progress_bar.update(1)
2403+
else:
2404+
raise AppException(use_case.response.errors)
24002405
return use_case.data
24012406

24022407

@@ -3513,6 +3518,7 @@ def attach_document_urls_to_project(
35133518
logger.warning(
35143519
constances.ALREADY_EXISTING_FILES_WARNING.format(len(duplicate_images))
35153520
)
3521+
logger.info(constances.ATTACHING_FILES_MESSAGE.format(len(images_to_upload)))
35163522
use_case = controller.interactive_attach_urls(
35173523
project_name=project_name,
35183524
folder_name=folder_name,

src/superannotate/lib/core/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,18 @@
8383
ATTACH_USER_LIMIT_ERROR_MESSAGE = "The number of items you want to attach exceeds the limit of your subscription plan."
8484

8585

86-
COPY_FOLDER_LIMIT_ERROR_MESSAGE = "The number of items you want to copy exceeds the limit of 50 000 items per folder."
86+
COPY_FOLDER_LIMIT_ERROR_MESSAGE = (
87+
"The number of items you want to copy exceeds the limit of 50 000 items per folder."
88+
)
8789
COPY_PROJECT_LIMIT_ERROR_MESSAGE = "The number of items you want to copy exceeds the limit of 500 000 items per project."
88-
COPY_SUPER_LIMIT_ERROR_MESSAGE = "The number of items you want to copy exceeds the limit of your subscription plan."
90+
COPY_SUPER_LIMIT_ERROR_MESSAGE = (
91+
"The number of items you want to copy exceeds the limit of your subscription plan."
92+
)
8993

9094

91-
MOVE_FOLDER_LIMIT_ERROR_MESSAGE = "The number of items you want to move exceeds the limit of 50 000 items per folder."
95+
MOVE_FOLDER_LIMIT_ERROR_MESSAGE = (
96+
"The number of items you want to move exceeds the limit of 50 000 items per folder."
97+
)
9298
MOVE_PROJECT_LIMIT_ERROR_MESSAGE = "The number of items you want to move exceeds the limit of 500 000 items per project."
9399

94100
__alL__ = (

src/superannotate/lib/core/usecases/images.py

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -835,14 +835,17 @@ def execute(self):
835835

836836
outline_color = 4 * (255,)
837837
for instance in self.annotations["instances"]:
838-
if (not instance.get("className")) or (not class_color_map.get(instance["className"])):
838+
if (not instance.get("className")) or (
839+
not class_color_map.get(instance["className"])
840+
):
839841
continue
840842
color = class_color_map.get(instance["className"])
841843
if not color:
842844
class_color_map[instance["className"]] = self.generate_color()
843845
for image in images:
844846
fill_color = (
845-
*class_color_map[instance["className"]], 255 if image.type == "fuse" else self.TRANSPARENCY
847+
*class_color_map[instance["className"]],
848+
255 if image.type == "fuse" else self.TRANSPARENCY,
846849
)
847850
if instance["type"] == "bbox":
848851
image.content.draw_bbox(
@@ -911,7 +914,9 @@ def execute(self):
911914
weight, height = image.get_size()
912915
empty_image_arr = np.full((height, weight, 4), [0, 0, 0, 255], np.uint8)
913916
for annotation in self.annotations["instances"]:
914-
if (not annotation.get("className")) or (not class_color_map.get(annotation["className"])):
917+
if (not annotation.get("className")) or (
918+
not class_color_map.get(annotation["className"])
919+
):
915920
continue
916921
fill_color = *class_color_map[annotation["className"]], 255
917922
for part in annotation["parts"]:
@@ -2002,17 +2007,28 @@ def validate_limitations(self):
20022007
if self._move:
20032008
if self._from_project.uuid == self._to_project.uuid:
20042009
if self._from_folder.uuid == self._to_folder.uuid:
2005-
raise AppValidationException("Cannot move image if source_project == destination_project.")
2010+
raise AppValidationException(
2011+
"Cannot move image if source_project == destination_project."
2012+
)
20062013
elif response.data.folder_limit.remaining_image_count < 1:
2007-
raise AppValidationException(constances.MOVE_FOLDER_LIMIT_ERROR_MESSAGE)
2014+
raise AppValidationException(
2015+
constances.MOVE_FOLDER_LIMIT_ERROR_MESSAGE
2016+
)
20082017
elif response.data.project_limit.remaining_image_count < 1:
2009-
raise AppValidationException(constances.MOVE_PROJECT_LIMIT_ERROR_MESSAGE)
2018+
raise AppValidationException(
2019+
constances.MOVE_PROJECT_LIMIT_ERROR_MESSAGE
2020+
)
20102021
else:
20112022
if response.data.folder_limit.remaining_image_count < 1:
20122023
raise AppValidationException(constances.COPY_FOLDER_LIMIT_ERROR_MESSAGE)
20132024
if response.data.project_limit.remaining_image_count < 1:
2014-
raise AppValidationException(constances.COPY_PROJECT_LIMIT_ERROR_MESSAGE)
2015-
if response.data.super_user_limit and response.data.super_user_limit.remaining_image_count < 1:
2025+
raise AppValidationException(
2026+
constances.COPY_PROJECT_LIMIT_ERROR_MESSAGE
2027+
)
2028+
if (
2029+
response.data.super_user_limit
2030+
and response.data.super_user_limit.remaining_image_count < 1
2031+
):
20162032
raise AppValidationException(constances.COPY_SUPER_LIMIT_ERROR_MESSAGE)
20172033

20182034
@property
@@ -2689,6 +2705,8 @@ def execute(self):
26892705
else:
26902706
from_s3 = None
26912707

2708+
for _ in range(len(annotations_to_upload) - len(response.data.images)):
2709+
yield
26922710
with concurrent.futures.ThreadPoolExecutor(
26932711
max_workers=self.MAX_WORKERS
26942712
) as executor:
@@ -2720,7 +2738,6 @@ def execute(self):
27202738
failed_annotations = [
27212739
annotation.path for annotation in failed_annotations
27222740
]
2723-
27242741
self._response.data = (
27252742
uploaded_annotations,
27262743
failed_annotations,
@@ -2732,42 +2749,41 @@ def execute(self):
27322749
def upload_to_s3(
27332750
self, image_id: int, image_info, bucket, from_s3, image_id_name_map
27342751
):
2735-
if from_s3:
2736-
file = io.BytesIO()
2737-
s3_object = from_s3.Object(
2738-
self._client_s3_bucket, image_id_name_map[image_id].path
2739-
)
2740-
s3_object.download_fileobj(file)
2741-
file.seek(0)
2742-
annotation_json = json.load(file)
2743-
else:
2744-
annotation_json = json.load(open(image_id_name_map[image_id].path))
2745-
2746-
self.fill_classes_data(annotation_json)
2747-
2748-
if not self._is_valid_json(annotation_json):
2749-
logger.warning(f"Invalid json {image_id_name_map[image_id].path}")
2750-
return image_id_name_map[image_id], False
2751-
bucket.put_object(
2752-
Key=image_info["annotation_json_path"], Body=json.dumps(annotation_json),
2753-
)
2754-
if self._project.project_type == constances.ProjectType.PIXEL.value:
2755-
mask_filename = (
2756-
image_id_name_map[image_id].name + constances.ANNOTATION_MASK_POSTFIX
2757-
)
2752+
try:
27582753
if from_s3:
27592754
file = io.BytesIO()
27602755
s3_object = from_s3.Object(
2761-
self._client_s3_bucket, f"{self._folder_path}/{mask_filename}"
2756+
self._client_s3_bucket, image_id_name_map[image_id].path
27622757
)
27632758
s3_object.download_fileobj(file)
27642759
file.seek(0)
2760+
annotation_json = json.load(file)
27652761
else:
2766-
with open(f"{self._folder_path}/{mask_filename}", "rb") as mask_file:
2767-
file = io.BytesIO(mask_file.read())
2768-
2769-
bucket.put_object(Key=image_info["annotation_bluemap_path"], Body=file)
2770-
return image_id_name_map[image_id], True
2762+
annotation_json = json.load(open(image_id_name_map[image_id].path))
2763+
self.fill_classes_data(annotation_json)
2764+
if not self._is_valid_json(annotation_json):
2765+
logger.warning(f"Invalid json {image_id_name_map[image_id].path}")
2766+
return image_id_name_map[image_id], False
2767+
bucket.put_object(
2768+
Key=image_info["annotation_json_path"],
2769+
Body=json.dumps(annotation_json),
2770+
)
2771+
if self._project.project_type == constances.ProjectType.PIXEL.value:
2772+
mask_path = image_id_name_map[image_id].path.replace(
2773+
"___pixel.json", constances.ANNOTATION_MASK_POSTFIX
2774+
)
2775+
if from_s3:
2776+
file = io.BytesIO()
2777+
s3_object = from_s3.Object(self._client_s3_bucket, mask_path)
2778+
s3_object.download_fileobj(file)
2779+
file.seek(0)
2780+
else:
2781+
with open(mask_path, "rb") as mask_file:
2782+
file = io.BytesIO(mask_file.read())
2783+
bucket.put_object(Key=image_info["annotation_bluemap_path"], Body=file)
2784+
return image_id_name_map[image_id], True
2785+
except Exception as _:
2786+
return image_id_name_map[image_id], False
27712787

27722788
def report_missing_data(self):
27732789
if self.missing_classes:
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import os
2+
from os.path import dirname
3+
4+
import src.superannotate as sa
5+
from tests.integration.base import BaseTestCase
6+
7+
8+
class TestRecursiveFolderPixel(BaseTestCase):
9+
PROJECT_NAME = "pixel_recursive_test"
10+
PROJECT_DESCRIPTION = "Desc"
11+
PROJECT_TYPE = "Pixel"
12+
S3_FOLDER_PATH = "pixel_all_fuse"
13+
JSON_POSTFIX = "*.json"
14+
15+
16+
def test_recursive_upload_pixel(self):
17+
uploaded, _, duplicated = sa.upload_images_from_folder_to_project(self.PROJECT_NAME,
18+
self.S3_FOLDER_PATH,
19+
from_s3_bucket="test-openseadragon-1212",
20+
recursive_subfolders=True
21+
)
22+
23+
uploaded, failed, missing = sa.upload_annotations_from_folder_to_project(self.PROJECT_NAME,
24+
self.S3_FOLDER_PATH,
25+
from_s3_bucket="test-openseadragon-1212",
26+
recursive_subfolders=True
27+
)
28+
self.assertEqual(115, len(uploaded))
29+
self.assertEqual(0, len(failed))
30+
self.assertEqual(0, len(missing))
31+
32+

0 commit comments

Comments
 (0)