Skip to content

Commit 37c1f79

Browse files
committed
Add los
1 parent 71ea1ef commit 37c1f79

File tree

6 files changed

+220
-30
lines changed

6 files changed

+220
-30
lines changed

src/superannotate/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import os
21
import logging.config
2+
import os
33

4-
from superannotate.version import __version__
54
from superannotate.lib.app.analytics.class_analytics import attribute_distribution
65
from superannotate.lib.app.analytics.class_analytics import class_distribution
76
from superannotate.lib.app.annotation_helpers import add_annotation_bbox_to_json
@@ -155,6 +154,7 @@
155154
from superannotate.lib.app.interface.sdk_interface import (
156155
upload_videos_from_folder_to_project,
157156
)
157+
from superannotate.version import __version__
158158

159159

160160
__all__ = [

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

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -179,17 +179,13 @@ def create_project(project_name: str, project_description: str, project_type: st
179179
:return: dict object metadata the new project
180180
:rtype: dict
181181
"""
182-
projects = controller.search_project(name=project_name).data
183-
if projects:
184-
raise AppException(
185-
f"Project with name {project_name} already exists."
186-
f" Please use unique names for projects to use with SDK."
187-
)
188-
189-
result = controller.create_project(
182+
response = controller.create_project(
190183
name=project_name, description=project_description, project_type=project_type
191-
).data
192-
return ProjectSerializer(result).serialize()
184+
)
185+
if response.errors:
186+
raise Exception(response.errors)
187+
188+
return ProjectSerializer(response.data).serialize()
193189

194190

195191
@Trackable
@@ -672,6 +668,8 @@ def _upload_image(image_url, image_path) -> ProcessedImage:
672668
else:
673669
failed_images.append(processed_image)
674670

671+
logger.info("Downloading %s images", len(images_to_upload))
672+
675673
for i in range(0, len(images_to_upload), 500):
676674
controller.upload_images(
677675
project_name=project_name,
@@ -1099,6 +1097,9 @@ def delete_images(project: Union[str, dict], image_names: Optional[List[str]] =
10991097
"""
11001098
project_name, folder_name = extract_project_folder(project)
11011099

1100+
if not isinstance(image_names, list) and image_names is not None:
1101+
raise AppValidationException("Image_names should be a list of strs or None.")
1102+
11021103
response = controller.delete_images(
11031104
project_name=project_name, folder_name=folder_name, image_names=image_names
11041105
)
@@ -1143,9 +1144,6 @@ def assign_images(project: Union[str, dict], image_names: List[str], user: str):
11431144
logger.warning(
11441145
f"Skipping {user}. {user} is not a verified contributor for the {project_name}"
11451146
)
1146-
1147-
1148-
11491147
return
11501148

11511149
controller.assign_images(project_name, folder_name, image_names, user)
@@ -1204,9 +1202,27 @@ def assign_folder(project_name: str, folder_name: str, users: List[str]):
12041202
:param users: list of user emails
12051203
:type users: list of str
12061204
"""
1205+
1206+
contributors = (
1207+
controller.get_project_metadata(
1208+
project_name=project_name, include_contributors=True
1209+
)
1210+
.data["project"]
1211+
.users
1212+
)
1213+
verified_users = [i["user_id"] for i in contributors]
1214+
verified_users = set(users).intersection(set(verified_users))
1215+
unverified_contributor = set(users) - verified_users
1216+
1217+
for user in unverified_contributor:
1218+
logger.warning(
1219+
f"Skipping {user} from assignees. {user} is not a verified contributor for the {project_name}"
1220+
)
1221+
12071222
response = controller.assign_folder(
1208-
project_name=project_name, folder_name=folder_name, users=users
1223+
project_name=project_name, folder_name=folder_name, users=list(verified_users)
12091224
)
1225+
12101226
if response.errors:
12111227
raise AppException(response.errors)
12121228

@@ -1226,10 +1242,15 @@ def share_project(project_name: str, user: Union[str, dict], user_role: str):
12261242
if isinstance(user, dict):
12271243
user_id = user["id"]
12281244
else:
1229-
user_id = controller.search_team_contributors(email=user).data[0]["id"]
1230-
controller.share_project(
1245+
response = controller.search_team_contributors(email=user)
1246+
if not response.data:
1247+
raise AppException(f"User {user} not found.")
1248+
user_id = response.data[0]["id"]
1249+
response = controller.share_project(
12311250
project_name=project_name, user_id=user_id, user_role=user_role
12321251
)
1252+
if response.errors:
1253+
raise AppException(response.errors)
12331254

12341255

12351256
@Trackable
@@ -1814,6 +1835,9 @@ def upload_videos_from_folder_to_project(
18141835
if os.name != "nt":
18151836
video_paths += list(Path(folder_path).glob(f"*.{extension.upper()}"))
18161837
else:
1838+
logger.warning(
1839+
"When using recursive subfolder parsing same name videos in different subfolders will overwrite each other."
1840+
)
18171841
video_paths += list(Path(folder_path).rglob(f"*.{extension.lower()}"))
18181842
if os.name != "nt":
18191843
video_paths += list(Path(folder_path).rglob(f"*.{extension.upper()}"))
@@ -1824,6 +1848,14 @@ def upload_videos_from_folder_to_project(
18241848
if all(not_in_exclude_list):
18251849
filtered_paths.append(path)
18261850

1851+
logger.info(
1852+
"Uploading all videos with extensions %s from %s to project %s. Excluded file patterns are: %s.",
1853+
extensions,
1854+
str(folder_path),
1855+
project_name,
1856+
exclude_file_patterns,
1857+
)
1858+
18271859
uploaded_images, failed_images = [], []
18281860
for path in tqdm(video_paths):
18291861
with tempfile.TemporaryDirectory() as temp_path:
@@ -2173,6 +2205,7 @@ def _upload_file_to_s3(to_s3_bucket, path, s3_key) -> None:
21732205

21742206
for future in concurrent.futures.as_completed(results):
21752207
future.result()
2208+
logger.info("Exported to AWS %s/%s", to_s3_bucket, str(path))
21762209

21772210

21782211
@Trackable
@@ -2567,6 +2600,7 @@ def upload_image_annotations(
25672600
image_name=image_name,
25682601
annotations=annotation_json,
25692602
mask=mask,
2603+
verbose=verbose,
25702604
)
25712605
if response.errors:
25722606
raise AppValidationException(response.errors)
@@ -3483,6 +3517,9 @@ def _upload_s3_image(image_path: str):
34833517
progress_bar.update(1)
34843518
uploaded = []
34853519
duplicates = []
3520+
3521+
logger.info("Uploading %s images to project.", len(images_to_upload))
3522+
34863523
for i in range(0, len(uploaded_image_entities), 500):
34873524
response = controller.upload_images(
34883525
project_name=project_name,
@@ -3494,6 +3531,11 @@ def _upload_s3_image(image_path: str):
34943531
uploaded.extend(attachments)
34953532
duplicates.extend(duplications)
34963533

3534+
if len(duplicates):
3535+
logger.warning(
3536+
"%s already existing images found that won't be uploaded.", len(duplicates)
3537+
)
3538+
34973539
return uploaded, failed_images, duplicates
34983540

34993541

src/superannotate/lib/core/plugin.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import io
2+
import logging
23
from pathlib import Path
34
from typing import List
45
from typing import Tuple
@@ -10,6 +11,8 @@
1011
from PIL import ImageDraw
1112
from PIL import ImageOps
1213

14+
logger = logging.getLogger()
15+
1316

1417
class ImagePlugin:
1518
def __init__(self, image_bytes: io.BytesIO, max_resolution: int = 4096):
@@ -185,8 +188,18 @@ def get_video_rotate_code(video_path):
185188
meta_dict = ffmpeg.probe(str(video_path))
186189
rot = int(meta_dict["streams"][0]["tags"]["rotate"])
187190
if rot:
191+
logger.info(
192+
"Frame rotation of %s found. Output images will be rotated accordingly.",
193+
rot,
194+
)
188195
return cv2_rotations[rot]
189-
except Exception:
196+
except Exception as e:
197+
warning_str = ""
198+
if "ffprobe" in str(e):
199+
warning_str = "This could be because ffmpeg package is not installed. To install it, run: sudo apt install ffmpeg"
200+
logger.warning(
201+
"Couldn't read video metadata to determine rotation. %s", warning_str
202+
)
190203
return
191204

192205
@staticmethod
@@ -202,8 +215,25 @@ def extract_frames(
202215
if not video.isOpened():
203216
return []
204217
frames_count = VideoPlugin.get_frames_count(video_path)
218+
logger.info("Video frame count is %s.", frames_count)
219+
205220
fps = video.get(cv2.CAP_PROP_FPS)
206-
ratio = fps / target_fps if target_fps else 1
221+
if target_fps > fps:
222+
logger.warning(
223+
"Video frame rate %s smaller than target frame rate %s. Cannot change frame rate.",
224+
fps,
225+
target_fps,
226+
)
227+
target_fps = fps
228+
229+
else:
230+
logger.info(
231+
"Changing video frame rate from %s to target frame rate %s.",
232+
fps,
233+
target_fps,
234+
)
235+
236+
ratio = fps / target_fps
207237
extracted_frames_paths = []
208238
zero_fill_count = len(str(frames_count))
209239

@@ -212,6 +242,8 @@ def extract_frames(
212242
frame_number = 0
213243
extracted_frame_number = 0
214244
extracted_frame_ratio = ratio
245+
logger.info("Extracting frames from video to %s.", extracted_frames_paths)
246+
215247
while len(extracted_frames_paths) < limit:
216248
success, frame = video.read()
217249
if success:

0 commit comments

Comments
 (0)