Source code for moviepy.video.fx.Resize
import numbers
from dataclasses import dataclass
from typing import Union
import numpy as np
from PIL import Image
from moviepy.Effect import Effect
[docs]
@dataclass
class Resize(Effect):
"""Effect returning a video clip that is a resized version of the clip.
Parameters
----------
new_size : tuple or float or function, optional
Can be either
- ``(width, height)`` in pixels or a float representing
- A scaling factor, like ``0.5``.
- A function of time returning one of these.
height : int, optional
Height of the new clip in pixels. The width is then computed so
that the width/height ratio is conserved.
width : int, optional
Width of the new clip in pixels. The height is then computed so
that the width/height ratio is conserved.
Examples
--------
.. code:: python
clip.with_effects([vfx.Resize((460,720))]) # New resolution: (460,720)
clip.with_effects([vfx.Resize(0.6)]) # width and height multiplied by 0.6
clip.with_effects([vfx.Resize(width=800)]) # height computed automatically.
clip.with_effects([vfx.Resize(lambda t : 1+0.02*t)]) # slow clip swelling
"""
new_size: Union[tuple, float, callable] = None
height: int = None
width: int = None
apply_to_mask: bool = True
[docs]
def resizer(self, pic, new_size):
"""Resize the image using PIL."""
new_size = list(map(int, new_size))
pil_img = Image.fromarray(pic)
resized_pil = pil_img.resize(new_size, Image.Resampling.LANCZOS)
return np.array(resized_pil)
[docs]
def apply(self, clip):
"""Apply the effect to the clip."""
w, h = clip.size
if self.new_size is not None:
def translate_new_size(new_size_):
"""Returns a [w, h] pair from `new_size_`. If `new_size_` is a
scalar, then work out the correct pair using the clip's size.
Otherwise just return `new_size_`
"""
if isinstance(new_size_, numbers.Number):
return [new_size_ * w, new_size_ * h]
else:
return new_size_
if hasattr(self.new_size, "__call__"):
# The resizing is a function of time
def get_new_size(t):
return translate_new_size(self.new_size(t))
if clip.is_mask:
def filter(get_frame, t):
return (
self.resizer(
(255 * get_frame(t)).astype("uint8"), get_new_size(t)
)
/ 255.0
)
else:
def filter(get_frame, t):
return self.resizer(
get_frame(t).astype("uint8"), get_new_size(t)
)
newclip = clip.transform(
filter,
keep_duration=True,
apply_to=(["mask"] if self.apply_to_mask else []),
)
if self.apply_to_mask and clip.mask is not None:
newclip.mask = clip.mask.with_effects(
[Resize(self.new_size, apply_to_mask=False)]
)
return newclip
else:
self.new_size = translate_new_size(self.new_size)
elif self.height is not None:
if hasattr(self.height, "__call__"):
def func(t):
return 1.0 * int(self.height(t)) / h
return clip.with_effects([Resize(func)])
else:
self.new_size = [w * self.height / h, self.height]
elif self.width is not None:
if hasattr(self.width, "__call__"):
def func(t):
return 1.0 * self.width(t) / w
return clip.with_effects([Resize(func)])
else:
self.new_size = [self.width, h * self.width / w]
else:
raise ValueError(
"You must provide either 'new_size' or 'height' or 'width'"
)
# From here, the resizing is constant (not a function of time), size=newsize
if clip.is_mask:
def image_filter(pic):
return (
1.0
* self.resizer((255 * pic).astype("uint8"), self.new_size)
/ 255.0
)
else:
def image_filter(pic):
return self.resizer(pic.astype("uint8"), self.new_size)
new_clip = clip.image_transform(image_filter)
if self.apply_to_mask and clip.mask is not None:
new_clip.mask = clip.mask.with_effects(
[Resize(self.new_size, apply_to_mask=False)]
)
return new_clip