mirror of
https://github.com/Zulko/moviepy.git
synced 2021-07-27 01:17:47 +03:00
* Add module docstrings except in FXs * Update flake8 config * Remove 'find_latest_imagemagick_version.py' * Add tests modules docstrings * Don't execute doctests for tests/ modules * Prevent test error in Windows
167 lines
4.9 KiB
Python
167 lines
4.9 KiB
Python
"""Implements ImageSequenceClip, a class to create a video clip from a set
|
|
of image files.
|
|
"""
|
|
|
|
import os
|
|
|
|
import numpy as np
|
|
from imageio import imread
|
|
|
|
from moviepy.video.VideoClip import VideoClip
|
|
|
|
|
|
class ImageSequenceClip(VideoClip):
|
|
"""A VideoClip made from a series of images.
|
|
|
|
Parameters
|
|
----------
|
|
|
|
sequence
|
|
Can be one of these:
|
|
|
|
- The name of a folder (containing only pictures). The pictures
|
|
will be considered in alphanumerical order.
|
|
- A list of names of image files. In this case you can choose to
|
|
load the pictures in memory pictures
|
|
- A list of Numpy arrays representing images. In this last case,
|
|
masks are not supported currently.
|
|
|
|
fps
|
|
Number of picture frames to read per second. Instead, you can provide
|
|
the duration of each image with durations (see below)
|
|
|
|
durations
|
|
List of the duration of each picture.
|
|
|
|
with_mask
|
|
Should the alpha layer of PNG images be considered as a mask ?
|
|
|
|
is_mask
|
|
Will this sequence of pictures be used as an animated mask.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
sequence,
|
|
fps=None,
|
|
durations=None,
|
|
with_mask=True,
|
|
is_mask=False,
|
|
load_images=False,
|
|
):
|
|
|
|
# CODE WRITTEN AS IT CAME, MAY BE IMPROVED IN THE FUTURE
|
|
|
|
if (fps is None) and (durations is None):
|
|
raise ValueError("Please provide either 'fps' or 'durations'.")
|
|
VideoClip.__init__(self, is_mask=is_mask)
|
|
|
|
# Parse the data
|
|
|
|
fromfiles = True
|
|
|
|
if isinstance(sequence, list):
|
|
if isinstance(sequence[0], str):
|
|
if load_images:
|
|
sequence = [imread(file) for file in sequence]
|
|
fromfiles = False
|
|
else:
|
|
fromfiles = True
|
|
else:
|
|
# sequence is already a list of numpy arrays
|
|
fromfiles = False
|
|
else:
|
|
# sequence is a folder name, make it a list of files:
|
|
fromfiles = True
|
|
sequence = sorted(
|
|
[os.path.join(sequence, file) for file in os.listdir(sequence)]
|
|
)
|
|
|
|
# check that all the images are of the same size
|
|
if isinstance(sequence[0], str):
|
|
size = imread(sequence[0]).shape
|
|
else:
|
|
size = sequence[0].shape
|
|
|
|
for image in sequence:
|
|
image1 = image
|
|
if isinstance(image, str):
|
|
image1 = imread(image)
|
|
if size != image1.shape:
|
|
raise Exception(
|
|
"Moviepy: ImageSequenceClip requires all images to be the same size"
|
|
)
|
|
|
|
self.fps = fps
|
|
if fps is not None:
|
|
durations = [1.0 / fps for image in sequence]
|
|
self.images_starts = [
|
|
1.0 * i / fps - np.finfo(np.float32).eps for i in range(len(sequence))
|
|
]
|
|
else:
|
|
self.images_starts = [0] + list(np.cumsum(durations))
|
|
self.durations = durations
|
|
self.duration = sum(durations)
|
|
self.end = self.duration
|
|
self.sequence = sequence
|
|
|
|
def find_image_index(t):
|
|
return max(
|
|
[i for i in range(len(self.sequence)) if self.images_starts[i] <= t]
|
|
)
|
|
|
|
if fromfiles:
|
|
|
|
self.last_index = None
|
|
self.last_image = None
|
|
|
|
def make_frame(t):
|
|
|
|
index = find_image_index(t)
|
|
|
|
if index != self.last_index:
|
|
self.last_image = imread(self.sequence[index])[:, :, :3]
|
|
self.last_index = index
|
|
|
|
return self.last_image
|
|
|
|
if with_mask and (imread(self.sequence[0]).shape[2] == 4):
|
|
|
|
self.mask = VideoClip(is_mask=True)
|
|
self.mask.last_index = None
|
|
self.mask.last_image = None
|
|
|
|
def mask_make_frame(t):
|
|
|
|
index = find_image_index(t)
|
|
if index != self.mask.last_index:
|
|
frame = imread(self.sequence[index])[:, :, 3]
|
|
self.mask.last_image = frame.astype(float) / 255
|
|
self.mask.last_index = index
|
|
|
|
return self.mask.last_image
|
|
|
|
self.mask.make_frame = mask_make_frame
|
|
self.mask.size = mask_make_frame(0).shape[:2][::-1]
|
|
|
|
else:
|
|
|
|
def make_frame(t):
|
|
|
|
index = find_image_index(t)
|
|
return self.sequence[index][:, :, :3]
|
|
|
|
if with_mask and (self.sequence[0].shape[2] == 4):
|
|
|
|
self.mask = VideoClip(is_mask=True)
|
|
|
|
def mask_make_frame(t):
|
|
index = find_image_index(t)
|
|
return 1.0 * self.sequence[index][:, :, 3] / 255
|
|
|
|
self.mask.make_frame = mask_make_frame
|
|
self.mask.size = mask_make_frame(0).shape[:2][::-1]
|
|
|
|
self.make_frame = make_frame
|
|
self.size = make_frame(0).shape[:2][::-1]
|