videopython 0.1.41__tar.gz → 0.2.1__tar.gz
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-0.2.1/.gitignore +140 -0
- videopython-0.2.1/PKG-INFO +130 -0
- {videopython-0.1.41 → videopython-0.2.1}/README.md +41 -1
- videopython-0.2.1/pyproject.toml +88 -0
- {videopython-0.1.41 → videopython-0.2.1}/src/videopython/base/transforms.py +18 -18
- {videopython-0.1.41 → videopython-0.2.1}/src/videopython/base/video.py +101 -58
- {videopython-0.1.41 → videopython-0.2.1}/src/videopython/generation/__init__.py +2 -1
- videopython-0.2.1/src/videopython/generation/audio.py +56 -0
- {videopython-0.1.41 → videopython-0.2.1}/src/videopython/generation/video.py +1 -1
- videopython-0.2.1/src/videopython/py.typed +0 -0
- videopython-0.2.1/src/videopython/utils/__init__.py +0 -0
- {videopython-0.1.41 → videopython-0.2.1}/src/videopython/utils/image.py +1 -1
- videopython-0.1.41/PKG-INFO +0 -283
- videopython-0.1.41/pyproject.toml +0 -43
- videopython-0.1.41/requirements-dev.txt +0 -7
- videopython-0.1.41/requirements-generation.txt +0 -4
- videopython-0.1.41/requirements.txt +0 -6
- videopython-0.1.41/setup.cfg +0 -4
- videopython-0.1.41/src/videopython/generation/audio.py +0 -22
- videopython-0.1.41/src/videopython.egg-info/PKG-INFO +0 -283
- videopython-0.1.41/src/videopython.egg-info/SOURCES.txt +0 -31
- videopython-0.1.41/src/videopython.egg-info/dependency_links.txt +0 -1
- videopython-0.1.41/src/videopython.egg-info/requires.txt +0 -21
- videopython-0.1.41/src/videopython.egg-info/top_level.txt +0 -1
- videopython-0.1.41/tests/test_compose.py +0 -35
- videopython-0.1.41/tests/test_effects.py +0 -71
- videopython-0.1.41/tests/test_transforms.py +0 -69
- videopython-0.1.41/tests/test_transitions.py +0 -40
- videopython-0.1.41/tests/test_utils.py +0 -11
- videopython-0.1.41/tests/test_video.py +0 -135
- {videopython-0.1.41 → videopython-0.2.1}/LICENSE +0 -0
- {videopython-0.1.41/src/videopython/base → videopython-0.2.1/src/videopython}/__init__.py +0 -0
- {videopython-0.1.41/src/videopython/utils → videopython-0.2.1/src/videopython/base}/__init__.py +0 -0
- {videopython-0.1.41 → videopython-0.2.1}/src/videopython/base/compose.py +0 -0
- {videopython-0.1.41 → videopython-0.2.1}/src/videopython/base/effects.py +0 -0
- {videopython-0.1.41 → videopython-0.2.1}/src/videopython/base/exceptions.py +0 -0
- {videopython-0.1.41 → videopython-0.2.1}/src/videopython/base/transitions.py +0 -0
- {videopython-0.1.41 → videopython-0.2.1}/src/videopython/generation/image.py +0 -0
- {videopython-0.1.41 → videopython-0.2.1}/src/videopython/utils/common.py +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
pip-wheel-metadata/
|
|
24
|
+
share/python-wheels/
|
|
25
|
+
*.egg-info/
|
|
26
|
+
.installed.cfg
|
|
27
|
+
*.egg
|
|
28
|
+
MANIFEST
|
|
29
|
+
|
|
30
|
+
# PyInstaller
|
|
31
|
+
# Usually these files are written by a python script from a template
|
|
32
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
33
|
+
*.manifest
|
|
34
|
+
*.spec
|
|
35
|
+
|
|
36
|
+
# Installer logs
|
|
37
|
+
pip-log.txt
|
|
38
|
+
pip-delete-this-directory.txt
|
|
39
|
+
|
|
40
|
+
# Unit test / coverage reports
|
|
41
|
+
htmlcov/
|
|
42
|
+
.tox/
|
|
43
|
+
.nox/
|
|
44
|
+
.coverage
|
|
45
|
+
.coverage.*
|
|
46
|
+
.cache
|
|
47
|
+
nosetests.xml
|
|
48
|
+
coverage.xml
|
|
49
|
+
*.cover
|
|
50
|
+
*.py,cover
|
|
51
|
+
.hypothesis/
|
|
52
|
+
.pytest_cache/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
target/
|
|
76
|
+
|
|
77
|
+
# Jupyter Notebook
|
|
78
|
+
.ipynb_checkpoints
|
|
79
|
+
|
|
80
|
+
# IPython
|
|
81
|
+
profile_default/
|
|
82
|
+
ipython_config.py
|
|
83
|
+
|
|
84
|
+
# pyenv
|
|
85
|
+
.python-version
|
|
86
|
+
|
|
87
|
+
# pipenv
|
|
88
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
89
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
90
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
91
|
+
# install all needed dependencies.
|
|
92
|
+
#Pipfile.lock
|
|
93
|
+
|
|
94
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
95
|
+
__pypackages__/
|
|
96
|
+
|
|
97
|
+
# Celery stuff
|
|
98
|
+
celerybeat-schedule
|
|
99
|
+
celerybeat.pid
|
|
100
|
+
|
|
101
|
+
# SageMath parsed files
|
|
102
|
+
*.sage.py
|
|
103
|
+
|
|
104
|
+
# Environments
|
|
105
|
+
.env
|
|
106
|
+
.venv
|
|
107
|
+
env/
|
|
108
|
+
venv/
|
|
109
|
+
ENV/
|
|
110
|
+
env.bak/
|
|
111
|
+
venv.bak/
|
|
112
|
+
|
|
113
|
+
# Spyder project settings
|
|
114
|
+
.spyderproject
|
|
115
|
+
.spyproject
|
|
116
|
+
|
|
117
|
+
# Rope project settings
|
|
118
|
+
.ropeproject
|
|
119
|
+
|
|
120
|
+
# mkdocs documentation
|
|
121
|
+
/site
|
|
122
|
+
|
|
123
|
+
# mypy
|
|
124
|
+
.mypy_cache/
|
|
125
|
+
.dmypy.json
|
|
126
|
+
dmypy.json
|
|
127
|
+
|
|
128
|
+
# type checker
|
|
129
|
+
.pyre/
|
|
130
|
+
.mypy_cache/
|
|
131
|
+
|
|
132
|
+
# Random shit
|
|
133
|
+
*.ipynb
|
|
134
|
+
.vscode
|
|
135
|
+
*.csv
|
|
136
|
+
|
|
137
|
+
# Data directories
|
|
138
|
+
data/downloaded/*.mp4
|
|
139
|
+
data/exported/*.mp4
|
|
140
|
+
!data/exported/example.mp4
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: videopython
|
|
3
|
+
Version: 0.2.1
|
|
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: tqdm>=4.66.3
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: black==24.3.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: isort==5.12.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: mypy==1.8.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: pydub-stubs==0.25.1.1; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest==7.4.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: types-pillow==10.2.0.20240213; extra == 'dev'
|
|
30
|
+
Requires-Dist: types-tqdm==4.66.0.20240106; extra == 'dev'
|
|
31
|
+
Provides-Extra: generation
|
|
32
|
+
Requires-Dist: accelerate>=0.29.2; extra == 'generation'
|
|
33
|
+
Requires-Dist: diffusers>=0.26.3; extra == 'generation'
|
|
34
|
+
Requires-Dist: torch>=2.1.0; extra == 'generation'
|
|
35
|
+
Requires-Dist: transformers>=4.38.1; extra == 'generation'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# About
|
|
39
|
+
|
|
40
|
+
Minimal video generation and processing library.
|
|
41
|
+
|
|
42
|
+
## Setup
|
|
43
|
+
|
|
44
|
+
### Install ffmpeg
|
|
45
|
+
```bash
|
|
46
|
+
# Install with brew for MacOS:
|
|
47
|
+
brew install ffmpeg
|
|
48
|
+
# Install with apt-get for Ubuntu:
|
|
49
|
+
sudo apt-get install ffmpeg
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Install with pip
|
|
53
|
+
```bash
|
|
54
|
+
pip install videopython[generation]
|
|
55
|
+
```
|
|
56
|
+
> You can install without `[generation]` dependencies for basic video handling and processing.
|
|
57
|
+
> The funcionalities found in `videopython.generation` won't work.
|
|
58
|
+
|
|
59
|
+
## Basic Usage
|
|
60
|
+
|
|
61
|
+
### Video handling
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from videopython.base.video import Video
|
|
65
|
+
|
|
66
|
+
# Load videos and print metadata
|
|
67
|
+
video1 = Video.from_path("tests/test_data/fast_benchmark.mp4")
|
|
68
|
+
print(video1)
|
|
69
|
+
|
|
70
|
+
video2 = Video.from_path("tests/test_data/slow_benchmark.mp4")
|
|
71
|
+
print(video2)
|
|
72
|
+
|
|
73
|
+
# Define the transformations
|
|
74
|
+
from videopython.base.transforms import CutSeconds, ResampleFPS, Resize, TransformationPipeline
|
|
75
|
+
|
|
76
|
+
pipeline = TransformationPipeline(
|
|
77
|
+
[CutSeconds(start=1.5, end=6.5), ResampleFPS(fps=30), Resize(width=1000, height=1000)]
|
|
78
|
+
)
|
|
79
|
+
video1 = pipeline.run(video1)
|
|
80
|
+
video2 = pipeline.run(video2)
|
|
81
|
+
|
|
82
|
+
# Combine videos, add audio and save
|
|
83
|
+
from videopython.base.transitions import FadeTransition
|
|
84
|
+
|
|
85
|
+
fade = FadeTransition(effect_time_seconds=3.0)
|
|
86
|
+
video = fade.apply(videos=(video1, video2))
|
|
87
|
+
video.add_audio_from_file("tests/test_data/test_audio.mp3")
|
|
88
|
+
|
|
89
|
+
savepath = video.save()
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Video Generation
|
|
93
|
+
|
|
94
|
+
> Using Nvidia A40 or better is recommended for the `videopython.generation` module.
|
|
95
|
+
```python
|
|
96
|
+
# Generate image and animate it
|
|
97
|
+
from videopython.generation import ImageToVideo
|
|
98
|
+
from videopython.generation import TextToImage
|
|
99
|
+
from videopython.generation import TextToMusic
|
|
100
|
+
|
|
101
|
+
image = TextToImage().generate_image(prompt="Golden Retriever playing in the park")
|
|
102
|
+
video = ImageToVideo().generate_video(image=image, fps=24)
|
|
103
|
+
|
|
104
|
+
# Video generation directly from prompt
|
|
105
|
+
from videopython.generation import TextToVideo
|
|
106
|
+
video_gen = TextToVideo()
|
|
107
|
+
video = video_gen.generate_video("Dogs playing in the snow")
|
|
108
|
+
for _ in range(10):
|
|
109
|
+
video += video_gen.generate_video("Dogs playing in the snow")
|
|
110
|
+
|
|
111
|
+
# Cut the first 2 seconds
|
|
112
|
+
from videopython.base.transforms import CutSeconds
|
|
113
|
+
transformed_video = CutSeconds(start_second=0, end_second=2).apply(video.copy())
|
|
114
|
+
|
|
115
|
+
# Upsample to 30 FPS
|
|
116
|
+
from videopython.base.transforms import ResampleFPS
|
|
117
|
+
transformed_video = ResampleFPS(new_fps=30).apply(transformed_video)
|
|
118
|
+
|
|
119
|
+
# Resize to 1000x1000
|
|
120
|
+
from videopython.base.transforms import Resize
|
|
121
|
+
transformed_video = Resize(width=1000, height=1000).apply(transformed_video)
|
|
122
|
+
|
|
123
|
+
# Add generated music
|
|
124
|
+
# MusicGen cannot generate more than 1503 tokens (~30seconds of audio)
|
|
125
|
+
text_to_music = TextToMusic()
|
|
126
|
+
audio = text_to_music.generate_audio("Happy dogs playing together in a park", max_new_tokens=256)
|
|
127
|
+
transformed_video.add_audio(audio=audio)
|
|
128
|
+
|
|
129
|
+
filepath = transformed_video.save()
|
|
130
|
+
```
|
|
@@ -20,12 +20,46 @@ pip install videopython[generation]
|
|
|
20
20
|
> The funcionalities found in `videopython.generation` won't work.
|
|
21
21
|
|
|
22
22
|
## Basic Usage
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
### Video handling
|
|
24
25
|
|
|
26
|
+
```python
|
|
27
|
+
from videopython.base.video import Video
|
|
28
|
+
|
|
29
|
+
# Load videos and print metadata
|
|
30
|
+
video1 = Video.from_path("tests/test_data/fast_benchmark.mp4")
|
|
31
|
+
print(video1)
|
|
32
|
+
|
|
33
|
+
video2 = Video.from_path("tests/test_data/slow_benchmark.mp4")
|
|
34
|
+
print(video2)
|
|
35
|
+
|
|
36
|
+
# Define the transformations
|
|
37
|
+
from videopython.base.transforms import CutSeconds, ResampleFPS, Resize, TransformationPipeline
|
|
38
|
+
|
|
39
|
+
pipeline = TransformationPipeline(
|
|
40
|
+
[CutSeconds(start=1.5, end=6.5), ResampleFPS(fps=30), Resize(width=1000, height=1000)]
|
|
41
|
+
)
|
|
42
|
+
video1 = pipeline.run(video1)
|
|
43
|
+
video2 = pipeline.run(video2)
|
|
44
|
+
|
|
45
|
+
# Combine videos, add audio and save
|
|
46
|
+
from videopython.base.transitions import FadeTransition
|
|
47
|
+
|
|
48
|
+
fade = FadeTransition(effect_time_seconds=3.0)
|
|
49
|
+
video = fade.apply(videos=(video1, video2))
|
|
50
|
+
video.add_audio_from_file("tests/test_data/test_audio.mp3")
|
|
51
|
+
|
|
52
|
+
savepath = video.save()
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Video Generation
|
|
56
|
+
|
|
57
|
+
> Using Nvidia A40 or better is recommended for the `videopython.generation` module.
|
|
25
58
|
```python
|
|
26
59
|
# Generate image and animate it
|
|
27
60
|
from videopython.generation import ImageToVideo
|
|
28
61
|
from videopython.generation import TextToImage
|
|
62
|
+
from videopython.generation import TextToMusic
|
|
29
63
|
|
|
30
64
|
image = TextToImage().generate_image(prompt="Golden Retriever playing in the park")
|
|
31
65
|
video = ImageToVideo().generate_video(image=image, fps=24)
|
|
@@ -49,5 +83,11 @@ transformed_video = ResampleFPS(new_fps=30).apply(transformed_video)
|
|
|
49
83
|
from videopython.base.transforms import Resize
|
|
50
84
|
transformed_video = Resize(width=1000, height=1000).apply(transformed_video)
|
|
51
85
|
|
|
86
|
+
# Add generated music
|
|
87
|
+
# MusicGen cannot generate more than 1503 tokens (~30seconds of audio)
|
|
88
|
+
text_to_music = TextToMusic()
|
|
89
|
+
audio = text_to_music.generate_audio("Happy dogs playing together in a park", max_new_tokens=256)
|
|
90
|
+
transformed_video.add_audio(audio=audio)
|
|
91
|
+
|
|
52
92
|
filepath = transformed_video.save()
|
|
53
93
|
```
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "videopython"
|
|
3
|
+
version = "0.2.1"
|
|
4
|
+
description = "Minimal video generation and processing library."
|
|
5
|
+
authors = [
|
|
6
|
+
{ name = "Bartosz Wójtowicz", email = "bartoszwojtowicz@outlook.com" },
|
|
7
|
+
{ name = "Bartosz Rudnikowicz", email = "bartoszrudnikowicz840@gmail.com" },
|
|
8
|
+
{ name = "Piotr Pukisz", email = "piotr.pukisz@gmail.com" }
|
|
9
|
+
]
|
|
10
|
+
license = { text = "Apache-2.0" }
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
requires-python = ">=3.10, <3.13"
|
|
13
|
+
keywords = ["python", "videopython", "video", "movie", "opencv", "generation", "editing"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"License :: OSI Approved :: Apache Software License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.10",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
dependencies = [
|
|
23
|
+
"numpy>=1.25.2",
|
|
24
|
+
"opencv-python>=4.9.0.80",
|
|
25
|
+
"pillow>=10.3.0",
|
|
26
|
+
"pydub>=0.25.1",
|
|
27
|
+
"tqdm>=4.66.3",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
dev = [
|
|
32
|
+
"black==24.3.0",
|
|
33
|
+
"isort==5.12.0",
|
|
34
|
+
"mypy==1.8.0",
|
|
35
|
+
"pytest==7.4.0",
|
|
36
|
+
"types-Pillow==10.2.0.20240213",
|
|
37
|
+
"types-tqdm==4.66.0.20240106",
|
|
38
|
+
"pydub-stubs==0.25.1.1",
|
|
39
|
+
]
|
|
40
|
+
generation = [
|
|
41
|
+
"accelerate>=0.29.2",
|
|
42
|
+
"diffusers>=0.26.3",
|
|
43
|
+
"torch>=2.1.0",
|
|
44
|
+
"transformers>=4.38.1",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[project.urls]
|
|
48
|
+
Homepage = "https://github.com/bartwojtowicz/videopython/"
|
|
49
|
+
Repository = "https://github.com/bartwojtowicz/videopython/"
|
|
50
|
+
Documentation = "https://github.com/bartwojtowicz/videopython/"
|
|
51
|
+
|
|
52
|
+
[tool.rye]
|
|
53
|
+
managed = true
|
|
54
|
+
dev-dependencies = [
|
|
55
|
+
"black==24.3.0",
|
|
56
|
+
"isort==5.12.0",
|
|
57
|
+
"mypy==1.8.0",
|
|
58
|
+
"pytest==7.4.0",
|
|
59
|
+
"types-Pillow==10.2.0.20240213",
|
|
60
|
+
"types-tqdm==4.66.0.20240106",
|
|
61
|
+
"pydub-stubs==0.25.1.1",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[tool.rye.scripts]
|
|
65
|
+
test-unit = "pytest"
|
|
66
|
+
test-type = "mypy src"
|
|
67
|
+
test-static = { chain = [
|
|
68
|
+
"black src -l 120 --check",
|
|
69
|
+
"isort src --profile black --check"
|
|
70
|
+
]}
|
|
71
|
+
|
|
72
|
+
[build-system]
|
|
73
|
+
requires = ["hatchling"]
|
|
74
|
+
build-backend = "hatchling.build"
|
|
75
|
+
|
|
76
|
+
[tool.hatch.build.targets.wheel]
|
|
77
|
+
packages = ["src/videopython"]
|
|
78
|
+
|
|
79
|
+
[tool.hatch.build.targets.sdist]
|
|
80
|
+
include = ["src/videopython", "src/videopython/py.typed"]
|
|
81
|
+
|
|
82
|
+
[tool.mypy]
|
|
83
|
+
mypy_path = "stubs"
|
|
84
|
+
|
|
85
|
+
[tool.pytest]
|
|
86
|
+
testpaths = ["src/tests"]
|
|
87
|
+
python_files = ["test_*.py"]
|
|
88
|
+
addopts = "-v --tb=short"
|
|
@@ -53,22 +53,22 @@ class TransformationPipeline:
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
class CutFrames(Transformation):
|
|
56
|
-
def __init__(self,
|
|
57
|
-
self.
|
|
58
|
-
self.
|
|
56
|
+
def __init__(self, start: int, end: int):
|
|
57
|
+
self.start = start
|
|
58
|
+
self.end = end
|
|
59
59
|
|
|
60
60
|
def apply(self, video: Video) -> Video:
|
|
61
|
-
video = video[self.
|
|
61
|
+
video = video[self.start : self.end]
|
|
62
62
|
return video
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
class CutSeconds(Transformation):
|
|
66
|
-
def __init__(self,
|
|
67
|
-
self.
|
|
68
|
-
self.
|
|
66
|
+
def __init__(self, start: float | int, end: float | int):
|
|
67
|
+
self.start = start
|
|
68
|
+
self.end = end
|
|
69
69
|
|
|
70
70
|
def apply(self, video: Video) -> Video:
|
|
71
|
-
video = video[round(self.
|
|
71
|
+
video = video[round(self.start * video.fps) : round(self.end * video.fps)]
|
|
72
72
|
return video
|
|
73
73
|
|
|
74
74
|
|
|
@@ -112,18 +112,18 @@ class Resize(Transformation):
|
|
|
112
112
|
|
|
113
113
|
|
|
114
114
|
class ResampleFPS(Transformation):
|
|
115
|
-
def __init__(self,
|
|
116
|
-
self.
|
|
115
|
+
def __init__(self, fps: int | float):
|
|
116
|
+
self.fps = float(fps)
|
|
117
117
|
|
|
118
118
|
def _downsample(self, video: Video) -> Video:
|
|
119
|
-
target_frame_count = int(len(video.frames) * (self.
|
|
119
|
+
target_frame_count = int(len(video.frames) * (self.fps / video.fps))
|
|
120
120
|
new_frame_indices = np.round(np.linspace(0, len(video.frames) - 1, target_frame_count)).astype(int)
|
|
121
121
|
video.frames = video.frames[new_frame_indices]
|
|
122
|
-
video.fps = self.
|
|
122
|
+
video.fps = self.fps
|
|
123
123
|
return video
|
|
124
124
|
|
|
125
125
|
def _upsample(self, video: Video) -> Video:
|
|
126
|
-
target_frame_count = int(len(video.frames) * (self.
|
|
126
|
+
target_frame_count = int(len(video.frames) * (self.fps / video.fps))
|
|
127
127
|
new_frame_indices = np.linspace(0, len(video.frames) - 1, target_frame_count)
|
|
128
128
|
new_frames = []
|
|
129
129
|
for i in tqdm(range(len(new_frame_indices) - 1)):
|
|
@@ -134,17 +134,17 @@ class ResampleFPS(Transformation):
|
|
|
134
134
|
]
|
|
135
135
|
new_frames.append(new_frame.astype(np.uint8))
|
|
136
136
|
video.frames = np.array(new_frames, dtype=np.uint8)
|
|
137
|
-
video.fps = self.
|
|
137
|
+
video.fps = self.fps
|
|
138
138
|
return video
|
|
139
139
|
|
|
140
140
|
def apply(self, video: Video) -> Video:
|
|
141
|
-
if video.fps == self.
|
|
141
|
+
if video.fps == self.fps:
|
|
142
142
|
return video
|
|
143
|
-
elif video.fps > self.
|
|
144
|
-
print(f"Downsampling video from {video.fps} to {self.
|
|
143
|
+
elif video.fps > self.fps:
|
|
144
|
+
print(f"Downsampling video from {video.fps} to {self.fps} FPS.")
|
|
145
145
|
video = self._downsample(video)
|
|
146
146
|
else:
|
|
147
|
-
print(f"Upsampling video from {video.fps} to {self.
|
|
147
|
+
print(f"Upsampling video from {video.fps} to {self.fps} FPS.")
|
|
148
148
|
video = self._upsample(video)
|
|
149
149
|
return video
|
|
150
150
|
|