Skip to content

Commit 6aa13f9

Browse files
dshabinVaghinak Basentsyan
authored andcommitted
Add big image
1 parent b99f7da commit 6aa13f9

19 files changed

+205
-108
lines changed

pytest.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
minversion = 3.0
33
log_cli=true
44
python_files = test_*.py
5-
addopts = -n32 --dist=loadscope
5+
;addopts = -n32 --dist=loadscope

requirements_dev.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ tox==3.24.2
33
pytest==6.2.4
44
pytest-xdist==2.3.0
55
pytest-parallel==0.1.0
6+
pytest-rerunfailures==10.2

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,18 @@ def export_project(
130130
project_name, folders, include_fuse, False, annotation_statuses
131131
)
132132
export_name = export_res.data["name"]
133-
self.controller.download_export(
133+
134+
use_case = controller.download_export(
134135
project_name=project_name,
135136
export_name=export_name,
136137
folder_path=folder,
137138
extract_zip_contents=not disable_extract_zip_contents,
138139
to_s3_bucket=False,
139140
)
141+
if use_case.is_valid():
142+
for _ in use_case.execute():
143+
continue
144+
140145
sys.exit(0)
141146

142147
def upload_preannotations(

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -576,18 +576,22 @@ def copy_image(
576576
image_entity = s3_response.data
577577
del img_bytes
578578

579+
annotation_status = None
579580
if copy_annotation_status:
580581
res = controller.get_image(
581582
project_name=source_project_name,
582583
image_name=image_name,
583584
folder_path=source_folder_name,
584585
)
585-
image_entity.annotation_status_code = res.annotation_status_code
586+
annotation_status = constances.AnnotationStatus.get_name(
587+
res.annotation_status_code
588+
)
586589

587590
controller.attach_urls(
588591
project_name=destination_project,
589592
files=[image_entity],
590593
folder_name=destination_folder,
594+
annotation_status=annotation_status,
591595
upload_state_code=constances.UploadState.BASIC.value,
592596
)
593597

@@ -1462,6 +1466,8 @@ def upload_images_from_folder_to_project(
14621466
logger.info(
14631467
"Uploading %s images to project %s.", len(images_to_upload), project_folder_name
14641468
)
1469+
if not images_to_upload:
1470+
return [], [], duplicates
14651471
if use_case.is_valid():
14661472
with tqdm(total=len(images_to_upload), desc="Uploading images") as progress_bar:
14671473
for _ in use_case.execute():
@@ -1818,7 +1824,9 @@ def upload_videos_from_folder_to_project(
18181824
logger.warning(
18191825
f"{len(duplicates)} already existing images found that won't be uploaded."
18201826
)
1821-
1827+
if not images_to_upload:
1828+
logger.warning(f"{len(duplicates)} already existing images found that won't be uploaded.")
1829+
continue
18221830
if use_case.is_valid():
18231831
with tqdm(
18241832
total=len(images_to_upload), desc="Uploading images"
@@ -2835,14 +2843,13 @@ def benchmark(
28352843
]:
28362844
raise AppException(LIMITED_FUNCTIONS[project["project"].project_type])
28372845

2838-
if export_root is None:
2846+
if not export_root:
28392847
with tempfile.TemporaryDirectory() as temp_dir:
2840-
export_root = temp_dir
28412848
response = controller.benchmark(
28422849
project_name=project_name,
28432850
ground_truth_folder_name=gt_folder,
28442851
folder_names=folder_names,
2845-
export_root=export_root,
2852+
export_root=temp_dir,
28462853
image_list=image_list,
28472854
annot_type=annot_type,
28482855
show_plots=show_plots,
@@ -3526,6 +3533,9 @@ def upload_images_to_project(
35263533
"%s already existing images found that won't be uploaded.", len(duplicates)
35273534
)
35283535
logger.info(f"Uploading {len(images_to_upload)} images to project {project}.")
3536+
uploaded, failed_images, duplications = [], [], duplicates
3537+
if not images_to_upload:
3538+
return uploaded, failed_images, duplications
35293539
if use_case.is_valid():
35303540
with tqdm(total=len(images_to_upload), desc="Uploading images") as progress_bar:
35313541
for _ in use_case.execute():

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def validate(cls, value: Union[str]) -> Union[str]:
4242
super().validate(value)
4343
if value.lower() not in cls.VALID_CHOICES:
4444
raise TypeError(
45-
f"Image quality should be on of {', '.join(cls.VALID_CHOICES)}."
45+
f"Image quality available choices are {', '.join(cls.VALID_CHOICES)}."
4646
)
4747
return value.lower()
4848

src/superannotate/lib/core/plugin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def extract_frames(
248248

249249
frame_number = 0
250250
extracted_frame_number = 0
251-
extracted_frame_ratio = ratio
251+
extracted_frame_ratio = 1.0
252252
logger.info("Extracting frames from video to %s.", extract_path)
253253
extracted_frames_paths = []
254254

src/superannotate/lib/core/usecases.py

Lines changed: 108 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -805,7 +805,7 @@ def __init__(
805805
):
806806
super().__init__(),
807807
self._project = project
808-
self._folder_names = list(folder_names)
808+
self._folder_names = list(folder_names) if folder_names else None
809809
self._backend_service = backend_service_provider
810810
self._annotation_statuses = annotation_statuses
811811
self._include_fuse = include_fuse
@@ -2284,12 +2284,12 @@ def annotation_classes_id_name_map(self) -> dict:
22842284
classes_data = defaultdict(dict)
22852285
annotation_classes = self._annotation_classes.get_all()
22862286
for annotation_class in annotation_classes:
2287-
class_info = {"name": annotation_class.name}
2287+
class_info = {"name": annotation_class.name, "attribute_groups": {}}
22882288
if annotation_class.attribute_groups:
22892289
for attribute_group in annotation_class.attribute_groups:
22902290
attribute_group_data = defaultdict(dict)
22912291
for attribute in attribute_group["attributes"]:
2292-
attribute_group_data[attribute["name"]] = attribute["id"]
2292+
attribute_group_data[attribute["id"]] = attribute["name"]
22932293
class_info["attribute_groups"] = {
22942294
attribute_group["id"]: {
22952295
"name": attribute_group["name"],
@@ -2312,49 +2312,39 @@ def fill_classes_data(self, annotations: dict):
23122312
annotation_classes = self.annotation_classes_id_name_map
23132313
if "instances" not in annotations:
23142314
return
2315-
unknown_classes = {}
2316-
# for annotation in [i for i in annotations["instances"] if "className" in i]:
2317-
for annotation in [i for i in annotations["instances"] if "classId" in i]:
2318-
annotation_class_id = annotation["classId"]
2319-
if annotation_class_id not in annotation_classes:
2320-
if annotation_class_id not in unknown_classes:
2321-
unknown_classes[annotation_class_id] = {
2322-
"name": "unknown_class",
2323-
"attribute_groups": {},
2324-
}
2325-
# annotation_classes.update(unknown_classes)
23262315
templates = self.get_templates_mapping()
23272316
for annotation in (
23282317
i for i in annotations["instances"] if i.get("type", None) == "template"
23292318
):
23302319
template_name = templates.get(annotation.get("templateId"), None)
23312320
if template_name:
23322321
annotation["templateName"] = template_name
2333-
2334-
for annotation in [i for i in annotations["instances"] if "classId" in i]:
2335-
annotation_class_id = annotation["classId"]
2336-
if annotation_class_id not in annotation_classes:
2337-
continue
2338-
annotation["className"] = annotation_classes[annotation_class_id]["name"]
2339-
for attribute in [i for i in annotation["attributes"] if "groupId" in i]:
2340-
if (
2322+
for annotation in [
2323+
i
2324+
for i in annotations["instances"]
2325+
if "classId" in i and i["classId"] in annotation_classes
2326+
]:
2327+
annotation_class = annotation_classes[annotation["classId"]]
2328+
annotation["className"] = annotation_class["name"]
2329+
for attribute in [
2330+
i
2331+
for i in annotation["attributes"]
2332+
if "groupId" in i
2333+
and i["groupId"] in annotation_class["attribute_groups"].keys()
2334+
]:
2335+
attribute["groupName"] = annotation_class["attribute_groups"][
23412336
attribute["groupId"]
2342-
not in annotation_classes[annotation_class_id]["attribute_groups"]
2343-
):
2344-
continue
2345-
attribute["groupName"] = annotation_classes[annotation_class_id][
2346-
"attribute_groups"
2347-
][attribute["groupId"]]["name"]
2337+
]["name"]
23482338
if (
2349-
attribute["groupId"]
2350-
not in annotation_classes[annotation_class_id]["attribute_groups"][
2351-
attribute["groupId"]
2352-
]["attributes"]
2339+
attribute["id"]
2340+
not in list(annotation_class["attribute_groups"][attribute["groupId"]][
2341+
"attributes"
2342+
].keys())
23532343
):
23542344
continue
2355-
attribute["name"] = annotation_classes[annotation_class_id][
2356-
"attribute_groups"
2357-
][attribute["groupId"]]["name"]
2345+
attribute["name"] = annotation_class["attribute_groups"][
2346+
attribute["groupId"]
2347+
]["attributes"][attribute["id"]]
23582348

23592349
def execute(self):
23602350
if self.is_valid():
@@ -3117,7 +3107,9 @@ def execute(self):
31173107
weight, height = image.get_size()
31183108
empty_image_arr = np.full((height, weight, 4), [0, 0, 0, 255], np.uint8)
31193109
for annotation in self.annotations["instances"]:
3120-
if not class_color_map.get(annotation["className"]):
3110+
if annotation.get("className") and not class_color_map.get(
3111+
annotation["className"]
3112+
):
31213113
continue
31223114
fill_color = *class_color_map[annotation["className"]], 255
31233115
for part in annotation["parts"]:
@@ -3336,6 +3328,41 @@ def annotation_classes_name_map(self) -> dict:
33363328
classes_data[annotation_class.name] = class_info
33373329
return classes_data
33383330

3331+
@property
3332+
def get_annotation_classes_name_to_id(self):
3333+
annotation_classes = self._annotation_classes
3334+
annotation_classes_dict = {}
3335+
for annotation_class in annotation_classes:
3336+
class_id = annotation_class["id"]
3337+
class_name = annotation_class["name"]
3338+
class_info = {"id": class_id, "attribute_groups": {}}
3339+
if "attribute_groups" in annotation_class:
3340+
for attribute_group in annotation_class["attribute_groups"]:
3341+
attribute_group_info = {}
3342+
for attribute in attribute_group["attributes"]:
3343+
if attribute["name"] in attribute_group_info:
3344+
logger.warning(
3345+
"Duplicate annotation class attribute name %s in attribute group %s. Only one of the annotation classe attributes will be used. This will result in errors in annotation upload.",
3346+
attribute["name"], attribute_group["name"]
3347+
)
3348+
attribute_group_info[attribute["name"]] = attribute["id"]
3349+
if attribute_group["name"] in class_info["attribute_groups"]:
3350+
logger.warning(
3351+
"Duplicate annotation class attribute group name %s. Only one of the annotation classe attribute groups will be used. This will result in errors in annotation upload.",
3352+
attribute_group["name"]
3353+
)
3354+
class_info["attribute_groups"][attribute_group["name"]] = {
3355+
"id": attribute_group["id"],
3356+
"attributes": attribute_group_info
3357+
}
3358+
if class_name in annotation_classes_dict:
3359+
logger.warning(
3360+
"Duplicate annotation class name %s. Only one of the annotation classes will be used. This will result in errors in annotation upload.",
3361+
class_name
3362+
)
3363+
annotation_classes_dict[class_name] = class_info
3364+
return annotation_classes_dict
3365+
33393366
def get_templates_mapping(self):
33403367
templates = self._backend_service.get_templates(
33413368
team_id=self._project.team_id
@@ -3382,7 +3409,7 @@ def fill_classes_data(self, annotations: dict):
33823409
attribute["groupName"]
33833410
not in annotation_classes[annotation_class_name]["attribute_groups"]
33843411
):
3385-
self.unknown_attributes.append(attribute["groupName"])
3412+
self.unknown_attribute_groups.append(attribute["groupName"])
33863413
continue
33873414
attribute["groupId"] = annotation_classes[annotation_class_name][
33883415
"attribute_groups"
@@ -3437,7 +3464,8 @@ def execute(self):
34373464
resource = session.resource("s3")
34383465
bucket = resource.Bucket(response.data.bucket)
34393466
self.fill_classes_data(self._annotations)
3440-
self.report_unknown_data()
3467+
# skipped report
3468+
# self.report_unknown_data()
34413469
bucket.put_object(
34423470
Key=response.data.images[image_data["id"]]["annotation_json_path"],
34433471
Body=json.dumps(self._annotations),
@@ -4922,52 +4950,57 @@ def _upload_image(self, image_path: str):
49224950
uploaded=False, path=image_path, entity=None, name=Path(image_path).name
49234951
)
49244952

4953+
def filter_paths(self, paths: List[str]):
4954+
paths = [
4955+
path
4956+
for path in paths
4957+
if not any(
4958+
[extension in path for extension in self.exclude_file_patterns]
4959+
)
4960+
]
4961+
name_path_map = defaultdict(list)
4962+
for path in paths:
4963+
name_path_map[Path(path).name].append(path)
4964+
4965+
filtered_paths = []
4966+
duplicated_paths = []
4967+
for file_name in name_path_map:
4968+
if len(name_path_map[file_name]) > 1:
4969+
duplicated_paths.append(name_path_map[file_name][1:])
4970+
filtered_paths.append(name_path_map[file_name][0])
4971+
4972+
image_entities = (
4973+
GetBulkImages(
4974+
service=self._backend_client,
4975+
project_id=self._project.uuid,
4976+
team_id=self._project.team_id,
4977+
folder_id=self._folder.uuid,
4978+
images=[image.split("/")[-1] for image in filtered_paths],
4979+
).execute().data
4980+
)
4981+
images_to_upload = []
4982+
image_list = [image.name for image in image_entities]
4983+
4984+
for path in filtered_paths:
4985+
if Path(path).name not in image_list:
4986+
images_to_upload.append(path)
4987+
else:
4988+
duplicated_paths.append(path)
4989+
return list(set(images_to_upload)), duplicated_paths
4990+
49254991
@property
49264992
def images_to_upload(self):
49274993
if not self._images_to_upload:
4928-
paths = self._paths
4929-
filtered_paths = []
4930-
duplicated_paths = []
4931-
for path in paths:
4932-
if path.split("/")[-1] not in [
4933-
path_name.split("/")[-1] for path_name in filtered_paths
4934-
]:
4935-
filtered_paths.append(path)
4936-
else:
4937-
duplicated_paths.append(path)
4938-
filtered_paths = [
4939-
path
4940-
for path in paths
4941-
if not any(
4942-
[extension in path for extension in self.exclude_file_patterns]
4943-
)
4944-
]
4945-
image_entities = (
4946-
GetBulkImages(
4947-
service=self._backend_client,
4948-
project_id=self._project.uuid,
4949-
team_id=self._project.team_id,
4950-
folder_id=self._folder.uuid,
4951-
images=[image.split("/")[-1] for image in filtered_paths],
4952-
)
4953-
.execute()
4954-
.data
4955-
)
4956-
images_to_upload = []
4957-
image_list = [image.name for image in image_entities]
4958-
4959-
for path in filtered_paths:
4960-
if path not in image_list:
4961-
images_to_upload.append(path)
4962-
else:
4963-
duplicated_paths.append(path)
4964-
self._images_to_upload = list(set(images_to_upload)), duplicated_paths
4994+
self._images_to_upload = self.filter_paths(self._paths)
49654995
return self._images_to_upload
49664996

49674997
def execute(self):
49684998
if self.is_valid():
49694999
images_to_upload, duplications = self.images_to_upload
49705000
images_to_upload = images_to_upload[: self.auth_data["availableImageCount"]]
5001+
if not images_to_upload:
5002+
return self._response
5003+
49715004
uploaded_images = []
49725005
failed_images = []
49735006
with concurrent.futures.ThreadPoolExecutor(

0 commit comments

Comments
 (0)