import numpy as np from moviepy.decorators import apply_to_mask try: from PIL import Image PIL_FOUND = True def pil_rotater(pic, angle, resample, expand): return np.array( Image.fromarray(pic).rotate(angle, expand=expand, resample=resample)) except ImportError: PIL_FOUND = False def rotate(clip, angle, unit='deg', resample="bicubic", expand=True): """ Change unit to 'rad' to define angles as radians. If the angle is not one of 90, 180, -90, -180 (degrees) there will be black borders. You can make them transparent with >>> newclip = clip.add_mask().rotate(72) Parameters =========== clip A video clip angle Either a value or a function angle(t) representing the angle of rotation unit Unit of parameter `angle` (either `deg` for degrees or `rad` for radians) resample One of "nearest", "bilinear", or "bicubic". expand Only applIf False, the clip will maintain the same True, the clip will be resized so that the whole """ resample = {"bilinear": Image.BILINEAR, "nearest": Image.NEAREST, "bicubic": Image.BICUBIC}[resample] if not hasattr(angle, '__call__'): # if angle is a constant, convert to a constant function a = +angle angle = lambda t: a transpo = [1,0] if clip.ismask else [1,0,2] def fl(gf, t): a = angle(t) im = gf(t) if unit == 'rad': a = 360.0*a/(2*np.pi) if (a==90) and expand: return np.transpose(im, axes=transpo)[::-1] elif (a==-90) and expand: return np.transpose(im, axes=transpo)[:,::-1] elif (a in [180, -180]) and expand: return im[::-1,::-1] elif not PIL_FOUND: raise ValueError('Without "Pillow" installed, only angles 90, -90,' '180 are supported, please install "Pillow" with' "pip install pillow") else: return pil_rotater(im, a, resample=resample, expand=expand) return clip.fl(fl, apply_to=["mask"])