From 92bfbc156dfdd24c074934f9321684c9d703026d Mon Sep 17 00:00:00 2001 From: Zulko Date: Sun, 24 Nov 2024 11:19:50 -0500 Subject: [PATCH 1/2] frame_function -> get_frame --- .../user_guide/compositing/with_position.py | 2 +- .../code/user_guide/loading/AudioArrayClip.py | 6 +-- .../code/user_guide/loading/AudioClip.py | 2 +- .../code/user_guide/loading/DataVideoClip.py | 4 +- .../code/user_guide/loading/VideoClip.py | 4 +- .../code/user_guide/loading/loading.py | 12 +++--- docs/_static/code/user_guide/loading/masks.py | 4 +- docs/user_guide/loading.rst | 8 ++-- moviepy/Clip.py | 16 ++++---- moviepy/audio/AudioClip.py | 24 +++++------ moviepy/audio/fx/AudioDelay.py | 4 +- moviepy/audio/io/AudioFileClip.py | 2 +- moviepy/video/VideoClip.py | 40 +++++++++---------- .../video/compositing/CompositeVideoClip.py | 6 +-- moviepy/video/io/ImageSequenceClip.py | 20 +++++----- moviepy/video/io/VideoFileClip.py | 12 +++--- moviepy/video/tools/subtitles.py | 4 +- tests/test_AudioClips.py | 4 +- tests/test_VideoClip.py | 4 +- tests/test_compositing.py | 15 +++---- tests/test_fx.py | 24 +++++------ tests/test_videotools.py | 4 +- 22 files changed, 110 insertions(+), 111 deletions(-) diff --git a/docs/_static/code/user_guide/compositing/with_position.py b/docs/_static/code/user_guide/compositing/with_position.py index 9fca3ec41..487e28a45 100644 --- a/docs/_static/code/user_guide/compositing/with_position.py +++ b/docs/_static/code/user_guide/compositing/with_position.py @@ -46,7 +46,7 @@ # Finally, we want the logo to be in the center, but to drop as time pass # We can do so by setting position as a function that take time as argument, -# a lot like frame_function +# a lot like get_frame top = (background.h - logo.h) / 2 logo = logo.with_position(lambda t: ("center", top + t * 30)) diff --git a/docs/_static/code/user_guide/loading/AudioArrayClip.py b/docs/_static/code/user_guide/loading/AudioArrayClip.py index fafc80269..0d11e70ba 100644 --- a/docs/_static/code/user_guide/loading/AudioArrayClip.py +++ b/docs/_static/code/user_guide/loading/AudioArrayClip.py @@ -14,17 +14,17 @@ n_frames = note_size * len(notes) -def frame_function(t, note_frequency): +def get_frame(t, note_frequency): return np.sin(note_frequency * 2 * np.pi * t) # At this point one could use this audioclip which generates the audio on the fly -# clip = AudioFileClip(frame_function) +# clip = AudioFileClip(get_frame) # We generate all frames timepoints audio_frame_values = [ - 2 * [frame_function(t, freq)] + 2 * [get_frame(t, freq)] for freq in notes.values() for t in np.arange(0, note_duration, 1.0 / sample_rate) ] diff --git a/docs/_static/code/user_guide/loading/AudioClip.py b/docs/_static/code/user_guide/loading/AudioClip.py index 34af669af..d2b7cd0bc 100644 --- a/docs/_static/code/user_guide/loading/AudioClip.py +++ b/docs/_static/code/user_guide/loading/AudioClip.py @@ -7,4 +7,4 @@ def audio_frame(t): return np.sin(440 * 2 * np.pi * t) -audio_clip = AudioClip(frame_function=audio_frame, duration=3) +audio_clip = AudioClip(get_frame=audio_frame, duration=3) diff --git a/docs/_static/code/user_guide/loading/DataVideoClip.py b/docs/_static/code/user_guide/loading/DataVideoClip.py index 096406207..55e1f9313 100644 --- a/docs/_static/code/user_guide/loading/DataVideoClip.py +++ b/docs/_static/code/user_guide/loading/DataVideoClip.py @@ -16,14 +16,14 @@ # The function make frame take data and create an image of 200x100 px # filled with the color given in the dataset -def frame_function(data): +def get_frame(data): frame = np.full((100, 200, 3), data, dtype=np.uint8) return frame # We create the DataVideoClip, and we set FPS at 2, making a 3s clip # (because len(dataset) = 6, so 6/2=3) -myclip = DataVideoClip(data=dataset, data_to_frame=frame_function, fps=2) +myclip = DataVideoClip(data=dataset, data_to_frame=get_frame, fps=2) # Modifying fps here will change video FPS, not clip FPS myclip.write_videofile("result.mp4", fps=30) diff --git a/docs/_static/code/user_guide/loading/VideoClip.py b/docs/_static/code/user_guide/loading/VideoClip.py index 5952af359..84f8bea8d 100644 --- a/docs/_static/code/user_guide/loading/VideoClip.py +++ b/docs/_static/code/user_guide/loading/VideoClip.py @@ -7,7 +7,7 @@ RED = (255, 0, 0) -def frame_function(t): +def get_frame(t): frequency = 1 # One pulse per second coef = 0.5 * (1 + math.sin(2 * math.pi * frequency * t)) # radius varies over time radius = WIDTH * coef @@ -25,6 +25,6 @@ def frame_function(t): # we define a 2s duration for the clip to be able to render it later -clip = VideoClip(frame_function, duration=2) +clip = VideoClip(get_frame, duration=2) # we must set a framerate because VideoClip have no framerate by default clip.write_gif("circle.gif", fps=15) diff --git a/docs/_static/code/user_guide/loading/loading.py b/docs/_static/code/user_guide/loading/loading.py index 3ebc677ed..3f166f55b 100644 --- a/docs/_static/code/user_guide/loading/loading.py +++ b/docs/_static/code/user_guide/loading/loading.py @@ -14,12 +14,12 @@ black = (255, 255, 255) # RGB for black -def frame_function(t): +def get_frame(t): """Random noise image of 200x100""" return np.random.randint(low=0, high=255, size=(100, 200, 3)) -def frame_function_audio(t): +def get_frame_audio(t): """A note by producing a sinewave of 440 Hz""" return np.sin(440 * 2 * np.pi * t) @@ -27,9 +27,9 @@ def frame_function_audio(t): # Now lets see how to load different type of resources ! # VIDEO CLIPS -# for custom animations, where frame_function is a function returning an image +# for custom animations, where get_frame is a function returning an image # as numpy array for a given time -clip = VideoClip(frame_function, duration=5) +clip = VideoClip(get_frame, duration=5) clip = VideoFileClip("example.mp4") # for videos # for a list or directory of images to be used as a video sequence clip = ImageSequenceClip("example_img_dir", fps=24) @@ -42,6 +42,6 @@ def frame_function_audio(t): # AUDIO CLIPS # for audio files, but also videos where you only want the keep the audio track clip = AudioFileClip("example.wav") -# for custom audio, where frame_function is a function returning a +# for custom audio, where get_frame is a function returning a # float (or tuple for stereo) for a given time -clip = AudioClip(frame_function_audio, duration=3) +clip = AudioClip(get_frame_audio, duration=3) diff --git a/docs/_static/code/user_guide/loading/masks.py b/docs/_static/code/user_guide/loading/masks.py index a58f48de5..edcd12ede 100644 --- a/docs/_static/code/user_guide/loading/masks.py +++ b/docs/_static/code/user_guide/loading/masks.py @@ -2,10 +2,10 @@ import numpy as np # Random RGB noise image of 200x100 -frame_function = lambda t: np.random.rand(100, 200) +get_frame = lambda t: np.random.rand(100, 200) # To define the VideoClip as a mask, just pass parameter is_mask as True -maskclip1 = VideoClip(frame_function, duration=4, is_mask=True) # A random noise mask +maskclip1 = VideoClip(get_frame, duration=4, is_mask=True) # A random noise mask maskclip2 = ImageClip("example_mask.jpg", is_mask=True) # A fixed mask as jpeg maskclip3 = VideoFileClip("example_mask.mp4", is_mask=True) # A video as a mask diff --git a/docs/user_guide/loading.rst b/docs/user_guide/loading.rst index 28bdb87d0..bcb69231e 100644 --- a/docs/user_guide/loading.rst +++ b/docs/user_guide/loading.rst @@ -65,7 +65,7 @@ VideoClip """""""""" :py:class:`~moviepy.video.VideoClip.VideoClip` is the base class for all the other video clips in MoviePy. If all you want is to edit video files, you will never need it. This class is practical when you want to make animations from frames that are generated by another library. -All you need is to define a function ``frame_function(t)`` which returns a `HxWx3` numpy array (of 8-bits integers) representing the frame at time ``t``. +All you need is to define a function ``get_frame(t)`` which returns a `HxWx3` numpy array (of 8-bits integers) representing the frame at time ``t``. Here is an example where we will create a pulsating red circle with graphical library `pillow `_. @@ -81,7 +81,7 @@ Resulting in this. .. note:: - Clips that are made with a ``frame_function`` do not have an explicit frame rate nor duration by default, so you must provide duration at clip creation and a frame rate (``fps``, frames per second) for :py:meth:`~moviepy.video.VideoClip.VideoClip.write_gif` and :py:meth:`~moviepy.video.VideoClip.VideoClip.write_videofile`, and more generally for any methods that requires iterating through the frames. + Clips that are made with a ``get_frame`` do not have an explicit frame rate nor duration by default, so you must provide duration at clip creation and a frame rate (``fps``, frames per second) for :py:meth:`~moviepy.video.VideoClip.VideoClip.write_gif` and :py:meth:`~moviepy.video.VideoClip.VideoClip.write_videofile`, and more generally for any methods that requires iterating through the frames. For more, see :py:class:`~moviepy.video.VideoClip.VideoClip`. @@ -137,7 +137,7 @@ UpdatedVideoClip .. warning:: This is really advanced usage, you will probably never need it, if you do, please go read the code. -:py:class:`~moviepy.video.io.VideoClip.UpdatedVideoClip` is a video whose frame_function requires some objects to be updated before we can compute it. +:py:class:`~moviepy.video.io.VideoClip.UpdatedVideoClip` is a video whose get_frame requires some objects to be updated before we can compute it. This is particularly practical in science where some algorithm needs to make some steps before a new frame can be generated, or maybe when trying to make a video based on a live exterior context. @@ -244,7 +244,7 @@ AudioClip :py:class:`~moviepy.audio.AudioClip.AudioClip` is the base class for all audio clips. If all you want is to edit audio files, you will never need it. -All you need is to define a function ``frame_function(t)`` which returns a ``Nx1`` or ``Nx2`` numpy array representing the sound at time ``t``. +All you need is to define a function ``get_frame(t)`` which returns a ``Nx1`` or ``Nx2`` numpy array representing the sound at time ``t``. .. literalinclude:: /_static/code/user_guide/loading/AudioClip.py :language: python diff --git a/moviepy/Clip.py b/moviepy/Clip.py index 871ed8e3e..9c8305d32 100644 --- a/moviepy/Clip.py +++ b/moviepy/Clip.py @@ -79,12 +79,12 @@ def get_frame(self, t): if t == self.memoized_t: return self.memoized_frame else: - frame = self.frame_function(t) + frame = self.get_frame(t) self.memoized_t = t self.memoized_frame = frame return frame else: - return self.frame_function(t) + return self.get_frame(t) def transform(self, func, apply_to=None, keep_duration=True): """General processing of a clip. @@ -126,8 +126,8 @@ def transform(self, func, apply_to=None, keep_duration=True): if apply_to is None: apply_to = [] - # mf = copy(self.frame_function) - new_clip = self.with_updated_frame_function(lambda t: func(self.get_frame, t)) + # mf = copy(self.get_frame) + new_clip = self.with_updated_get_frame(lambda t: func(self.get_frame, t)) if not keep_duration: new_clip.duration = None @@ -296,17 +296,17 @@ def with_duration(self, duration, change_end=True): self.start = self.end - duration @outplace - def with_updated_frame_function(self, frame_function): - """Sets a ``frame_function`` attribute for the clip. Useful for setting + def with_updated_get_frame(self, get_frame): + """Sets a ``get_frame`` attribute for the clip. Useful for setting arbitrary/complicated videoclips. Parameters ---------- - frame_function : function + get_frame : function New frame creator function for the clip. """ - self.frame_function = frame_function + self.get_frame = get_frame def with_fps(self, fps, change_duration=False): """Returns a copy of the clip with a new default fps for functions like diff --git a/moviepy/audio/AudioClip.py b/moviepy/audio/AudioClip.py index edcaf4888..ce35ab28b 100644 --- a/moviepy/audio/AudioClip.py +++ b/moviepy/audio/AudioClip.py @@ -22,7 +22,7 @@ class AudioClip(Clip): See ``AudioFileClip`` and ``CompositeAudioClip`` for usable classes. - An AudioClip is a Clip with a ``frame_function`` attribute of + An AudioClip is a Clip with a ``get_frame`` attribute of the form `` t -> [ f_t ]`` for mono sound and ``t-> [ f1_t, f2_t ]`` for stereo sound (the arrays are Numpy arrays). The `f_t` are floats between -1 and 1. These bounds can be @@ -32,7 +32,7 @@ class AudioClip(Clip): Parameters ---------- - frame_function + get_frame A function `t-> frame at time t`. The frame does not mean much for a sound, it is just a float. What 'makes' the sound are the variations of that float in the time. @@ -51,28 +51,28 @@ class AudioClip(Clip): # Plays the note A in mono (a sine wave of frequency 440 Hz) import numpy as np - frame_function = lambda t: np.sin(440 * 2 * np.pi * t) - clip = AudioClip(frame_function, duration=5, fps=44100) + get_frame = lambda t: np.sin(440 * 2 * np.pi * t) + clip = AudioClip(get_frame, duration=5, fps=44100) clip.preview() # Plays the note A in stereo (two sine waves of frequencies 440 and 880 Hz) - frame_function = lambda t: np.array([ + get_frame = lambda t: np.array([ np.sin(440 * 2 * np.pi * t), np.sin(880 * 2 * np.pi * t) ]).T.copy(order="C") - clip = AudioClip(frame_function, duration=3, fps=44100) + clip = AudioClip(get_frame, duration=3, fps=44100) clip.preview() """ - def __init__(self, frame_function=None, duration=None, fps=None): + def __init__(self, get_frame=None, duration=None, fps=None): super().__init__() if fps is not None: self.fps = fps - if frame_function is not None: - self.frame_function = frame_function + if get_frame is not None: + self.get_frame = get_frame frame0 = self.get_frame(0) if hasattr(frame0, "__iter__"): self.nchannels = len(list(frame0)) @@ -337,7 +337,7 @@ def __init__(self, array, fps): self.fps = fps self.duration = 1.0 * len(array) / fps - def frame_function(t): + def get_frame(t): """Complicated, but must be able to handle the case where t is a list of the form sin(t). """ @@ -354,7 +354,7 @@ def frame_function(t): else: return self.array[i] - self.frame_function = frame_function + self.get_frame = get_frame self.nchannels = len(list(self.get_frame(0))) @@ -402,7 +402,7 @@ def ends(self): """Returns ending times for all clips in the composition.""" return (clip.end for clip in self.clips) - def frame_function(self, t): + def get_frame(self, t): """Renders a frame for the composition for the time ``t``.""" played_parts = [clip.is_playing(t) for clip in self.clips] diff --git a/moviepy/audio/fx/AudioDelay.py b/moviepy/audio/fx/AudioDelay.py index d1611d07b..bbf3ba19d 100644 --- a/moviepy/audio/fx/AudioDelay.py +++ b/moviepy/audio/fx/AudioDelay.py @@ -42,10 +42,10 @@ class AudioDelay(Effect): ]) # stereo A note - frame_function = lambda t: np.array( + get_frame = lambda t: np.array( [np.sin(440 * 2 * np.pi * t), np.sin(880 * 2 * np.pi * t)] ).T - clip = AudioClip(frame_function=frame_function, duration=0.1, fps=44100) + clip = AudioClip(get_frame=get_frame, duration=0.1, fps=44100) clip = clip.with_effects([afx.AudioDelay(offset=.2, n_repeats=11, decay=0)]) """ diff --git a/moviepy/audio/io/AudioFileClip.py b/moviepy/audio/io/AudioFileClip.py index 67c30d069..cc1e5ada1 100644 --- a/moviepy/audio/io/AudioFileClip.py +++ b/moviepy/audio/io/AudioFileClip.py @@ -75,7 +75,7 @@ def __init__( self.buffersize = self.reader.buffersize self.filename = filename - self.frame_function = lambda t: self.reader.get_frame(t) + self.get_frame = lambda t: self.reader.get_frame(t) self.nchannels = self.reader.nchannels def close(self): diff --git a/moviepy/video/VideoClip.py b/moviepy/video/VideoClip.py index e92e7d5b9..6d49c2012 100644 --- a/moviepy/video/VideoClip.py +++ b/moviepy/video/VideoClip.py @@ -77,7 +77,7 @@ class VideoClip(Clip): is_mask Boolean set to `True` if the clip is a mask. - frame_function + get_frame A function ``t-> frame at time t`` where ``frame`` is a w*h*3 RGB array. @@ -104,7 +104,7 @@ class VideoClip(Clip): """ def __init__( - self, frame_function=None, is_mask=False, duration=None, has_constant_size=True + self, get_frame=None, is_mask=False, duration=None, has_constant_size=True ): super().__init__() self.mask = None @@ -112,8 +112,8 @@ def __init__( self.pos = lambda t: (0, 0) self.relative_pos = False self.layer_index = 0 - if frame_function: - self.frame_function = frame_function + if get_frame: + self.get_frame = get_frame self.size = self.get_frame(0).shape[:2][::-1] self.is_mask = is_mask self.has_constant_size = has_constant_size @@ -835,15 +835,13 @@ def with_background_color(self, size=None, color=(0, 0, 0), pos=None, opacity=No return result @outplace - def with_updated_frame_function( - self, frame_function: Callable[[float], np.ndarray] - ): + def with_updated_get_frame(self, get_frame: Callable[[float], np.ndarray]): """Change the clip's ``get_frame``. - Returns a copy of the VideoClip instance, with the frame_function + Returns a copy of the VideoClip instance, with the get_frame attribute set to `mf`. """ - self.frame_function = frame_function + self.get_frame = get_frame self.size = self.get_frame(0).shape[:2][::-1] @outplace @@ -867,10 +865,10 @@ def with_mask(self, mask: Union["VideoClip", str] = "auto"): mask = ColorClip(self.size, 1.0, is_mask=True) else: - def frame_function(t): + def get_frame(t): return np.ones(self.get_frame(t).shape[:2], dtype=float) - mask = VideoClip(is_mask=True, frame_function=frame_function) + mask = VideoClip(is_mask=True, get_frame=get_frame) self.mask = mask @outplace @@ -1128,12 +1126,12 @@ def __init__(self, data, data_to_frame, fps, is_mask=False, has_constant_size=Tr self.data_to_frame = data_to_frame self.fps = fps - def frame_function(t): + def get_frame(t): return self.data_to_frame(self.data[int(self.fps * t)]) VideoClip.__init__( self, - frame_function, + get_frame, is_mask=is_mask, duration=1.0 * len(data) / fps, has_constant_size=has_constant_size, @@ -1142,16 +1140,16 @@ def frame_function(t): class UpdatedVideoClip(VideoClip): """ - Class of clips whose frame_function requires some objects to + Class of clips whose get_frame requires some objects to be updated. Particularly practical in science where some algorithm needs to make some steps before a new frame can be generated. - UpdatedVideoClips have the following frame_function: + UpdatedVideoClips have the following get_frame: .. code:: python - def frame_function(t): + def get_frame(t): while self.world.clip_t < t: world.update() # updates, and increases world.clip_t return world.to_frame() @@ -1177,13 +1175,13 @@ def frame_function(t): def __init__(self, world, is_mask=False, duration=None): self.world = world - def frame_function(t): + def get_frame(t): while self.world.clip_t < t: world.update() return world.to_frame() VideoClip.__init__( - self, frame_function=frame_function, is_mask=is_mask, duration=duration + self, get_frame=get_frame, is_mask=is_mask, duration=duration ) @@ -1254,7 +1252,7 @@ def __init__( # if the image was just a 2D mask, it should arrive here # unchanged - self.frame_function = lambda t: img + self.get_frame = lambda t: img self.size = img.shape[:2][::-1] self.img = img @@ -1286,7 +1284,7 @@ def image_transform(self, image_func, apply_to=None): apply_to = [] arr = image_func(self.get_frame(0)) self.size = arr.shape[:2][::-1] - self.frame_function = lambda t: arr + self.get_frame = lambda t: arr self.img = arr for attr in apply_to: @@ -1875,7 +1873,7 @@ def __init__( VideoClip.__init__( self, - frame_function=lambda t: frame_array[int(t * fps)], + get_frame=lambda t: frame_array[int(t * fps)], is_mask=is_mask, duration=duration, ) diff --git a/moviepy/video/compositing/CompositeVideoClip.py b/moviepy/video/compositing/CompositeVideoClip.py index df905c292..e39de1d4c 100644 --- a/moviepy/video/compositing/CompositeVideoClip.py +++ b/moviepy/video/compositing/CompositeVideoClip.py @@ -116,7 +116,7 @@ def __init__( maskclips, self.size, is_mask=True, bg_color=0.0 ) - def frame_function(self, t): + def get_frame(self, t): """The clips playing at time `t` are blitted over one another.""" frame = self.bg.get_frame(t).astype("uint8") im = Image.fromarray(frame) @@ -284,7 +284,7 @@ def concatenate_videoclips( if method == "chain": - def frame_function(t): + def get_frame(t): i = max([i for i, e in enumerate(timings) if e <= t]) return clips[i].get_frame(t - timings[i]) @@ -294,7 +294,7 @@ def get_mask(clip): mask.duration = clip.duration return mask - result = VideoClip(is_mask=is_mask, frame_function=frame_function) + result = VideoClip(is_mask=is_mask, get_frame=get_frame) if any([clip.mask is not None for clip in clips]): masks = [get_mask(clip) for clip in clips] result.mask = concatenate_videoclips(masks, method="chain", is_mask=True) diff --git a/moviepy/video/io/ImageSequenceClip.py b/moviepy/video/io/ImageSequenceClip.py index 96b407f0c..c160cd184 100644 --- a/moviepy/video/io/ImageSequenceClip.py +++ b/moviepy/video/io/ImageSequenceClip.py @@ -121,7 +121,7 @@ def find_image_index(t): self.last_index = None self.last_image = None - def frame_function(t): + def get_frame(t): index = find_image_index(t) if index != self.last_index: @@ -135,7 +135,7 @@ def frame_function(t): self.mask.last_index = None self.mask.last_image = None - def mask_frame_function(t): + def mask_get_frame(t): index = find_image_index(t) if index != self.mask.last_index: frame = imread(self.sequence[index])[:, :, 3] @@ -144,24 +144,24 @@ def mask_frame_function(t): return self.mask.last_image - self.mask.frame_function = mask_frame_function - self.mask.size = mask_frame_function(0).shape[:2][::-1] + self.mask.get_frame = mask_get_frame + self.mask.size = mask_get_frame(0).shape[:2][::-1] else: - def frame_function(t): + def get_frame(t): index = find_image_index(t) return self.sequence[index][:, :, :3] if with_mask and (self.sequence[0].shape[2] == 4): self.mask = VideoClip(is_mask=True) - def mask_frame_function(t): + def mask_get_frame(t): index = find_image_index(t) return 1.0 * self.sequence[index][:, :, 3] / 255 - self.mask.frame_function = mask_frame_function - self.mask.size = mask_frame_function(0).shape[:2][::-1] + self.mask.get_frame = mask_get_frame + self.mask.size = mask_get_frame(0).shape[:2][::-1] - self.frame_function = frame_function - self.size = frame_function(0).shape[:2][::-1] + self.get_frame = get_frame + self.size = get_frame(0).shape[:2][::-1] diff --git a/moviepy/video/io/VideoFileClip.py b/moviepy/video/io/VideoFileClip.py index 67ec8cf5a..30c2b5c4b 100644 --- a/moviepy/video/io/VideoFileClip.py +++ b/moviepy/video/io/VideoFileClip.py @@ -125,18 +125,18 @@ def __init__( self.filename = filename if has_mask: - self.frame_function = lambda t: self.reader.get_frame(t)[:, :, :3] + self.get_frame = lambda t: self.reader.get_frame(t)[:, :, :3] - def mask_frame_function(t): + def mask_get_frame(t): return self.reader.get_frame(t)[:, :, 3] / 255.0 - self.mask = VideoClip( - is_mask=True, frame_function=mask_frame_function - ).with_duration(self.duration) + self.mask = VideoClip(is_mask=True, get_frame=mask_get_frame).with_duration( + self.duration + ) self.mask.fps = self.fps else: - self.frame_function = lambda t: self.reader.get_frame(t) + self.get_frame = lambda t: self.reader.get_frame(t) # Make a reader for the audio, if any. if audio and self.reader.infos["audio_found"]: diff --git a/moviepy/video/tools/subtitles.py b/moviepy/video/tools/subtitles.py index b240f2de4..1e1dec322 100644 --- a/moviepy/video/tools/subtitles.py +++ b/moviepy/video/tools/subtitles.py @@ -111,7 +111,7 @@ def add_textclip_if_none(t): return sub - def frame_function(t): + def get_frame(t): sub = add_textclip_if_none(t) return self.textclips[sub].get_frame(t) if sub else np.array([[[0, 0, 0]]]) @@ -119,7 +119,7 @@ def make_mask_frame(t): sub = add_textclip_if_none(t) return self.textclips[sub].mask.get_frame(t) if sub else np.array([[0]]) - self.frame_function = frame_function + self.get_frame = get_frame hasmask = bool(self.make_textclip("T").mask) self.mask = VideoClip(make_mask_frame, is_mask=True) if hasmask else None diff --git a/tests/test_AudioClips.py b/tests/test_AudioClips.py index b818d4b93..e21c01987 100644 --- a/tests/test_AudioClips.py +++ b/tests/test_AudioClips.py @@ -186,7 +186,7 @@ def test_audioclip_mono_max_volume(mono_wave): @pytest.mark.parametrize(("nchannels"), (2, 4, 8, 16)) @pytest.mark.parametrize(("channel_muted"), ("left", "right")) def test_audioclip_stereo_max_volume(nchannels, channel_muted): - def frame_function(t): + def get_frame(t): frame = [] # build channels (one of each pair muted) for i in range(int(nchannels / 2)): @@ -200,7 +200,7 @@ def frame_function(t): frame.append(np.sin(t * 0)) return np.array(frame).T - clip = AudioClip(frame_function, fps=44100, duration=1) + clip = AudioClip(get_frame, fps=44100, duration=1) max_volume = clip.max_volume(stereo=True) # if `stereo == True`, `AudioClip.max_volume` returns a Numpy array` assert isinstance(max_volume, np.ndarray) diff --git a/tests/test_VideoClip.py b/tests/test_VideoClip.py index 53c04817b..14d28aad3 100644 --- a/tests/test_VideoClip.py +++ b/tests/test_VideoClip.py @@ -215,8 +215,8 @@ def test_oncolor(util): def test_setaudio(util): clip = ColorClip(size=(100, 60), color=(255, 0, 0), duration=0.5) - frame_function_440 = lambda t: [np.sin(440 * 2 * np.pi * t)] - audio = AudioClip(frame_function_440, duration=0.5) + get_frame_440 = lambda t: [np.sin(440 * 2 * np.pi * t)] + audio = AudioClip(get_frame_440, duration=0.5) audio.fps = 44100 clip = clip.with_audio(audio) location = os.path.join(util.TMP_DIR, "setaudio.mp4") diff --git a/tests/test_compositing.py b/tests/test_compositing.py index 43018d747..b4f359a6a 100644 --- a/tests/test_compositing.py +++ b/tests/test_compositing.py @@ -16,18 +16,19 @@ def __init__(self, clip): self.clip = clip def expect_color_at(self, ts, expected, xy=[0, 0]): - frame = self.clip.frame_function(ts) + frame = self.clip.get_frame(ts) r, g, b = expected actual = frame[xy[1]][xy[0]] diff = abs(actual[0] - r) + abs(actual[1] - g) + abs(actual[2] - b) mismatch = diff > ClipPixelTest.ALLOWABLE_COLOR_VARIATION - assert ( - not mismatch - ), "Expected (%02x,%02x,%02x) but got (%02x,%02x,%02x) at timestamp %s" % ( - *expected, - *actual, - ts, + assert not mismatch, ( + "Expected (%02x,%02x,%02x) but got (%02x,%02x,%02x) at timestamp %s" + % ( + *expected, + *actual, + ts, + ) ) diff --git a/tests/test_fx.py b/tests/test_fx.py index 746b84913..71ed15bff 100644 --- a/tests/test_fx.py +++ b/tests/test_fx.py @@ -1082,8 +1082,8 @@ def test_audio_normalize(): def test_audio_normalize_muted(): z_array = np.array([0.0]) - frame_function = lambda t: z_array - clip = AudioClip(frame_function, duration=1, fps=44100) + get_frame = lambda t: z_array + clip = AudioClip(get_frame, duration=1, fps=44100) clip = clip.with_effects([afx.AudioNormalize()]) assert np.array_equal(clip.to_soundarray(), z_array) @@ -1173,17 +1173,17 @@ def test_multiply_volume_audioclip( end_time, ): if sound_type == "stereo": - frame_function = lambda t: np.array( + get_frame = lambda t: np.array( [ np.sin(440 * 2 * np.pi * t), np.sin(160 * 2 * np.pi * t), ] ).T.copy(order="C") else: - frame_function = lambda t: [np.sin(440 * 2 * np.pi * t)] + get_frame = lambda t: [np.sin(440 * 2 * np.pi * t)] clip = AudioClip( - frame_function, + get_frame, duration=duration if duration else 0.1, fps=22050, ) @@ -1374,7 +1374,7 @@ def test_audio_delay(stereo_wave, duration, offset, n_repeats, decay): # stereo audio clip clip = AudioClip( - frame_function=stereo_wave(left_freq=440, right_freq=880), + get_frame=stereo_wave(left_freq=440, right_freq=880), duration=duration, fps=44100, ) @@ -1448,11 +1448,11 @@ def test_audio_fadein( mono_wave, stereo_wave, sound_type, fps, clip_duration, fadein_duration ): if sound_type == "stereo": - frame_function = stereo_wave(left_freq=440, right_freq=160) + get_frame = stereo_wave(left_freq=440, right_freq=160) else: - frame_function = mono_wave(440) + get_frame = mono_wave(440) - clip = AudioClip(frame_function, duration=clip_duration, fps=fps) + clip = AudioClip(get_frame, duration=clip_duration, fps=fps) new_clip = clip.with_effects([afx.AudioFadeIn(fadein_duration)]) # first frame is muted @@ -1509,11 +1509,11 @@ def test_audio_fadeout( mono_wave, stereo_wave, sound_type, fps, clip_duration, fadeout_duration ): if sound_type == "stereo": - frame_function = stereo_wave(left_freq=440, right_freq=160) + get_frame = stereo_wave(left_freq=440, right_freq=160) else: - frame_function = mono_wave(440) + get_frame = mono_wave(440) - clip = AudioClip(frame_function, duration=clip_duration, fps=fps) + clip = AudioClip(get_frame, duration=clip_duration, fps=fps) new_clip = clip.with_effects([afx.AudioFadeOut(fadeout_duration)]) fadeout_duration = convert_to_seconds(fadeout_duration) diff --git a/tests/test_videotools.py b/tests/test_videotools.py index 52c4e24ae..e51372d9f 100644 --- a/tests/test_videotools.py +++ b/tests/test_videotools.py @@ -1088,8 +1088,8 @@ def test_find_audio_period(mono_wave, stereo_wave, wave_type): wave2 = stereo_wave(left_freq=100, right_freq=200) clip = CompositeAudioClip( [ - AudioClip(frame_function=wave1, duration=0.3, fps=22050), - AudioClip(frame_function=wave2, duration=0.3, fps=22050).with_effects( + AudioClip(get_frame=wave1, duration=0.3, fps=22050), + AudioClip(get_frame=wave2, duration=0.3, fps=22050).with_effects( [afx.MultiplyVolume(0, end_time=0.1)] ), ] From 6d8f5e21476c006ba390853e66336e6088e15620 Mon Sep 17 00:00:00 2001 From: Zulko Date: Sun, 24 Nov 2024 11:24:11 -0500 Subject: [PATCH 2/2] name fixes --- moviepy/video/VideoClip.py | 4 ++-- tests/test_VideoClip.py | 23 ++++++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/moviepy/video/VideoClip.py b/moviepy/video/VideoClip.py index 6d49c2012..2543211e5 100644 --- a/moviepy/video/VideoClip.py +++ b/moviepy/video/VideoClip.py @@ -637,7 +637,7 @@ def preview( # ----------------------------------------------------------------- # F I L T E R I N G - def with_effects_on_subclip( + def with_effects_on_time_range( self, effects: List["Effect"], start_time=0, end_time=None, **kwargs ): """Apply a transformation to a part of the clip. @@ -653,7 +653,7 @@ def with_effects_on_subclip( # The scene between times t=3s and t=6s in ``clip`` will be # be played twice slower in ``new_clip`` - new_clip = clip.with_sub_effect(MultiplySpeed(0.5), 3, 6) + new_clip = clip.with_effects_on_time_range(MultiplySpeed(0.5), 3, 6) """ left = None if (start_time == 0) else self.subclipped(0, start_time) diff --git a/tests/test_VideoClip.py b/tests/test_VideoClip.py index 14d28aad3..a5b43cc79 100644 --- a/tests/test_VideoClip.py +++ b/tests/test_VideoClip.py @@ -8,7 +8,17 @@ import pytest -from moviepy import * +from moviepy import ( + VideoClip, + VideoFileClip, + AudioClip, + AudioFileClip, + BitmapClip, + ColorClip, + CompositeVideoClip, + ImageClip, + vfx, +) from moviepy.tools import convert_to_seconds @@ -182,10 +192,10 @@ def test_write_gif(util, video): assert os.path.isfile(location) -def test_with_sub_effetcs(util): +def test_with_effects_on_time_range(util): clip = VideoFileClip("media/big_buck_bunny_0_30.webm").subclipped(0, 1) - new_clip = clip.with_effects_on_subclip([vfx.MultiplySpeed(0.5)]) - location = os.path.join(util.TMP_DIR, "with_effects_on_subclip.mp4") + new_clip = clip.with_effects_on_time_range([vfx.MultiplySpeed(0.5)]) + location = os.path.join(util.TMP_DIR, "with_effects_on_time_range.mp4") new_clip.write_videofile(location) assert os.path.isfile(location) @@ -215,7 +225,10 @@ def test_oncolor(util): def test_setaudio(util): clip = ColorClip(size=(100, 60), color=(255, 0, 0), duration=0.5) - get_frame_440 = lambda t: [np.sin(440 * 2 * np.pi * t)] + + def get_frame_440(t): + return [np.sin(440 * 2 * np.pi * t)] + audio = AudioClip(get_frame_440, duration=0.5) audio.fps = 44100 clip = clip.with_audio(audio)