diff --git a/act/tests/sample_files.py b/act/tests/sample_files.py index ab9c4de859..fdc4a0f732 100644 --- a/act/tests/sample_files.py +++ b/act/tests/sample_files.py @@ -8,6 +8,7 @@ from arm_test_data import DATASETS # Single files +EXAMPLE_MPEG = DATASETS.fetch('nsacamskyradmovieC1.a1.20240401.100300.mpg') EXAMPLE_MET1 = DATASETS.fetch('sgpmetE13.b1.20190101.000000.cdf') EXAMPLE_MET_SAIL = DATASETS.fetch('gucmetM1.b1.20230301.000000.cdf') EXAMPLE_MET_CSV = DATASETS.fetch('sgpmetE13.b1.20210401.000000.csv') diff --git a/act/utils/io_utils.py b/act/utils/io_utils.py index ceaab5703f..8f9479dd24 100644 --- a/act/utils/io_utils.py +++ b/act/utils/io_utils.py @@ -8,8 +8,8 @@ import types try: + import moviepy.editor as moviepy_editor import moviepy.video.io.ImageSequenceClip - from moviepy.video.io.VideoFileClip import VideoFileClip MOVIEPY_AVAILABLE = True except (ImportError, RuntimeError): @@ -337,11 +337,35 @@ def generate_movie(images, write_filename=None, fps=10, **kwargs): write_directory.mkdir(parents=True, exist_ok=True) if IS_MOVIE: - with VideoFileClip(images) as clip: - # Not sure why but need to set the duration of the clip with subclip() to write - # the full file out. - clip = clip.subclip(t_start=clip.start, t_end=clip.end * clip.fps) - clip.write_videofile(str(write_filename), fps=fps, **kwargs) + with moviepy_editor.VideoFileClip(images) as clip: + # There can be an issue converting mpeg to other movie format because the + # duration parameter in the movie file is not set. So moviepy guesses and + # can get the duration wrong. This will find the correct duration (to 0.1 seconds) + # and set before writing. + if Path(images).suffix == '.mpg': + import numpy as np + import warnings + + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', category=UserWarning) + frame = None + duration = 0.0 # Duration of movie in seconds + while True: + result = clip.get_frame(duration) + if frame is not None and np.all(frame == result): + break + + duration += 0.1 + frame = result + + clip = clip.set_start(0) + clip = clip.set_duration(duration) + clip = clip.set_end(duration) + clip.write_videofile(str(write_filename), **kwargs) + + else: + clip.write_videofile(str(write_filename), **kwargs) + else: clip = moviepy.video.io.ImageSequenceClip.ImageSequenceClip(images, fps=fps) clip.write_videofile(str(write_filename), **kwargs) diff --git a/tests/utils/test_io_utils.py b/tests/utils/test_io_utils.py index 9b2239e559..4d7263cd74 100644 --- a/tests/utils/test_io_utils.py +++ b/tests/utils/test_io_utils.py @@ -275,5 +275,13 @@ def test_generate_movie(): assert Path(result).name == write_filename assert np.isclose(Path(result).stat().st_size, 173189, 1000) + # Test converting MPEG to mp4 + write_filename = 'movie3.mp4' + mpeg_file = sample_files.EXAMPLE_MPEG + result = act.utils.generate_movie(mpeg_file, write_filename=write_filename) + files = list(Path().glob(write_filename)) + assert len(files) == 1 + assert np.isclose(files[0].stat().st_size, 1625298, rtol=100, atol=100) + finally: chdir(cwd)