Zoom-In Effect for moviepy
See original GitHub issueI am trying to create a video from slides. Each slide has a zoom-in effect. All the code examples for creating the zoom-in effect gave poor results if the zoom speed is very slow. The side effect was a slight twitching.
Below I will provide examples that the documentation offers and the function that I offer.
The problem I faced too: https://github.com/Zulko/moviepy/issues/183
Example from documentation
https://zulko.github.io/moviepy/ref/videofx/moviepy.video.fx.all.resize.html
import moviepy.editor as mp
size = (1920, 1080)
img = 'https://www.colorado.edu/cumuseum/sites/default/files/styles/widescreen/public/slider/coachwhip2_1.jpg'
slide = mp.ImageClip(img).set_fps(25).set_duration(10).resize(size)
slide = slide.resize(lambda t: 1 + 0.04 * t) # Zoom-in effect
slide = slide.set_position(('center', 'center'))
slide = mp.CompositeVideoClip([slide], size=size)
slide.write_videofile('zoom-test-1.mp4')
Result: https://youtu.be/qlU_4hVFm6I
My function
Slideshow example: https://gist.github.com/mowshon/2a0664fab0ae799734594a5e91e518d5
import moviepy.editor as mp
import math
from PIL import Image
import numpy
def zoom_in_effect(clip, zoom_ratio=0.04):
def effect(get_frame, t):
img = Image.fromarray(get_frame(t))
base_size = img.size
new_size = [
math.ceil(img.size[0] * (1 + (zoom_ratio * t))),
math.ceil(img.size[1] * (1 + (zoom_ratio * t)))
]
# The new dimensions must be even.
new_size[0] = new_size[0] + (new_size[0] % 2)
new_size[1] = new_size[1] + (new_size[1] % 2)
img = img.resize(new_size, Image.LANCZOS)
x = math.ceil((new_size[0] - base_size[0]) / 2)
y = math.ceil((new_size[1] - base_size[1]) / 2)
img = img.crop([
x, y, new_size[0] - x, new_size[1] - y
]).resize(base_size, Image.LANCZOS)
result = numpy.array(img)
img.close()
return result
return clip.fl(effect)
size = (1920, 1080)
img = 'https://www.colorado.edu/cumuseum/sites/default/files/styles/widescreen/public/slider/coachwhip2_1.jpg'
slide = mp.ImageClip(img).set_fps(25).set_duration(10).resize(size)
slide = zoom_in_effect(slide, 0.04)
slide.write_videofile('zoom-test-2.mp4')
Result: https://youtu.be/U-A54E00sC8
In my example, there is also a slight wobble, but not as obvious as in the first example. Below is a link to a video where you can compare both options.
Comparation video: https://www.youtube.com/watch?v=UPyYdrwWE14
- Left side: My function
- Right side: Example from docs
I would be glad to get advice on improving the code.
Issue Analytics
- State:
- Created 3 years ago
- Comments:6 (2 by maintainers)
Looking at the various
resizer
s inmoviepy/video/fx/resize.py
it looks like they are in fact casting the width and height toint
s. I’m wondering if the aliasing algorithms can account for fractions of pixels. But that may be beyond the scope of whatmoviepy
s contract covers, as it’s usingOpenCV
,Pillow
, andSciPy
to resize.Proposed solution
Always maintain the ratio of width-to-height by first calculating the new width, then calculating the height based on the width. So replace
with something like
At the end, I ended up generating separate videos of zooming with command line ffmpeg (credits to: https://superuser.com/a/1112680)
increase the zoom_smooth parameter for example to 10 to get a smoother zoom (it will also take you more time/resources)