Skip to content

Instantly share code, notes, and snippets.

@mowshon
Last active February 21, 2025 09:55
Show Gist options
  • Save mowshon/2a0664fab0ae799734594a5e91e518d5 to your computer and use it in GitHub Desktop.
Save mowshon/2a0664fab0ae799734594a5e91e518d5 to your computer and use it in GitHub Desktop.
Zoom-in Effect for Moviepy. This function makes the zoom effect smoother.
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.transform(effect)
size = (1920, 1080)
images = [
'https://www.colorado.edu/cumuseum/sites/default/files/styles/widescreen/public/slider/coachwhip2_1.jpg',
'https://www.colorado.edu/cumuseum/sites/default/files/styles/widescreen/public/slider/green2_1.jpg',
'https://www.colorado.edu/cumuseum/sites/default/files/styles/widescreen/public/slider/westterrgarter_1.jpg',
'https://www.colorado.edu/cumuseum/sites/default/files/styles/widescreen/public/slider/prairierattle4.jpg'
]
slides = []
for n, url in enumerate(images):
slides.append(
mp.ImageClip(url).set_fps(25).set_duration(5).resize(size)
)
slides[n] = zoom_in_effect(slides[n], 0.04)
video = mp.concatenate_videoclips(slides)
video.write_videofile('zoomin.mp4')
@robertpalmerpw
Copy link

Thanks for the code, best way to zoom I have found.

@ScottTylerHall349
Copy link

This is a 10/10

@alexyca
Copy link

alexyca commented Jun 22, 2023

Thanks you, this is perfect working

@fandyaditya
Copy link

Thankyou!

@hucara
Copy link

hucara commented Mar 18, 2024

Not really doing anything for me. Is this just for images or also for videoclips?

@jojoabing
Copy link

Not really doing anything for me. Is this just for images or also for videoclips?

It only works for images as far as I can see. I guess you would have to get the individual frames as images and feed them in instead.

@salah55s
Copy link

much thanks, keep the heat up. and do so for a video too

@juaandominguez
Copy link

Thank you so much, the best zoom function for mp without a doubt!

@mowshon
Copy link
Author

mowshon commented Sep 4, 2024

You can use it for video as well, just use your VideoFileClip instead of ImageClip.

@Izolovi4
Copy link

Izolovi4 commented Sep 7, 2024

is there any way to make borders expend as well?

@harsh-zymr
Copy link

harsh-zymr commented Dec 30, 2024

for the 2024-25 people fl is replaced with transform.

@mowshon
Copy link
Author

mowshon commented Dec 30, 2024

@harsh-zymr thank you
Updated

@clemlesne
Copy link

Correction with typing and comments:

from collections.abc import Callable
from moviepy import VideoClip
from PIL import Image
from PIL.Image import Resampling
import math
import numpy


def zoom_effect(
    clip: VideoClip,
    ratio: float = 0.04,
) -> VideoClip:
    """
    Apply a zoom effect to a clip.
    """

    def _apply(
        get_frame: Callable[[float], numpy.ndarray],
        t: float,
    ) -> numpy.ndarray:
        # Get the frame
        img = Image.fromarray(get_frame(t))
        base_size = img.size

        # Calculate the new size
        new_size = (
            math.ceil(img.size[0] * (1 + (ratio * t))),
            math.ceil(img.size[1] * (1 + (ratio * t))),
        )

        # Make the size even
        new_size = (
            new_size[0] + (new_size[0] % 2),
            new_size[1] + (new_size[1] % 2),
        )

        # Resize the image
        img = img.resize(new_size, Resampling.LANCZOS)

        # Crop the image
        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, Resampling.LANCZOS
        )

        # Convert to numpy array and return
        result = numpy.array(img)
        img.close()
        return result

    return clip.transform(_apply)

@JohanManders
Copy link

Complete working example for using local images with MoviePy V2:

from collections.abc import Callable
from moviepy import *
from PIL import Image
from PIL.Image import Resampling
import math
import numpy

def zoom_effect(
    clip: VideoClip,
    ratio: float = 0.04,
) -> VideoClip:
    """
    Apply a zoom effect to a clip.
    """

    def _apply(
        get_frame: Callable[[float], numpy.ndarray],
        t: float,
    ) -> numpy.ndarray:
        # Get the frame
        img = Image.fromarray(get_frame(t))
        base_size = img.size

        # Calculate the new size
        new_size = (
            math.ceil(img.size[0] * (1 + (ratio * t))),
            math.ceil(img.size[1] * (1 + (ratio * t))),
        )

        # Make the size even
        new_size = (
            new_size[0] + (new_size[0] % 2),
            new_size[1] + (new_size[1] % 2),
        )

        # Resize the image
        img = img.resize(new_size, Resampling.LANCZOS)

        # Crop the image
        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, Resampling.LANCZOS
        )

        # Convert to numpy array and return
        result = numpy.array(img)
        img.close()
        return result

    return clip.transform(_apply)

size = (1920, 1080)

images = [
    '001.jpg',
    '002.jpg',
    '003.jpg',
    '004.jpg'
]

slides = []
for n, url in enumerate(images):
    slides.append(
        ImageClip(url).with_fps(24).with_duration(5).resized(size)
    )

    slides[n] = zoom_effect(slides[n])

video = concatenate_videoclips(slides)
video.write_videofile('zoomin.mp4')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment