Skip to content

Commit 33a3d7a

Browse files
committed
Added the docstrings.
1 parent 04eff7d commit 33a3d7a

File tree

1 file changed

+158
-5
lines changed

1 file changed

+158
-5
lines changed

vidiopy/video/ImageSequenceClip.py

Lines changed: 158 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,34 @@
11
import os
22
import math
33
from pathlib import Path
4-
from typing import Callable, Self, Any
4+
from typing import Callable
55
from PIL import Image
66
import numpy as np
77
from ..decorators import *
88
from .VideoClip import VideoClip
99

1010

1111
class ImageSequenceClip(VideoClip):
12+
"""
13+
A class used to represent a sequence of images as a video clip.
14+
15+
This class extends the VideoClip class and provides additional functionality for handling sequences of images. It allows for the creation of a video clip from a sequence of images, with the ability to specify the frames per second (fps) and duration of the clip. The sequence of images can be provided as a tuple of PIL Images, paths to images, numpy arrays, or a path to a directory. The class also provides methods for importing the image sequence, generating a numpy array or PIL Image representation of a specific frame in the clip, and applying a function to each frame of the clip.
16+
17+
Attributes:
18+
clip (tuple[Image.Image, ...]): The sequence of images as a tuple of PIL Images.
19+
fps (int | float | None): The frames per second of the clip.
20+
_dur (int | float | None): The duration of the clip in seconds.
21+
audio (optional): The audio of the clip.
22+
23+
Methods:
24+
__init__(sequence, fps=None, duration=None, audio=None): Initializes an instance of the ImageSequenceClip class.
25+
__eq__(other): Checks if this instance is equal to another instance.
26+
_import_image_sequence(sequence): Imports an image sequence from a tuple of PIL Images, paths to images, numpy arrays, or a path to a directory.
27+
make_frame_array(t): Generates a numpy array representation of a specific frame in the clip.
28+
make_frame_pil(t): Generates a PIL Image representation of a specific frame in the clip.
29+
fl_frame_transform(func, *args, **kwargs): Applies a function to each frame of the clip.
30+
fl_clip_transform(func, *args, **kwargs): Applies a function to each frame of the clip along with its timestamp.
31+
"""
1232
def __init__(
1333
self,
1434
sequence: (
@@ -22,6 +42,28 @@ def __init__(
2242
duration: int | float | None = None,
2343
audio=None,
2444
):
45+
"""
46+
Initializes an instance of the ImageSequenceClip class.
47+
48+
This method imports an image sequence from the specified sequence, sets the fps and duration of the image sequence clip, and sets the audio of the image sequence clip if specified.
49+
50+
Args:
51+
sequence (str | Path | tuple[Image.Image, ...] | tuple[np.ndarray, ...] | tuple[str | Path, ...]): The sequence to import. It can be a tuple of PIL Images, paths to images, numpy arrays, or a path to a directory.
52+
fps (int | float | None, optional): The frames per second of the image sequence clip. If not specified, it is calculated from the duration and the number of images in the sequence.
53+
duration (int | float | None, optional): The duration of the image sequence clip in seconds. If not specified, it is calculated from the fps and the number of images in the sequence.
54+
audio (optional): The audio of the image sequence clip. If not specified, the image sequence clip will have no audio.
55+
56+
Raises:
57+
ValueError: If neither fps nor duration is specified.
58+
ValueError: If not all images in the sequence have the same size.
59+
60+
Example:
61+
>>> image_sequence_clip = ImageSequenceClip(("image1.jpg", "image2.jpg"), fps=24)
62+
63+
Note:
64+
This method uses the _import_image_sequence method to import the image sequence and the set_audio method to set the audio of the image sequence clip.
65+
"""
66+
# method body goes here
2567
super().__init__()
2668

2769
self.clip: tuple[Image.Image, ...] = self._import_image_sequence(sequence)
@@ -64,6 +106,27 @@ def _import_image_sequence(
64106
str | Path | tuple[str | Path] | tuple[Image.Image] | tuple[np.ndarray]
65107
),
66108
) -> tuple[Image.Image, ...]:
109+
"""
110+
Imports an image sequence from a tuple of PIL Images, paths to images, numpy arrays, or a path to a directory.
111+
112+
This method checks the type of the sequence argument and imports the image sequence accordingly. If the sequence is a tuple of PIL Images, it returns the sequence as is. If the sequence is a tuple of numpy arrays, it converts each numpy array to a PIL Image. If the sequence is a tuple of paths to images, it opens each image and returns a tuple of PIL Images. If the sequence is a path to a directory, it opens all images in the directory and returns a tuple of PIL Images.
113+
114+
Args:
115+
sequence (str | Path | tuple[str | Path] | tuple[Image.Image] | tuple[np.ndarray]): The sequence to import. It can be a tuple of PIL Images, paths to images, numpy arrays, or a path to a directory.
116+
117+
Returns:
118+
tuple[Image.Image, ...]: The imported image sequence as a tuple of PIL Images.
119+
120+
Raises:
121+
TypeError: If the sequence is not a tuple of PIL Images, paths to images, numpy arrays, or a path to a directory.
122+
123+
Example:
124+
>>> image_sequence_clip = ImageSequenceClip()
125+
>>> image_sequence = image_sequence_clip._import_image_sequence(("image1.jpg", "image2.jpg"))
126+
127+
Note:
128+
This method uses the PIL Image class to open images and convert numpy arrays to images.
129+
"""
67130
if isinstance(sequence, tuple):
68131
if isinstance(sequence[0], Image.Image):
69132
return sequence
@@ -92,14 +155,56 @@ def _import_image_sequence(
92155
)
93156

94157
@requires_duration_or_end
95-
def make_frame_array(self, t) -> np.ndarray:
158+
def make_frame_array(self, t: int | float) -> np.ndarray:
159+
"""
160+
Generates a numpy array representation of a specific frame in the image sequence clip.
161+
162+
This method calculates the index of the frame for a specific time, retrieves the frame from the image sequence clip, and converts it to a numpy array.
163+
164+
Args:
165+
t (int | float): The time of the frame to convert.
166+
167+
Returns:
168+
np.ndarray: The numpy array representation of the frame.
169+
170+
Raises:
171+
None
172+
173+
Example:
174+
>>> image_sequence_clip = ImageSequenceClip()
175+
>>> frame_array = image_sequence_clip.make_frame_array(10)
176+
177+
Note:
178+
This method uses the duration or end of the image sequence clip to calculate the time per frame.
179+
"""
96180
time_per_frame = (self.duration if self.duration else self.end) / len(self.clip)
97181
frame_index = math.floor(t / time_per_frame)
98182
frame_index = min(len(self.clip) - 1, max(0, frame_index))
99183
return np.array(self.clip[frame_index])
100184

101185
@requires_duration_or_end
102-
def make_frame_pil(self, t) -> Image.Image:
186+
def make_frame_pil(self, t: int | float) -> Image.Image:
187+
"""
188+
Generates a PIL Image representation of a specific frame in the image sequence clip.
189+
190+
This method calculates the index of the frame for a specific time, retrieves the frame from the image sequence clip, and returns it as a PIL Image.
191+
192+
Args:
193+
t (int | float): The time of the frame to convert.
194+
195+
Returns:
196+
Image.Image: The PIL Image representation of the frame.
197+
198+
Raises:
199+
ValueError: If neither the duration nor the end of the image sequence clip is set.
200+
201+
Example:
202+
>>> image_sequence_clip = ImageSequenceClip()
203+
>>> frame_image = image_sequence_clip.make_frame_pil(10)
204+
205+
Note:
206+
This method uses either the duration or the end of the image sequence clip to calculate the time per frame.
207+
"""
103208
if self.duration is None and self.end is None:
104209
raise ValueError("either duration or end must be set")
105210
time_per_frame = (self.duration if self.duration else self.end) / len(self.clip)
@@ -109,7 +214,30 @@ def make_frame_pil(self, t) -> Image.Image:
109214

110215
def fl_frame_transform(
111216
self, func: Callable[..., Image.Image], *args, **kwargs
112-
) -> Self:
217+
) -> "ImageSequenceClip":
218+
"""
219+
Applies a function to each frame of the image sequence clip.
220+
221+
This method iterates over each frame in the image sequence clip, applies a function to it, and replaces the original frame with the result. The function is expected to take a PIL Image as its first argument and return a PIL Image.
222+
223+
Args:
224+
func (Callable[..., Image.Image]): The function to apply to each frame. It should take a PIL Image as its first argument and return a PIL Image.
225+
*args: Additional positional arguments to pass to the function.
226+
**kwargs: Additional keyword arguments to pass to the function.
227+
228+
Returns:
229+
ImageSequenceClip: The current instance of the ImageSequenceClip class.
230+
231+
Raises:
232+
None
233+
234+
Example:
235+
>>> image_sequence_clip = ImageSequenceClip()
236+
>>> image_sequence_clip.fl_frame_transform(lambda frame: frame.rotate(90))
237+
238+
Note:
239+
This method modifies the current instance of the ImageSequenceClip class in-place.
240+
"""
113241
clip: list[Image.Image] = []
114242
for frame in self.clip:
115243
frame = func(frame, *args, **kwargs)
@@ -118,7 +246,32 @@ def fl_frame_transform(
118246
return self
119247

120248
@requires_fps
121-
def fl_clip_transform(self, func, *args, **kwargs):
249+
def fl_clip_transform(
250+
self, func: Callable[..., Image.Image], *args, **kwargs
251+
) -> "ImageSequenceClip":
252+
"""
253+
Applies a function to each frame of the image sequence clip along with its timestamp.
254+
255+
This method iterates over each frame in the image sequence clip, applies a function to it and its timestamp, and replaces the original frame with the result. The function is expected to take a PIL Image and a float as its first two arguments and return a PIL Image.
256+
257+
Args:
258+
func (Callable[..., Image.Image]): The function to apply to each frame. It should take a PIL Image and a float as its first two arguments and return a PIL Image.
259+
*args: Additional positional arguments to pass to the function.
260+
**kwargs: Additional keyword arguments to pass to the function.
261+
262+
Returns:
263+
ImageSequenceClip: The current instance of the ImageSequenceClip class.
264+
265+
Raises:
266+
ValueError: If the fps of the image sequence clip is not set.
267+
268+
Example:
269+
>>> image_sequence_clip = ImageSequenceClip()
270+
>>> image_sequence_clip.fl_clip_transform(lambda frame, t: frame.rotate(90 * t))
271+
272+
Note:
273+
This method modifies the current instance of the ImageSequenceClip class in-place.
274+
"""
122275
td = 1 / self.fps
123276
frame_time = 0.0
124277
clip = []

0 commit comments

Comments
 (0)