Skip to content

Instantly share code, notes, and snippets.

@mowshon
Last active July 30, 2025 11:21
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')

@smrht
Copy link

smrht commented Jul 30, 2025

Thanks allot! this is great work

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