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