parallel-matplotlib-animation 0.1.1__tar.gz → 0.1.2__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.
- parallel_matplotlib_animation-0.1.2/.claude/settings.local.json +12 -0
- parallel_matplotlib_animation-0.1.2/.gitignore +308 -0
- parallel_matplotlib_animation-0.1.1/README.md → parallel_matplotlib_animation-0.1.2/PKG-INFO +36 -5
- parallel_matplotlib_animation-0.1.1/PKG-INFO → parallel_matplotlib_animation-0.1.2/README.md +22 -28
- parallel_matplotlib_animation-0.1.2/assets/multi_panel_animation.gif +0 -0
- parallel_matplotlib_animation-0.1.2/assets/nondeterministic_video_loader.gif +0 -0
- parallel_matplotlib_animation-0.1.2/assets/scaling_graph.png +0 -0
- parallel_matplotlib_animation-0.1.2/assets/simple_wave_animation.gif +0 -0
- parallel_matplotlib_animation-0.1.2/assets/very_complex_animation.gif +0 -0
- parallel_matplotlib_animation-0.1.2/dev/mp4_to_gif.sh +31 -0
- {parallel_matplotlib_animation-0.1.1 → parallel_matplotlib_animation-0.1.2}/pyproject.toml +8 -8
- parallel_matplotlib_animation-0.1.2/src/parallel_animate/__init__.py +1 -0
- {parallel_matplotlib_animation-0.1.1 → parallel_matplotlib_animation-0.1.2}/src/parallel_animate/animator.py +150 -96
- parallel_matplotlib_animation-0.1.2/src/parallel_animate/examples/nondeterministic_video_loader.py +46 -0
- parallel_matplotlib_animation-0.1.2/src/parallel_animate/util.py +64 -0
- parallel_matplotlib_animation-0.1.2/tests/__init__.py +0 -0
- parallel_matplotlib_animation-0.1.2/tests/test_animator.py +521 -0
- parallel_matplotlib_animation-0.1.2/tests/test_examples.py +47 -0
- parallel_matplotlib_animation-0.1.2/tests/test_util.py +56 -0
- parallel_matplotlib_animation-0.1.2/uv.lock +785 -0
- parallel_matplotlib_animation-0.1.1/src/parallel_animate/__init__.py +0 -1
- {parallel_matplotlib_animation-0.1.1 → parallel_matplotlib_animation-0.1.2}/LICENSE +0 -0
- {parallel_matplotlib_animation-0.1.1 → parallel_matplotlib_animation-0.1.2}/src/parallel_animate/examples/__init__.py +0 -0
- {parallel_matplotlib_animation-0.1.1 → parallel_matplotlib_animation-0.1.2}/src/parallel_animate/examples/multi_panel_animation.py +0 -0
- {parallel_matplotlib_animation-0.1.1 → parallel_matplotlib_animation-0.1.2}/src/parallel_animate/examples/scaling_test.py +0 -0
- {parallel_matplotlib_animation-0.1.1 → parallel_matplotlib_animation-0.1.2}/src/parallel_animate/examples/simple_wave_animation.py +0 -0
- {parallel_matplotlib_animation-0.1.1 → parallel_matplotlib_animation-0.1.2}/src/parallel_animate/examples/very_complex_animation.py +0 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
example_output/
|
|
2
|
+
|
|
3
|
+
######################################################################
|
|
4
|
+
|
|
5
|
+
# Created by https://www.toptal.com/developers/gitignore/api/python,linux,macos,windows,git,vim,visualstudiocode
|
|
6
|
+
# Edit at https://www.toptal.com/developers/gitignore?templates=python,linux,macos,windows,git,vim,visualstudiocode
|
|
7
|
+
|
|
8
|
+
### Git ###
|
|
9
|
+
# Created by git for backups. To disable backups in Git:
|
|
10
|
+
# $ git config --global mergetool.keepBackup false
|
|
11
|
+
*.orig
|
|
12
|
+
|
|
13
|
+
# Created by git when using merge tools for conflicts
|
|
14
|
+
*.BACKUP.*
|
|
15
|
+
*.BASE.*
|
|
16
|
+
*.LOCAL.*
|
|
17
|
+
*.REMOTE.*
|
|
18
|
+
*_BACKUP_*.txt
|
|
19
|
+
*_BASE_*.txt
|
|
20
|
+
*_LOCAL_*.txt
|
|
21
|
+
*_REMOTE_*.txt
|
|
22
|
+
|
|
23
|
+
### Linux ###
|
|
24
|
+
*~
|
|
25
|
+
|
|
26
|
+
# temporary files which can be created if a process still has a handle open of a deleted file
|
|
27
|
+
.fuse_hidden*
|
|
28
|
+
|
|
29
|
+
# KDE directory preferences
|
|
30
|
+
.directory
|
|
31
|
+
|
|
32
|
+
# Linux trash folder which might appear on any partition or disk
|
|
33
|
+
.Trash-*
|
|
34
|
+
|
|
35
|
+
# .nfs files are created when an open file is removed but is still being accessed
|
|
36
|
+
.nfs*
|
|
37
|
+
|
|
38
|
+
### macOS ###
|
|
39
|
+
# General
|
|
40
|
+
.DS_Store
|
|
41
|
+
.AppleDouble
|
|
42
|
+
.LSOverride
|
|
43
|
+
|
|
44
|
+
# Icon must end with two \r
|
|
45
|
+
Icon
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# Thumbnails
|
|
49
|
+
._*
|
|
50
|
+
|
|
51
|
+
# Files that might appear in the root of a volume
|
|
52
|
+
.DocumentRevisions-V100
|
|
53
|
+
.fseventsd
|
|
54
|
+
.Spotlight-V100
|
|
55
|
+
.TemporaryItems
|
|
56
|
+
.Trashes
|
|
57
|
+
.VolumeIcon.icns
|
|
58
|
+
.com.apple.timemachine.donotpresent
|
|
59
|
+
|
|
60
|
+
# Directories potentially created on remote AFP share
|
|
61
|
+
.AppleDB
|
|
62
|
+
.AppleDesktop
|
|
63
|
+
Network Trash Folder
|
|
64
|
+
Temporary Items
|
|
65
|
+
.apdisk
|
|
66
|
+
|
|
67
|
+
### macOS Patch ###
|
|
68
|
+
# iCloud generated files
|
|
69
|
+
*.icloud
|
|
70
|
+
|
|
71
|
+
### Python ###
|
|
72
|
+
# Byte-compiled / optimized / DLL files
|
|
73
|
+
__pycache__/
|
|
74
|
+
*.py[cod]
|
|
75
|
+
*$py.class
|
|
76
|
+
|
|
77
|
+
# C extensions
|
|
78
|
+
*.so
|
|
79
|
+
|
|
80
|
+
# Distribution / packaging
|
|
81
|
+
.Python
|
|
82
|
+
build/
|
|
83
|
+
develop-eggs/
|
|
84
|
+
dist/
|
|
85
|
+
downloads/
|
|
86
|
+
eggs/
|
|
87
|
+
.eggs/
|
|
88
|
+
lib/
|
|
89
|
+
lib64/
|
|
90
|
+
parts/
|
|
91
|
+
sdist/
|
|
92
|
+
var/
|
|
93
|
+
wheels/
|
|
94
|
+
share/python-wheels/
|
|
95
|
+
*.egg-info/
|
|
96
|
+
.installed.cfg
|
|
97
|
+
*.egg
|
|
98
|
+
MANIFEST
|
|
99
|
+
|
|
100
|
+
# PyInstaller
|
|
101
|
+
# Usually these files are written by a python script from a template
|
|
102
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
103
|
+
*.manifest
|
|
104
|
+
*.spec
|
|
105
|
+
|
|
106
|
+
# Installer logs
|
|
107
|
+
pip-log.txt
|
|
108
|
+
pip-delete-this-directory.txt
|
|
109
|
+
|
|
110
|
+
# Unit test / coverage reports
|
|
111
|
+
htmlcov/
|
|
112
|
+
.tox/
|
|
113
|
+
.nox/
|
|
114
|
+
.coverage
|
|
115
|
+
.coverage.*
|
|
116
|
+
.cache
|
|
117
|
+
nosetests.xml
|
|
118
|
+
coverage.xml
|
|
119
|
+
*.cover
|
|
120
|
+
*.py,cover
|
|
121
|
+
.hypothesis/
|
|
122
|
+
.pytest_cache/
|
|
123
|
+
cover/
|
|
124
|
+
|
|
125
|
+
# Translations
|
|
126
|
+
*.mo
|
|
127
|
+
*.pot
|
|
128
|
+
|
|
129
|
+
# Django stuff:
|
|
130
|
+
*.log
|
|
131
|
+
local_settings.py
|
|
132
|
+
db.sqlite3
|
|
133
|
+
db.sqlite3-journal
|
|
134
|
+
|
|
135
|
+
# Flask stuff:
|
|
136
|
+
instance/
|
|
137
|
+
.webassets-cache
|
|
138
|
+
|
|
139
|
+
# Scrapy stuff:
|
|
140
|
+
.scrapy
|
|
141
|
+
|
|
142
|
+
# Sphinx documentation
|
|
143
|
+
docs/_build/
|
|
144
|
+
|
|
145
|
+
# PyBuilder
|
|
146
|
+
.pybuilder/
|
|
147
|
+
target/
|
|
148
|
+
|
|
149
|
+
# Jupyter Notebook
|
|
150
|
+
.ipynb_checkpoints
|
|
151
|
+
|
|
152
|
+
# IPython
|
|
153
|
+
profile_default/
|
|
154
|
+
ipython_config.py
|
|
155
|
+
|
|
156
|
+
# pyenv
|
|
157
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
158
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
159
|
+
# .python-version
|
|
160
|
+
|
|
161
|
+
# pipenv
|
|
162
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
163
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
164
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
165
|
+
# install all needed dependencies.
|
|
166
|
+
#Pipfile.lock
|
|
167
|
+
|
|
168
|
+
# poetry
|
|
169
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
170
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
171
|
+
# commonly ignored for libraries.
|
|
172
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
173
|
+
#poetry.lock
|
|
174
|
+
|
|
175
|
+
# pdm
|
|
176
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
177
|
+
#pdm.lock
|
|
178
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
179
|
+
# in version control.
|
|
180
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
181
|
+
.pdm.toml
|
|
182
|
+
|
|
183
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
184
|
+
__pypackages__/
|
|
185
|
+
|
|
186
|
+
# Celery stuff
|
|
187
|
+
celerybeat-schedule
|
|
188
|
+
celerybeat.pid
|
|
189
|
+
|
|
190
|
+
# SageMath parsed files
|
|
191
|
+
*.sage.py
|
|
192
|
+
|
|
193
|
+
# Environments
|
|
194
|
+
.env
|
|
195
|
+
.venv
|
|
196
|
+
env/
|
|
197
|
+
venv/
|
|
198
|
+
ENV/
|
|
199
|
+
env.bak/
|
|
200
|
+
venv.bak/
|
|
201
|
+
|
|
202
|
+
# Spyder project settings
|
|
203
|
+
.spyderproject
|
|
204
|
+
.spyproject
|
|
205
|
+
|
|
206
|
+
# Rope project settings
|
|
207
|
+
.ropeproject
|
|
208
|
+
|
|
209
|
+
# mkdocs documentation
|
|
210
|
+
/site
|
|
211
|
+
|
|
212
|
+
# mypy
|
|
213
|
+
.mypy_cache/
|
|
214
|
+
.dmypy.json
|
|
215
|
+
dmypy.json
|
|
216
|
+
|
|
217
|
+
# Pyre type checker
|
|
218
|
+
.pyre/
|
|
219
|
+
|
|
220
|
+
# pytype static type analyzer
|
|
221
|
+
.pytype/
|
|
222
|
+
|
|
223
|
+
# Cython debug symbols
|
|
224
|
+
cython_debug/
|
|
225
|
+
|
|
226
|
+
# PyCharm
|
|
227
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
228
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
229
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
230
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
231
|
+
#.idea/
|
|
232
|
+
|
|
233
|
+
### Python Patch ###
|
|
234
|
+
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
|
235
|
+
poetry.toml
|
|
236
|
+
|
|
237
|
+
# ruff
|
|
238
|
+
.ruff_cache/
|
|
239
|
+
|
|
240
|
+
# LSP config files
|
|
241
|
+
pyrightconfig.json
|
|
242
|
+
|
|
243
|
+
### Vim ###
|
|
244
|
+
# Swap
|
|
245
|
+
[._]*.s[a-v][a-z]
|
|
246
|
+
!*.svg # comment out if you don't need vector files
|
|
247
|
+
[._]*.sw[a-p]
|
|
248
|
+
[._]s[a-rt-v][a-z]
|
|
249
|
+
[._]ss[a-gi-z]
|
|
250
|
+
[._]sw[a-p]
|
|
251
|
+
|
|
252
|
+
# Session
|
|
253
|
+
Session.vim
|
|
254
|
+
Sessionx.vim
|
|
255
|
+
|
|
256
|
+
# Temporary
|
|
257
|
+
.netrwhist
|
|
258
|
+
# Auto-generated tag files
|
|
259
|
+
tags
|
|
260
|
+
# Persistent undo
|
|
261
|
+
[._]*.un~
|
|
262
|
+
|
|
263
|
+
### VisualStudioCode ###
|
|
264
|
+
.vscode/*
|
|
265
|
+
!.vscode/settings.json
|
|
266
|
+
!.vscode/tasks.json
|
|
267
|
+
!.vscode/launch.json
|
|
268
|
+
!.vscode/extensions.json
|
|
269
|
+
!.vscode/*.code-snippets
|
|
270
|
+
|
|
271
|
+
# Local History for Visual Studio Code
|
|
272
|
+
.history/
|
|
273
|
+
|
|
274
|
+
# Built Visual Studio Code Extensions
|
|
275
|
+
*.vsix
|
|
276
|
+
|
|
277
|
+
### VisualStudioCode Patch ###
|
|
278
|
+
# Ignore all local history of files
|
|
279
|
+
.history
|
|
280
|
+
.ionide
|
|
281
|
+
|
|
282
|
+
### Windows ###
|
|
283
|
+
# Windows thumbnail cache files
|
|
284
|
+
Thumbs.db
|
|
285
|
+
Thumbs.db:encryptable
|
|
286
|
+
ehthumbs.db
|
|
287
|
+
ehthumbs_vista.db
|
|
288
|
+
|
|
289
|
+
# Dump file
|
|
290
|
+
*.stackdump
|
|
291
|
+
|
|
292
|
+
# Folder config file
|
|
293
|
+
[Dd]esktop.ini
|
|
294
|
+
|
|
295
|
+
# Recycle Bin used on file shares
|
|
296
|
+
$RECYCLE.BIN/
|
|
297
|
+
|
|
298
|
+
# Windows Installer files
|
|
299
|
+
*.cab
|
|
300
|
+
*.msi
|
|
301
|
+
*.msix
|
|
302
|
+
*.msm
|
|
303
|
+
*.msp
|
|
304
|
+
|
|
305
|
+
# Windows shortcuts
|
|
306
|
+
*.lnk
|
|
307
|
+
|
|
308
|
+
# End of https://www.toptal.com/developers/gitignore/api/python,linux,macos,windows,git,vim,visualstudiocode
|
parallel_matplotlib_animation-0.1.1/README.md → parallel_matplotlib_animation-0.1.2/PKG-INFO
RENAMED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: parallel-matplotlib-animation
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Animate matplotlib figures into videos, in parallel but with efficient caching
|
|
5
|
+
Project-URL: Repository, https://github.com/sibocw/parallel-matplotlib-animation
|
|
6
|
+
Author-email: Sibo Wang-Chen <sibo.wang@epfl.ch>
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: av>=14.0
|
|
10
|
+
Requires-Dist: matplotlib>=3.10.0
|
|
11
|
+
Requires-Dist: numpy<3,>=2
|
|
12
|
+
Requires-Dist: pillow>=11.0
|
|
13
|
+
Requires-Dist: tqdm>=4.67
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
1
16
|
# parallel-matplotlib-animation
|
|
2
17
|
|
|
3
18
|
Create matplotlib animations rendered to video in parallel, with efficient resources reuse.
|
|
@@ -63,7 +78,7 @@ anim = WaveAnimation()
|
|
|
63
78
|
anim.make_video("wave.mp4", param_by_frame=params, fps=30, num_workers=4)
|
|
64
79
|
```
|
|
65
80
|
|
|
66
|
-
|
|
81
|
+
<img src="assets/simple_wave_animation.gif" width="480"/>
|
|
67
82
|
|
|
68
83
|
## Usage
|
|
69
84
|
This library has a single class: `parallel_animate.Animator`. To make an animation, you must create your own class inheriting from it and define the following methods:
|
|
@@ -75,18 +90,34 @@ This library has a single class: `parallel_animate.Animator`. To make an animati
|
|
|
75
90
|
Once you have defined your animator class, there is a single method that you need to call that makes the video: **`.make_video(...)`**. It accepts the following arguments:
|
|
76
91
|
|
|
77
92
|
- `output_file` (Path or str): Output video path
|
|
78
|
-
- `param_by_frame` (
|
|
93
|
+
- `param_by_frame` (Iterable): Iterable of parameters. Each element is the `params` argument to be given to the `.update` call for the corresponding frame. Can be a list, tuple, generator, or any other iterable. Using generators is particularly useful for large data (e.g., bitmaps) to avoid loading everything into memory at once.
|
|
79
94
|
- `fps` (int): Frame rate of the output video
|
|
95
|
+
- `n_frames` (int or None): Number of frames to render. If None, use the length of `param_by_frame`. If param_by_frame does not have `__len__` implemented and `n_frames` is None, the progress bar won't show completion percentage.
|
|
80
96
|
- `num_workers` (int): Number of worker processes to be spawned. If -1, use all CPU cores. If -2, use all but one CPU cores, etc. If 1, no child process is created and the video is made in the main process itself. Default is -1.
|
|
81
97
|
- See the docstring for `parallel_animate.animator` directly for less commonly used, optional parameters. These control logging, rendering quality, etc.
|
|
82
98
|
|
|
99
|
+
### Special case: frame params arriving out-of-order in `param_by_frame`
|
|
100
|
+
In some cases, frames in `param_by_frame` might be out of order. We can handle these scenarios by populating `param_by_frame` with a special `parallel_animate.IndexedFrameParams` dataclass, which specifies the frame index that overrides the ordering in `param_by_frame`. This can be useful when, for example, the animator needs to draw frames that are decoded from a video, and the dataloader for that video might return frames in nondeterministic order because it's parallelized.
|
|
101
|
+
|
|
102
|
+
See `src/parallel_animate/examples/nondeterministic_video_loader.py` for details.
|
|
83
103
|
|
|
84
104
|
## Examples
|
|
85
105
|
|
|
86
106
|
See [`src/parallel_animate/examples/`](https://github.com/sibocw/parallel-matplotlib-animation/blob/main/src/parallel_animate/examples/):
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
107
|
+
|
|
108
|
+
`simple_wave_animation.py`: The example above
|
|
109
|
+
|
|
110
|
+
`multi_panel_animation.py`: 5 subplots with different plot types
|
|
111
|
+
|
|
112
|
+
<img src="assets/multi_panel_animation.gif" width="480"/>
|
|
113
|
+
|
|
114
|
+
`very_complex_animation.py`: 14 subplots with GridSpec layout
|
|
115
|
+
|
|
116
|
+
<img src="assets/very_complex_animation.gif" width="480"/>
|
|
117
|
+
|
|
118
|
+
`nondeterministic_video_loader.py`: handling frames that arrive out of order
|
|
119
|
+
|
|
120
|
+
<img src="assets/nondeterministic_video_loader.gif" width="240"/>
|
|
90
121
|
|
|
91
122
|
|
|
92
123
|
## Performance test
|
parallel_matplotlib_animation-0.1.1/PKG-INFO → parallel_matplotlib_animation-0.1.2/README.md
RENAMED
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: parallel-matplotlib-animation
|
|
3
|
-
Version: 0.1.1
|
|
4
|
-
Summary: Animate matplotlib figures into videos, in parallel but with efficient caching
|
|
5
|
-
License-File: LICENSE
|
|
6
|
-
Author: Sibo Wang-Chen
|
|
7
|
-
Author-email: sibo.wang@epfl.ch
|
|
8
|
-
Requires-Python: >=3.10
|
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.14
|
|
15
|
-
Requires-Dist: Pillow (>=11.0)
|
|
16
|
-
Requires-Dist: av (>=14.0)
|
|
17
|
-
Requires-Dist: matplotlib (>=3.10.0)
|
|
18
|
-
Requires-Dist: numpy (>=2,<3)
|
|
19
|
-
Requires-Dist: tqdm (>=4.67)
|
|
20
|
-
Project-URL: Repository, https://github.com/sibocw/parallel-matplotlib-animation
|
|
21
|
-
Description-Content-Type: text/markdown
|
|
22
|
-
|
|
23
1
|
# parallel-matplotlib-animation
|
|
24
2
|
|
|
25
3
|
Create matplotlib animations rendered to video in parallel, with efficient resources reuse.
|
|
@@ -85,7 +63,7 @@ anim = WaveAnimation()
|
|
|
85
63
|
anim.make_video("wave.mp4", param_by_frame=params, fps=30, num_workers=4)
|
|
86
64
|
```
|
|
87
65
|
|
|
88
|
-
|
|
66
|
+
<img src="assets/simple_wave_animation.gif" width="480"/>
|
|
89
67
|
|
|
90
68
|
## Usage
|
|
91
69
|
This library has a single class: `parallel_animate.Animator`. To make an animation, you must create your own class inheriting from it and define the following methods:
|
|
@@ -97,18 +75,34 @@ This library has a single class: `parallel_animate.Animator`. To make an animati
|
|
|
97
75
|
Once you have defined your animator class, there is a single method that you need to call that makes the video: **`.make_video(...)`**. It accepts the following arguments:
|
|
98
76
|
|
|
99
77
|
- `output_file` (Path or str): Output video path
|
|
100
|
-
- `param_by_frame` (
|
|
78
|
+
- `param_by_frame` (Iterable): Iterable of parameters. Each element is the `params` argument to be given to the `.update` call for the corresponding frame. Can be a list, tuple, generator, or any other iterable. Using generators is particularly useful for large data (e.g., bitmaps) to avoid loading everything into memory at once.
|
|
101
79
|
- `fps` (int): Frame rate of the output video
|
|
80
|
+
- `n_frames` (int or None): Number of frames to render. If None, use the length of `param_by_frame`. If param_by_frame does not have `__len__` implemented and `n_frames` is None, the progress bar won't show completion percentage.
|
|
102
81
|
- `num_workers` (int): Number of worker processes to be spawned. If -1, use all CPU cores. If -2, use all but one CPU cores, etc. If 1, no child process is created and the video is made in the main process itself. Default is -1.
|
|
103
82
|
- See the docstring for `parallel_animate.animator` directly for less commonly used, optional parameters. These control logging, rendering quality, etc.
|
|
104
83
|
|
|
84
|
+
### Special case: frame params arriving out-of-order in `param_by_frame`
|
|
85
|
+
In some cases, frames in `param_by_frame` might be out of order. We can handle these scenarios by populating `param_by_frame` with a special `parallel_animate.IndexedFrameParams` dataclass, which specifies the frame index that overrides the ordering in `param_by_frame`. This can be useful when, for example, the animator needs to draw frames that are decoded from a video, and the dataloader for that video might return frames in nondeterministic order because it's parallelized.
|
|
86
|
+
|
|
87
|
+
See `src/parallel_animate/examples/nondeterministic_video_loader.py` for details.
|
|
105
88
|
|
|
106
89
|
## Examples
|
|
107
90
|
|
|
108
91
|
See [`src/parallel_animate/examples/`](https://github.com/sibocw/parallel-matplotlib-animation/blob/main/src/parallel_animate/examples/):
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
92
|
+
|
|
93
|
+
`simple_wave_animation.py`: The example above
|
|
94
|
+
|
|
95
|
+
`multi_panel_animation.py`: 5 subplots with different plot types
|
|
96
|
+
|
|
97
|
+
<img src="assets/multi_panel_animation.gif" width="480"/>
|
|
98
|
+
|
|
99
|
+
`very_complex_animation.py`: 14 subplots with GridSpec layout
|
|
100
|
+
|
|
101
|
+
<img src="assets/very_complex_animation.gif" width="480"/>
|
|
102
|
+
|
|
103
|
+
`nondeterministic_video_loader.py`: handling frames that arrive out of order
|
|
104
|
+
|
|
105
|
+
<img src="assets/nondeterministic_video_loader.gif" width="240"/>
|
|
112
106
|
|
|
113
107
|
|
|
114
108
|
## Performance test
|
|
@@ -123,4 +117,4 @@ The left-most blue dot indicates serial processing with resources reuse. The bla
|
|
|
123
117
|
## Unit tests
|
|
124
118
|
```bash
|
|
125
119
|
python -m unittest discover -s tests
|
|
126
|
-
```
|
|
120
|
+
```
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Convert an MP4 video to a GIF with specified scale and optional frame limit.
|
|
4
|
+
# Useful for creating GIFs from rendered videos for documentation.
|
|
5
|
+
# Usage:
|
|
6
|
+
# ./mp4_to_gif.sh input.mp4 output.gif scale max_frames
|
|
7
|
+
# Example:
|
|
8
|
+
# ./mp4_to_gif.sh input.mp4 output.gif 320 100 # limit to 100 frames
|
|
9
|
+
# ./mp4_to_gif.sh input.mp4 output.gif 320 0 # unlimited
|
|
10
|
+
|
|
11
|
+
input_path="$1"
|
|
12
|
+
output_path="$2"
|
|
13
|
+
scale="$3"
|
|
14
|
+
max_frames="$4"
|
|
15
|
+
|
|
16
|
+
# Check arguments
|
|
17
|
+
if [ $# -lt 3 ] || [ $# -gt 4 ]; then
|
|
18
|
+
echo "Usage: $0 <input_path> <output_path> <scale> [max_frames]"
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# Set frame limit option
|
|
23
|
+
frame_limit=""
|
|
24
|
+
if [ -n "$max_frames" ] && [ "$max_frames" -gt 0 ]; then
|
|
25
|
+
frame_limit="-vframes $max_frames"
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
ffmpeg -i "$input_path" $frame_limit \
|
|
29
|
+
-filter_complex "fps=10,scale=${scale}:-1:flags=lanczos,split[s0][s1];[s0]palettegen=stats_mode=single[p];[s1][p]paletteuse=dither=sierra2_4a" \
|
|
30
|
+
-loop 0 "$output_path"
|
|
31
|
+
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "parallel-matplotlib-animation"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "Animate matplotlib figures into videos, in parallel but with efficient caching"
|
|
5
5
|
authors = [
|
|
6
|
-
{name = "Sibo Wang-Chen",email = "sibo.wang@epfl.ch"}
|
|
6
|
+
{ name = "Sibo Wang-Chen", email = "sibo.wang@epfl.ch" }
|
|
7
7
|
]
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
requires-python = ">=3.10"
|
|
@@ -16,11 +16,11 @@ dependencies = [
|
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
[project.urls]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
[tool.poetry]
|
|
22
|
-
packages = [{include = "parallel_animate", from = "src"}]
|
|
19
|
+
Repository = "https://github.com/sibocw/parallel-matplotlib-animation"
|
|
23
20
|
|
|
24
21
|
[build-system]
|
|
25
|
-
requires = ["
|
|
26
|
-
build-backend = "
|
|
22
|
+
requires = ["hatchling"]
|
|
23
|
+
build-backend = "hatchling.build"
|
|
24
|
+
|
|
25
|
+
[tool.hatch.build.targets.wheel]
|
|
26
|
+
packages = ["src/parallel_animate"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .animator import Animator, IndexedFrameParams
|