pythonanimationvideomoviepy

Moviepy zooming effects need tweaking


I would like to zoom a clip to a certain dimension, then stop further zooming. In other words, the clip stop further increasing its size after reaching a certain size, and its better if the clip start zooming from much smaller of its original size, and to a bigger version. I am using moviepy module. With the following code I can progressively zoom a clip, but having a hard time figuring out how to grow a clip from small to big, creating an animated effect. Any feedback you provide is greatly appreciated.

import os
from moviepy.editor import *

screensize = (640,360)

clip = (ImageClip(img)
        .resize(height=screensize[1]*4)
        .resize(lambda t : 1+0.02*t)
        .set_position(('center', 'center'))
        .set_duration(10)
        )

I am having a very hard time figuring out how to write a function in order to create a type-writing effect with moviepy's txtclip, meaning letters within the clip will be shown one after another, creating a smooth type-writer animation text. Moviepy got some helpful classes such as findObjects--which can detect individual letters out of a clip:

txtClip = TextClip('Cool effect',color='white', font="Amiri-Bold",
                   kerning = 5, fontsize=100)
cvc = CompositeVideoClip( [txtClip.set_pos('center')],
                        size=screensize, transparent=True)

letters = findObjects(cvc) # a list of ImageClips 

here is the link: http://zulko.github.io/moviepy/examples/moving_letters.html

after finding the letters(letter clips) I would like to join them in such a way that they appear one after another, which looks like type writing.

The documentation already got a few examples of moving letters, which might be of great use, however. Thank you


Solution

  • This is a way of achieving what you want. The key is to define the resizing logic in a named function instead of using a lambda.

    import os
    from moviepy.editor import *
    
    
    def resize_func(t):
        if t < 4:
            return 1 + 0.2*t  # Zoom-in.
        elif 4 <= t <= 6:
            return 1 + 0.2*4  # Stay.
        else: # 6 < t
            return 1 + 0.2*(duration-t)  # Zoom-out.
    
    duration = 10
    screensize = (640,360)
    
    clip_img = (
        ImageClip('test.png')
        .resize(screensize)
        .resize(resize_func)
        .set_position(('center', 'center'))
        .set_duration(duration)
        .set_fps(25)
        )
    
    clip = CompositeVideoClip([clip_img], size=screensize)
    clip.write_videofile('test.mp4')
    

    EDIT

    The code that follows answers the second part of your question (I don't know if it'd be better to make two separate questions).

    from __future__ import division
    from moviepy.editor import *
    from moviepy.video.tools.segmenting import findObjects
    
    
    def clip_typewriter(text, duration_clip, duration_effect):
        # `duration_effect` is effectively the time where the last letter appears.
        clip_text = TextClip(
                        text,
                        color='white',
                        font='Consolas',
                        fontsize=80,
                        kerning=3,
                        )
        letters = findObjects(clip_text, preview=False)
        # Select the start time in seconds for each letter found:
        n = len(letters)
        times_start = [duration_effect*i/(n-1) for i in range(n)]
        clips_letters = []
        for i, letter in enumerate(letters):
            clips_letters.append(letter
                .set_position(letter.screenpos)
                .set_start(times_start[i])
                .set_end(duration_clip)
                # Here you can add key sounds using `set_audio`.
                )
        return CompositeVideoClip(clips_letters, size=clip_text.size)
    
    
    if __name__ == '__main__':
        screensize = (320, 180)
        fps = 12
        clip_1 = clip_typewriter('hello', 2, 1).set_start(1).set_position('center')
        clip_2 = clip_typewriter('world', 2, 1).set_start(4).set_position('center')
        clip_final = CompositeVideoClip([clip_1, clip_2], size=screensize)
        clip_final.write_gif('test_typewriter.gif', fps=fps)
    

    Result:

    enter image description here