Skip to content

Commit 8427214

Browse files
committed
Fix copy_images - video
1 parent fdf0c2b commit 8427214

File tree

2 files changed

+64
-63
lines changed

2 files changed

+64
-63
lines changed

superannotate/db/project_images.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
from .project_api import get_project_and_folder_metadata
1818
from .projects import (
1919
__create_image, get_image_array_to_upload,
20-
get_project_default_image_quality_in_editor, upload_image_array_to_s3
20+
get_project_default_image_quality_in_editor, upload_image_array_to_s3,
21+
_get_available_image_counts
2122
)
2223
from .utils import _get_upload_auth_token
2324

@@ -99,12 +100,9 @@ def upload_image_to_project(
99100
folder_id = get_project_root_folder_id(project)
100101

101102
team_id, project_id = project["team_id"], project["id"]
102-
params = {
103-
'team_id': team_id,
104-
'folder_id' : folder_id
105-
}
106-
res = _get_upload_auth_token(params=params,project_id=project_id)
107-
prefix = res['filePath']
103+
params = {'team_id': team_id, 'folder_id': folder_id}
104+
res = _get_upload_auth_token(params=params, project_id=project_id)
105+
prefix = res['filePath']
108106
s3_session = boto3.Session(
109107
aws_access_key_id=res['accessKeyId'],
110108
aws_secret_access_key=res['secretAccessKey'],
@@ -224,18 +222,23 @@ def copy_images(
224222
)
225223
if image_names is None:
226224
image_names = search_images((source_project, source_project_folder))
225+
226+
limit = _get_available_image_counts(
227+
destination_project, destination_project_folder
228+
)
229+
image_names = image_names[:limit]
227230
res = _copy_images(
228231
(source_project, source_project_folder),
229232
(destination_project, destination_project_folder), image_names,
230233
include_annotations, copy_annotation_status, copy_pin
231234
)
232235
logger.info(
233236
"Copied images %s from %s to %s. Number of skipped images %s",
234-
image_names,
235-
source_project["name"] + "" if source_project_folder is None else "/" +
236-
source_project_folder["name"], destination_project["name"] +
237-
"" if destination_project_folder is None else destination_project["name"] + "/" +
238-
destination_project_folder["name"], len(res["skipped"])
237+
image_names, source_project["name"] +
238+
"" if source_project_folder is None else source_project["name"] + "/" +
239+
source_project_folder["name"], destination_project["name"] + ""
240+
if destination_project_folder is None else destination_project["name"] +
241+
"/" + destination_project_folder["name"], len(res["skipped"])
239242
)
240243
return res["skipped"]
241244

superannotate/db/projects.py

Lines changed: 49 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ def _get_video_frames_count(video_path):
221221
return total_num_of_frames
222222

223223

224-
def _get_video_fps_ration(target_fps,video,ratio):
224+
def _get_video_fps_ration(target_fps, video, ratio):
225225
"""
226226
Get video fps / target fps ratio
227227
"""
@@ -230,7 +230,7 @@ def _get_video_fps_ration(target_fps,video,ratio):
230230
logger.warning(
231231
"Video frame rate %s smaller than target frame rate %s. Cannot change frame rate.",
232232
video_fps, target_fps
233-
)
233+
)
234234
else:
235235
logger.info(
236236
"Changing video frame rate from %s to target frame rate %s.",
@@ -239,22 +239,24 @@ def _get_video_fps_ration(target_fps,video,ratio):
239239
ratio = video_fps / target_fps
240240
return ratio
241241

242-
def _get_available_image_counts(project,folder):
242+
243+
def _get_available_image_counts(project, folder):
243244
if folder:
244245
folder_id = folder["id"]
245246
else:
246247
folder_id = get_project_root_folder_id(project)
247-
params = {'team_id': project['team_id'] , 'folder_id' : folder_id }
248-
res = _get_upload_auth_token(params=params,project_id=project['id'])
248+
params = {'team_id': project['team_id'], 'folder_id': folder_id}
249+
res = _get_upload_auth_token(params=params, project_id=project['id'])
249250
return res['availableImageCount']
250251

252+
251253
def _get_video_rotate_code(video_path):
252254
rotate_code = None
253255
try:
254256
cv2_rotations = {
255-
90 : cv2.ROTATE_90_CLOCKWISE,
256-
180 : cv2.ROTATE_180,
257-
270 :cv2.ROTATE_90_COUNTERCLOCKWISE,
257+
90: cv2.ROTATE_90_CLOCKWISE,
258+
180: cv2.ROTATE_180,
259+
270: cv2.ROTATE_90_COUNTERCLOCKWISE,
258260
}
259261

260262
meta_dict = ffmpeg.probe(str(video_path))
@@ -276,14 +278,26 @@ def _get_video_rotate_code(video_path):
276278
return rotate_code
277279

278280

279-
def _extract_frames_from_video(start_time,end_time,ratio,video,video_path,tempdir,limit,rotate_code,total_num_of_frames):
281+
def _extract_frames_from_video(
282+
start_time, end_time, video_path, tempdir, limit, target_fps
283+
):
284+
video = cv2.VideoCapture(str(video_path), cv2.CAP_FFMPEG)
285+
if not video.isOpened():
286+
raise SABaseException(0, "Couldn't open video file " + str(video_path))
287+
total_num_of_frames = _get_video_frames_count(video_path)
288+
logger.info("Video frame count is %s.", total_num_of_frames)
289+
ratio = 1.0
290+
if target_fps:
291+
ratio = _get_video_fps_ration(target_fps, video, ratio)
292+
rotate_code = _get_video_rotate_code(video_path)
280293
video_name = Path(video_path).stem
281294
frame_no = 0
282295
frame_no_with_change = 1.0
283296
extracted_frame_no = 1
284297
logger.info("Extracting frames from video to %s.", tempdir.name)
285298
zero_fill_count = len(str(total_num_of_frames))
286-
while extracted_frame_no < (limit + 1) :
299+
extracted_frames_paths = []
300+
while len(extracted_frames_paths) < limit:
287301
success, frame = video.read()
288302
if not success:
289303
break
@@ -298,16 +312,16 @@ def _extract_frames_from_video(start_time,end_time,ratio,video,video_path,tempdi
298312
continue
299313
if rotate_code:
300314
frame = cv2.rotate(frame, rotate_code)
301-
cv2.imwrite(
302-
str(
303-
Path(tempdir.name) / (
304-
video_name + "_" +
305-
str(extracted_frame_no).zfill(zero_fill_count) + ".jpg"
306-
)
307-
), frame
315+
path = str(
316+
Path(tempdir.name) / (
317+
video_name + "_" +
318+
str(extracted_frame_no).zfill(zero_fill_count) + ".jpg"
319+
)
308320
)
321+
cv2.imwrite(path, frame)
322+
extracted_frames_paths.append(path)
309323
extracted_frame_no += 1
310-
return extracted_frame_no - 1
324+
return extracted_frames_paths
311325

312326

313327
def upload_video_to_project(
@@ -345,7 +359,7 @@ def upload_video_to_project(
345359
"""
346360

347361
project, folder = get_project_and_folder_metadata(project)
348-
limit = _get_available_image_counts(project,folder)
362+
limit = _get_available_image_counts(project, folder)
349363

350364
upload_state = common.upload_state_int_to_str(project.get("upload_state"))
351365
if upload_state == "External":
@@ -354,23 +368,13 @@ def upload_video_to_project(
354368
"The function does not support projects containing images attached with URLs"
355369
)
356370
logger.info("Uploading from video %s.", str(video_path))
357-
rotate_code = _get_video_rotate_code(video_path)
358-
video = cv2.VideoCapture(str(video_path), cv2.CAP_FFMPEG)
359-
if not video.isOpened():
360-
raise SABaseException(0, "Couldn't open video file " + str(video_path))
361-
362-
total_num_of_frames = _get_video_frames_count(video_path)
363-
logger.info("Video frame count is %s.", total_num_of_frames)
364-
ratio = 1.0
365-
if target_fps:
366-
ratio = _get_video_fps_ration(target_fps,video,ratio)
367371
tempdir = tempfile.TemporaryDirectory()
368-
extracted_frame_no = _extract_frames_from_video(start_time,end_time,ratio,
369-
video,video_path,tempdir,
370-
limit,rotate_code,total_num_of_frames)
372+
extracted_frames = _extract_frames_from_video(
373+
start_time, end_time, video_path, tempdir, limit, target_fps
374+
)
371375
logger.info(
372376
"Extracted %s frames from video. Now uploading to platform.",
373-
extracted_frame_no
377+
len(extracted_frames)
374378
)
375379
filenames = upload_images_from_folder_to_project(
376380
(project, folder),
@@ -853,9 +857,7 @@ def upload_images_to_project(
853857
:rtype: tuple (3 members) of list of strs
854858
"""
855859
project, folder = get_project_and_folder_metadata(project)
856-
folder_name = project["name"] + (
857-
f'/{folder["name"]}' if folder else ""
858-
)
860+
folder_name = project["name"] + (f'/{folder["name"]}' if folder else "")
859861
upload_state = common.upload_state_int_to_str(project.get("upload_state"))
860862
if upload_state == "External":
861863
raise SABaseException(
@@ -895,24 +897,23 @@ def upload_images_to_project(
895897
if len_img_paths == 0:
896898
return ([], [], duplicate_images)
897899

898-
899900
if folder:
900901
folder_id = folder["id"]
901902
else:
902903
folder_id = get_project_root_folder_id(project)
903904

904-
params = {'team_id': team_id , 'folder_id' : folder_id }
905+
params = {'team_id': team_id, 'folder_id': folder_id}
905906
uploaded = [[] for _ in range(_NUM_THREADS)]
906907
tried_upload = [[] for _ in range(_NUM_THREADS)]
907908
couldnt_upload = [[] for _ in range(_NUM_THREADS)]
908909
finish_event = threading.Event()
909910

910-
res = _get_upload_auth_token(params=params,project_id=project_id)
911+
res = _get_upload_auth_token(params=params, project_id=project_id)
911912

912913
prefix = res['filePath']
913914
limit = res['availableImageCount']
914915
images_to_upload = img_paths[:limit]
915-
images_to_skip = img_paths[limit:]
916+
images_to_skip = [str(path) for path in img_paths[limit:]]
916917
chunksize = int(math.ceil(len(images_to_upload) / _NUM_THREADS))
917918

918919
tqdm_thread = threading.Thread(
@@ -927,8 +928,8 @@ def upload_images_to_project(
927928
t = threading.Thread(
928929
target=__upload_images_to_aws_thread,
929930
args=(
930-
res, images_to_upload, project, annotation_status, prefix, thread_id,
931-
chunksize, couldnt_upload, uploaded, tried_upload,
931+
res, images_to_upload, project, annotation_status, prefix,
932+
thread_id, chunksize, couldnt_upload, uploaded, tried_upload,
932933
image_quality_in_editor, from_s3_bucket, folder_id
933934
),
934935
daemon=True
@@ -987,9 +988,7 @@ def attach_image_urls_to_project(
987988
"""
988989

989990
project, folder = get_project_and_folder_metadata(project)
990-
folder_name = project["name"] + (
991-
f'/{folder["name"]}' if folder else ""
992-
)
991+
folder_name = project["name"] + (f'/{folder["name"]}' if folder else "")
993992
upload_state = common.upload_state_int_to_str(project.get("upload_state"))
994993
if upload_state == "Basic":
995994
raise SABaseException(
@@ -1027,8 +1026,7 @@ def attach_image_urls_to_project(
10271026
image_data = pd.DataFrame(image_data, columns=["name", "url"])
10281027
img_names_urls = image_data.values.tolist()
10291028
logger.info(
1030-
"Uploading %s images to project %s.", len(img_names_urls),
1031-
folder_name
1029+
"Uploading %s images to project %s.", len(img_names_urls), folder_name
10321030
)
10331031
if len(img_names_urls) == 0:
10341032
return ([], [], duplicate_images)
@@ -1038,14 +1036,14 @@ def attach_image_urls_to_project(
10381036
else:
10391037
folder_id = get_project_root_folder_id(project)
10401038

1041-
params = {'team_id': team_id , 'folder_id' : folder_id }
1039+
params = {'team_id': team_id, 'folder_id': folder_id}
10421040
uploaded = [[] for _ in range(_NUM_THREADS)]
10431041
tried_upload = [[] for _ in range(_NUM_THREADS)]
10441042
couldnt_upload = [[] for _ in range(_NUM_THREADS)]
10451043
finish_event = threading.Event()
10461044

1047-
res = _get_upload_auth_token(params=params,project_id=project_id)
1048-
1045+
res = _get_upload_auth_token(params=params, project_id=project_id)
1046+
10491047
prefix = res['filePath']
10501048
limit = res['availableImageCount']
10511049
images_to_upload = img_names_urls[:limit]
@@ -1084,7 +1082,7 @@ def attach_image_urls_to_project(
10841082
for f in upload_thread:
10851083
list_of_uploaded.append(str(f))
10861084

1087-
list_of_not_uploaded += [i[0] for i in images_to_skip ]
1085+
list_of_not_uploaded += [i[0] for i in images_to_skip]
10881086
return (list_of_uploaded, list_of_not_uploaded, duplicate_images)
10891087

10901088

0 commit comments

Comments
 (0)