videopython 0.4.0__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of videopython might be problematic. Click here for more details.
- videopython/ai/understanding/transcribe.py +48 -19
- videopython/base/combine.py +45 -0
- videopython/base/text/__init__.py +0 -0
- videopython/{utils/text.py → base/text/overlay.py} +383 -8
- videopython/base/text/transcription.py +121 -0
- videopython/base/utils.py +6 -0
- videopython/base/video.py +164 -77
- videopython-0.5.0.dist-info/METADATA +194 -0
- {videopython-0.4.0.dist-info → videopython-0.5.0.dist-info}/RECORD +11 -12
- videopython/base/compose.py +0 -55
- videopython/base/transcription.py +0 -13
- videopython/utils/__init__.py +0 -3
- videopython/utils/common.py +0 -31
- videopython/utils/image.py +0 -47
- videopython-0.4.0.dist-info/METADATA +0 -118
- {videopython-0.4.0.dist-info → videopython-0.5.0.dist-info}/WHEEL +0 -0
- {videopython-0.4.0.dist-info → videopython-0.5.0.dist-info}/licenses/LICENSE +0 -0
videopython/base/compose.py
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
from itertools import repeat
|
|
2
|
-
from multiprocessing import Pool
|
|
3
|
-
|
|
4
|
-
from videopython.base.transforms import TransformationPipeline
|
|
5
|
-
from videopython.base.transitions import InstantTransition, Transition
|
|
6
|
-
from videopython.base.video import Video
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class VideoComposer:
|
|
10
|
-
"""
|
|
11
|
-
Composes multiple Videos into single video using selected transformations
|
|
12
|
-
on each video and applies transitions.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
def __init__(
|
|
16
|
-
self,
|
|
17
|
-
transformation_pipeline: TransformationPipeline | None = None,
|
|
18
|
-
transition: Transition = InstantTransition(),
|
|
19
|
-
):
|
|
20
|
-
"""Initializes VideoComposer.
|
|
21
|
-
|
|
22
|
-
Args:
|
|
23
|
-
transformation_pipeline: Pipeline of transformations to apply on each video.
|
|
24
|
-
transition: Transition to apply between videos
|
|
25
|
-
"""
|
|
26
|
-
self.transition = transition
|
|
27
|
-
self.transformation_pipeline = transformation_pipeline
|
|
28
|
-
|
|
29
|
-
def _apply_transformation(self, video: Video, transformation_pipeline: TransformationPipeline) -> Video:
|
|
30
|
-
return transformation_pipeline(video)
|
|
31
|
-
|
|
32
|
-
def compose(self, videos: list[Video]) -> Video:
|
|
33
|
-
# Apply transformation on each video using multiprocessing pool:
|
|
34
|
-
if self.transformation_pipeline:
|
|
35
|
-
transformed_videos = []
|
|
36
|
-
with Pool() as pool:
|
|
37
|
-
transformed_videos = pool.starmap(
|
|
38
|
-
self._apply_transformation,
|
|
39
|
-
zip(videos, repeat(self.transformation_pipeline)),
|
|
40
|
-
)
|
|
41
|
-
videos = transformed_videos
|
|
42
|
-
|
|
43
|
-
# Check if videos are compatible:
|
|
44
|
-
self._compatibility_check(videos)
|
|
45
|
-
|
|
46
|
-
# Apply transition:
|
|
47
|
-
final_video = videos.pop(0)
|
|
48
|
-
for _ in range(len(videos)):
|
|
49
|
-
final_video = self.transition.apply((final_video, videos.pop(0)))
|
|
50
|
-
|
|
51
|
-
return final_video
|
|
52
|
-
|
|
53
|
-
@staticmethod
|
|
54
|
-
def _compatibility_check(videos: list[Video]):
|
|
55
|
-
assert all([videos[0].metadata.can_be_merged_with(other_video.metadata) for other_video in videos])
|
videopython/utils/__init__.py
DELETED
videopython/utils/common.py
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import time
|
|
2
|
-
import uuid
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Callable
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def generate_random_name(suffix=".mp4"):
|
|
8
|
-
"""Generates random name."""
|
|
9
|
-
return f"{uuid.uuid4()}{suffix}"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def timeit(func: Callable):
|
|
13
|
-
"""Decorator to measure execution time of a function."""
|
|
14
|
-
|
|
15
|
-
def timed(*args, **kwargs):
|
|
16
|
-
start = time.time()
|
|
17
|
-
result = func(*args, **kwargs)
|
|
18
|
-
end = time.time()
|
|
19
|
-
print(f"Execution time: {end - start:.3f} seconds.")
|
|
20
|
-
return result
|
|
21
|
-
|
|
22
|
-
return timed
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def check_path(path: str, dir_exists: bool = True, suffix: str | None = None) -> str:
|
|
26
|
-
fullpath = Path(path).resolve()
|
|
27
|
-
if dir_exists and not fullpath.parent.exists():
|
|
28
|
-
raise ValueError(f"Directory `{fullpath.parent}` does not exist!")
|
|
29
|
-
if suffix and suffix != fullpath.suffix:
|
|
30
|
-
raise ValueError(f"Required suffix `{suffix}` does not match the file suffix `{fullpath.suffix}`")
|
|
31
|
-
return str(fullpath)
|
videopython/utils/image.py
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
from typing import Literal
|
|
2
|
-
|
|
3
|
-
import cv2
|
|
4
|
-
import numpy as np
|
|
5
|
-
|
|
6
|
-
from videopython.base.video import Video
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class SlideOverImage:
|
|
10
|
-
def __init__(
|
|
11
|
-
self,
|
|
12
|
-
direction: Literal["left", "right"],
|
|
13
|
-
video_shape: tuple[int, int] = (1080, 1920),
|
|
14
|
-
fps: float = 24.0,
|
|
15
|
-
length_seconds: float = 1.0,
|
|
16
|
-
) -> None:
|
|
17
|
-
self.direction = direction
|
|
18
|
-
self.video_width, self.video_height = video_shape
|
|
19
|
-
self.fps = fps
|
|
20
|
-
self.length_seconds = length_seconds
|
|
21
|
-
|
|
22
|
-
def apply(self, image: np.ndarray) -> Video:
|
|
23
|
-
image = self._resize(image)
|
|
24
|
-
max_offset = image.shape[1] - self.video_width
|
|
25
|
-
frame_count = round(self.fps * self.length_seconds)
|
|
26
|
-
|
|
27
|
-
deltas = np.linspace(0, max_offset, frame_count)
|
|
28
|
-
frames = []
|
|
29
|
-
|
|
30
|
-
for delta in deltas:
|
|
31
|
-
if self.direction == "right":
|
|
32
|
-
frame = image[:, round(delta) : round(delta) + self.video_width]
|
|
33
|
-
elif self.direction == "left":
|
|
34
|
-
frame = image[:, image.shape[1] - round(delta) - self.video_width : image.shape[1] - round(delta)]
|
|
35
|
-
frames.append(frame)
|
|
36
|
-
|
|
37
|
-
return Video.from_frames(frames=np.stack(frames, axis=0), fps=self.fps)
|
|
38
|
-
|
|
39
|
-
def _resize(self, image: np.ndarray) -> np.ndarray:
|
|
40
|
-
resize_factor = image.shape[0] / self.video_height
|
|
41
|
-
resize_dims = (round(image.shape[1] / resize_factor), round(image.shape[0] / resize_factor)) # width, height
|
|
42
|
-
image = cv2.resize(image, resize_dims)
|
|
43
|
-
if self.video_height > image.shape[0] or self.video_width > image.shape[1]:
|
|
44
|
-
raise ValueError(
|
|
45
|
-
f"Image `{image.shape}` is too small for the video frame `({self.video_width}, {self.video_height})`!"
|
|
46
|
-
)
|
|
47
|
-
return image
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: videopython
|
|
3
|
-
Version: 0.4.0
|
|
4
|
-
Summary: Minimal video generation and processing library.
|
|
5
|
-
Project-URL: Homepage, https://github.com/bartwojtowicz/videopython/
|
|
6
|
-
Project-URL: Repository, https://github.com/bartwojtowicz/videopython/
|
|
7
|
-
Project-URL: Documentation, https://github.com/bartwojtowicz/videopython/
|
|
8
|
-
Author-email: Bartosz Wójtowicz <bartoszwojtowicz@outlook.com>, Bartosz Rudnikowicz <bartoszrudnikowicz840@gmail.com>, Piotr Pukisz <piotr.pukisz@gmail.com>
|
|
9
|
-
License: Apache-2.0
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Keywords: editing,generation,movie,opencv,python,video,videopython
|
|
12
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
-
Classifier: Operating System :: OS Independent
|
|
14
|
-
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
-
Requires-Python: <3.13,>=3.10
|
|
18
|
-
Requires-Dist: numpy>=1.25.2
|
|
19
|
-
Requires-Dist: opencv-python>=4.9.0.80
|
|
20
|
-
Requires-Dist: pillow>=10.3.0
|
|
21
|
-
Requires-Dist: pydub>=0.25.1
|
|
22
|
-
Requires-Dist: soundpython>=0.1.11
|
|
23
|
-
Requires-Dist: tqdm>=4.66.3
|
|
24
|
-
Description-Content-Type: text/markdown
|
|
25
|
-
|
|
26
|
-
# About
|
|
27
|
-
|
|
28
|
-
Minimal video generation and processing library.
|
|
29
|
-
|
|
30
|
-
## Setup
|
|
31
|
-
|
|
32
|
-
### Install ffmpeg
|
|
33
|
-
```bash
|
|
34
|
-
# Install with brew for MacOS:
|
|
35
|
-
brew install ffmpeg
|
|
36
|
-
# Install with apt-get for Ubuntu:
|
|
37
|
-
sudo apt-get install ffmpeg
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Install with pip
|
|
41
|
-
```bash
|
|
42
|
-
pip install videopython[ai]
|
|
43
|
-
```
|
|
44
|
-
> You can install without `[ai]` dependencies for basic video handling and processing.
|
|
45
|
-
> The funcionalities found in `videopython.ai` won't work.
|
|
46
|
-
|
|
47
|
-
## Basic Usage
|
|
48
|
-
|
|
49
|
-
### Video handling
|
|
50
|
-
|
|
51
|
-
```python
|
|
52
|
-
from videopython.base.video import Video
|
|
53
|
-
|
|
54
|
-
# Load videos and print metadata
|
|
55
|
-
video1 = Video.from_path("tests/test_data/small_video.mp4")
|
|
56
|
-
print(video1)
|
|
57
|
-
|
|
58
|
-
video2 = Video.from_path("tests/test_data/big_video.mp4")
|
|
59
|
-
print(video2)
|
|
60
|
-
|
|
61
|
-
# Define the transformations
|
|
62
|
-
from videopython.base.transforms import CutSeconds, ResampleFPS, Resize, TransformationPipeline
|
|
63
|
-
|
|
64
|
-
pipeline = TransformationPipeline(
|
|
65
|
-
[CutSeconds(start=1.5, end=6.5), ResampleFPS(fps=30), Resize(width=1000, height=1000)]
|
|
66
|
-
)
|
|
67
|
-
video1 = pipeline.run(video1)
|
|
68
|
-
video2 = pipeline.run(video2)
|
|
69
|
-
|
|
70
|
-
# Combine videos, add audio and save
|
|
71
|
-
from videopython.base.transitions import FadeTransition
|
|
72
|
-
|
|
73
|
-
fade = FadeTransition(effect_time_seconds=3.0)
|
|
74
|
-
video = fade.apply(videos=(video1, video2))
|
|
75
|
-
video.add_audio_from_file("tests/test_data/test_audio.mp3")
|
|
76
|
-
|
|
77
|
-
savepath = video.save()
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### Video Generation
|
|
81
|
-
|
|
82
|
-
> Using Nvidia A40 or better is recommended for the `videopython.ai` module.
|
|
83
|
-
```python
|
|
84
|
-
# Generate image and animate it
|
|
85
|
-
from videopython.ai.generation import ImageToVideo
|
|
86
|
-
from videopython.ai.generation import TextToImage
|
|
87
|
-
from videopython.ai.generation import TextToMusic
|
|
88
|
-
|
|
89
|
-
image = TextToImage().generate_image(prompt="Golden Retriever playing in the park")
|
|
90
|
-
video = ImageToVideo().generate_video(image=image, fps=24)
|
|
91
|
-
|
|
92
|
-
# Video generation directly from prompt
|
|
93
|
-
from videopython.ai.generation import TextToVideo
|
|
94
|
-
video_gen = TextToVideo()
|
|
95
|
-
video = video_gen.generate_video("Dogs playing in the snow")
|
|
96
|
-
for _ in range(10):
|
|
97
|
-
video += video_gen.generate_video("Dogs playing in the snow")
|
|
98
|
-
|
|
99
|
-
# Cut the first 2 seconds
|
|
100
|
-
from videopython.base.transforms import CutSeconds
|
|
101
|
-
transformed_video = CutSeconds(start_second=0, end_second=2).apply(video.copy())
|
|
102
|
-
|
|
103
|
-
# Upsample to 30 FPS
|
|
104
|
-
from videopython.base.transforms import ResampleFPS
|
|
105
|
-
transformed_video = ResampleFPS(new_fps=30).apply(transformed_video)
|
|
106
|
-
|
|
107
|
-
# Resize to 1000x1000
|
|
108
|
-
from videopython.base.transforms import Resize
|
|
109
|
-
transformed_video = Resize(width=1000, height=1000).apply(transformed_video)
|
|
110
|
-
|
|
111
|
-
# Add generated music
|
|
112
|
-
# MusicGen cannot generate more than 1503 tokens (~30seconds of audio)
|
|
113
|
-
text_to_music = TextToMusic()
|
|
114
|
-
audio = text_to_music.generate_audio("Happy dogs playing together in a park", max_new_tokens=256)
|
|
115
|
-
transformed_video.add_audio(audio=audio)
|
|
116
|
-
|
|
117
|
-
filepath = transformed_video.save()
|
|
118
|
-
```
|
|
File without changes
|
|
File without changes
|