Skip to content
This repository was archived by the owner on Aug 7, 2025. It is now read-only.

Commit d391dd6

Browse files
Fix (frame extraction): Use fps in ffmpeg command (#665)
* fix: remove forbidden chars in file name * fix:styling * fix: skip non existing frames * fix: add fps info to ffmpeg command * fix: raise error instead of dkipping
1 parent cb09648 commit d391dd6

File tree

5 files changed

+29
-10
lines changed

5 files changed

+29
-10
lines changed

src/encord_active/db/scripts/migrate_disk_to_db.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@
8585
"Random Values on Objects": "metric_random",
8686
"Image-level Annotation Quality": "metric_annotation_quality",
8787
"Shape outlier detection": "metric_label_shape_outlier",
88-
"Detect Occlusion in Video": "$SKIP"
88+
"Detect Occlusion in Video": "$SKIP",
8989
}
9090

9191
# Metrics that need to be migrated to the normalised format from percentage for consistency with other metrics.

src/encord_active/lib/common/data_utils.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@
2121
_EXTRACT_FRAMES_FOLDER: tempfile.TemporaryDirectory = tempfile.TemporaryDirectory()
2222

2323

24-
def extract_frames(video_file_name: Path, img_dir: Path, data_hash: str, symlink_folder: bool = True) -> None:
24+
def extract_frames(
25+
video_file_name: Path,
26+
img_dir: Path,
27+
data_hash: str,
28+
frame_rate: Optional[float] = None,
29+
symlink_folder: bool = True,
30+
) -> None:
2531
if data_hash not in _EXTRACT_FRAMES_CACHE:
2632
while True:
2733
try:
@@ -34,7 +40,7 @@ def extract_frames(video_file_name: Path, img_dir: Path, data_hash: str, symlink
3440
pass
3541

3642
try:
37-
_extract_frames(video_file_name, tempdir, data_hash)
43+
_extract_frames(video_file_name, tempdir, data_hash, frame_rate)
3844
except Exception:
3945
shutil.rmtree(tempdir, ignore_errors=True)
4046
raise
@@ -50,10 +56,17 @@ def extract_frames(video_file_name: Path, img_dir: Path, data_hash: str, symlink
5056
(img_dir / frame.name).symlink_to(frame, target_is_directory=False)
5157

5258

53-
def _extract_frames(video_file_name: Path, img_dir: Path, data_hash: str) -> None:
59+
def _extract_frames(video_file_name: Path, img_dir: Path, data_hash: str, frame_rate: Optional[float]) -> None:
5460
# DENIS: for the rest to work, I will need to throw if the current directory exists and give a nice user warning.
5561
img_dir.mkdir(parents=True, exist_ok=True)
56-
command = f'ffmpeg -i "{video_file_name}" -start_number 0 {img_dir}/{data_hash}_%d.png -hide_banner'
62+
63+
if frame_rate:
64+
command = (
65+
f'ffmpeg -i "{video_file_name}" -r {frame_rate} -start_number 0 {img_dir}/{data_hash}_%d.png -hide_banner'
66+
)
67+
else:
68+
command = f'ffmpeg -i "{video_file_name}" -start_number 0 {img_dir}/{data_hash}_%d.png -hide_banner'
69+
5770
if subprocess.run(command, shell=True, capture_output=True, stdout=None, check=False).returncode != 0:
5871
raise RuntimeError(
5972
"Failed to split the video into multiple image files. Please ensure that you have FFMPEG "

src/encord_active/lib/common/iterator.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import re
23
import tempfile
34
from abc import abstractmethod
45
from collections.abc import Sized
@@ -130,14 +131,15 @@ def iterate(self, desc: str = "") -> Generator[Tuple[dict, Optional[Image.Image]
130131
# Create temporary folder containing the video
131132
with tempfile.TemporaryDirectory() as working_dir:
132133
working_path = Path(working_dir)
133-
video_path = working_path / str(data_unit["data_title"])
134+
safe_data_title = re.sub(r'[\\/:*?"<>|\x00-\x1F\x7F ]', "_", data_unit["data_title"])
135+
video_path = working_path / safe_data_title
134136
video_images_dir = working_path / "images"
135137
download_file(
136138
video_metadata.signed_url,
137139
project_dir=self.project_file_structure.project_dir,
138140
destination=video_path,
139141
)
140-
extract_frames(video_path, video_images_dir, self.du_hash)
142+
extract_frames(video_path, video_images_dir, self.du_hash, data_unit["data_fps"])
141143

142144
fake_data_unit = deepcopy(data_unit)
143145

src/encord_active/lib/project/project.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ def split_lr_video(label_row: LabelRow, project_file_structure: ProjectFileStruc
509509
num_frames = count_frames(video_path)
510510
frames_per_second = get_frames_per_second(video_path)
511511
video_images = Path(video_dir) / "images"
512-
extract_frames(video_path, video_images, data_hash)
512+
extract_frames(video_path, video_images, data_hash, frames_per_second)
513513
image_path = next(video_images.iterdir())
514514
image = Image.open(image_path)
515515

src/encord_active/lib/project/project_file_structure.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,9 @@ def iter_data_unit_with_image(
252252
download_file(
253253
data_unit_struct.signed_url, project_dir=self._project.project_dir, destination=video_file
254254
)
255-
extract_frames(video_file, images_dir, data_unit_struct.du_hash)
255+
extract_frames(
256+
video_file, images_dir, data_unit_struct.du_hash, data_unit_struct.frames_per_second
257+
)
256258
downloaded_image = next(images_dir.glob(f"{data_unit_struct.du_hash}_{data_unit_struct.frame}.*"))
257259
yield data_unit_struct, Image.open(downloaded_image)
258260
else:
@@ -284,7 +286,9 @@ def iter_data_unit_with_image_or_signed_url(
284286
else:
285287
video_file = video_dir / label_row_json["data_title"]
286288
download_file(data_unit_struct.signed_url, project_dir=self._project, destination=video_file)
287-
extract_frames(video_file, images_dir, data_unit_struct.du_hash)
289+
extract_frames(
290+
video_file, images_dir, data_unit_struct.du_hash, data_unit_struct.frames_per_second
291+
)
288292
downloaded_image = next(images_dir.glob(f"{data_unit_struct.du_hash}_{data_unit_struct.frame}.*"))
289293
yield data_unit_struct, Image.open(downloaded_image)
290294
else:

0 commit comments

Comments
 (0)