Skip to main content

vapoursynth-imageseqs

a rust vapoursynth source plugin for reading an ordered list of images as a clip.

features

  • one frame per file, in the order supplied to Read or ReadAlpha;
  • separate color and alpha clips with depth-preserving opaque alpha fallback;
  • lots of formats support
  • native YUV output for supported WebP, HEIF/HEIC, AVIF, and JPEG 2000 files;
  • nominal 9–15-bit preservation, including 10/12-bit gray, RGB, alpha, and YUV formats;
  • EXIF and codestream orientation handling with an apply_rotation switch;
  • CICP/container color metadata and optional raw ICC export through ICCProfile;
  • background prefetching, bounded lookahead memory, variable-format clips.

requirements

for normal use:

  • VapourSynth R79 or newer;
  • Python 3.12 or newer when installing the wheel;
  • the plugin binary for the current operating system and architecture.

the wheel does not install VapourSynth or a Python helper module. Unix builds also need the dav1d and libde265 shared libraries at runtime. Windows artifacts use the static x64-windows-static-md vcpkg triplet.

for source builds:

  • Rust 1.88 or newer and Cargo;
  • CMake, Ninja, and pkg-config;
  • Debian/Ubuntu: libdav1d-dev, libde265-dev, and libwebp-dev;
  • macOS: Homebrew dav1d, libde265, and webp packages;
  • Windows: vcpkg with the x64-windows-static-md triplet.

JPEG 2000 uses bundled OpenJPEG sources, and libheif is built as part of the project. see the build section for platform commands and the legal files for native dependency obligations.

install

the python wheel is plugin-only. it installs the native library at vapoursynth/plugins/ and does not add a python module.

python -m pip install vapoursynth-imageseqs

until the package is published, download the matching *-plugin ci artifact and copy its native file into your vapoursynth plugin directory:

platform file
windows vs_imageseqs.dll
linux libvs_imageseqs.so
macos arm64 libvs_imageseqs.dylib

if vapoursynth does not find it automatically, load it explicitly:

core.std.LoadPlugin(r"path/to/vs_imageseqs.dll")

the ci artifact and wheel include LICENSE, THIRD_PARTY_NOTICES, and LICENSES/.

use

pass files in the order they should become frames:

from pathlib import Path

import vapoursynth as vs

core = vs.core
files = sorted(Path(r"I:\images").glob("*.png"))

clip = core.imgseqs.Read(
    files=[str(path) for path in files],
    fpsnum=24,
    fpsden=1,
)

Read returns the color clip. ReadAlpha returns a dictionary containing the color clip and a separate gray alpha clip:

result = core.imgseqs.ReadAlpha(files=[str(path) for path in files])
clip, alpha = result["clip"], result["alpha"]

arguments

argument default meaning
files required ordered image paths
fpsnum, fpsden 24/1 output frame rate
mismatch False allow variable sizes and pixel formats
apply_rotation True apply the file's exif/codestream orientation
icc_profile False expose embedded ICC bytes as ICCProfile
debug False log create and per-frame timings
prefetch half the logical cores, capped at 4 background decode workers; 0 disables lookahead
prefetch_memory max of 192 MiB and one largest frame per worker lookahead budget in MiB; 0 is invalid

without mismatch, every file must have the same size and format. with it, each frame keeps its own size and format.

rotation applies the displayed orientation by default. orientations 5–8 swap width and height, and ImgSeqOrientation still reports the original code. apply_rotation=False keeps the stored picture and stored size. a rotated and an upright file therefore need mismatch=True when rotation is enabled.

debug=True logs probing, decoding, allocation, pixel conversion, properties, and total frame time to the vapoursynth log.

formats

input notes
png, jpeg, bmp, gif, ico, tiff standard image decoder
dds image's DXT1, DXT3, and DXT5 decoder
farbfeld 16-bit RGBA input
webp libwebp; lossy opaque files use YUV420P8
avif dav1d; monochrome uses Gray8/Gray10/Gray12, color may use its YUV planes
heif, heic libheif/libde265; monochrome uses Gray8/Gray10/Gray12, color may use its YUV planes
jxl jpeg xl decoder
jp2, j2k, jpf, jpx, j2c JPEG 2000 through OpenJPEG; gray/RGB at 8–16 bits, and sYCC may use YUV planes
exr, hdr, pnm, qoi, tga standard image decoder

supported output includes gray and rgb at 8–16 bits, rgb 32-bit float, and the YUV formats needed by the source: YUV420P8, YUV420P10, YUV422P8, YUV422P10, YUV444P8, YUV444P10, YUV444P12, and YUV444P16.

Read ignores alpha. ReadAlpha uses the source depth for alpha: Gray8– Gray16, or GrayS. the alpha channel is the second channel of LA and the fourth channel of RGBA. files without alpha get an opaque plane filled with the format maximum (255, 1023, 4095, 65535, or 1.0).

both clips share the frame count, rate, and indexes. each file is decoded once, and mismatch applies to both clips.

nominal bit depth

when a container states 9–16 bits, the frame format names that depth and the samples are right-aligned in its word. for example, a ten-bit avif is RGB30 with samples in 0..1023, not RGB48 with sixteen-bit-scaled samples. this also applies to gray and alpha. files with no stated depth, 8-bit files, 16-bit files, and float files keep their decoder format.

yuv output

the plugin keeps a file's own YUV planes when its container names a matrix that vapoursynth understands:

  • lossy webp without alpha: YUV420P8, _Matrix=5, _Range=0;
  • color heif/heic/avif: the source planes, such as YUV420P8 or YUV444P10, tagged from the file's matrix and range.

an unspecified matrix (2) or no matrix keeps the RGB frame the plugin builds. lossless webp, webp with alpha, and other formats stay RGB or gray. use resize when a downstream filter needs RGB:

rgb = core.resize.Bicubic(clip, format=vs.RGB24)

the frame's _Matrix and _Range are used automatically. for an untagged source, provide them explicitly:

rgb = core.resize.Bicubic(
    clip,
    format=vs.RGB24,
    matrix_in_s="470bg",
    range_in_s="limited",
)

zimg cannot convert an odd-sized 4:2:0 frame directly. for a variable clip, crop odd edges before conversion and add the border back. FrameEval can do that per frame, then a final resize can declare one constant format for filters such as std.GPUUpload:

source = core.imgseqs.Read(files=files, mismatch=True)
plain_rgb = core.resize.Bicubic(source, format=vs.RGB24)

def convert(n=0, **_):
    frame = source.get_frame(n)
    right = frame.width % 2 if frame.format.subsampling_w else 0
    bottom = frame.height % 2 if frame.format.subsampling_h else 0
    if not right and not bottom:
        return plain_rgb
    even = core.std.CropAbs(
        source,
        width=frame.width - right,
        height=frame.height - bottom,
    )
    rgb = core.resize.Bicubic(even, format=vs.RGB24)
    return core.std.AddBorders(rgb, right=right, bottom=bottom)

rgb = core.std.FrameEval(source, convert)
rgb = core.resize.Bicubic(rgb, format=vs.RGB24)

Crop and AddBorders need a constant size and format, so use CropAbs for variable clips. the example restores odd edges with black pixels; stack the last real row and column instead if edge extension is preferred. a variable clip is converted frame by frame, and resize arguments are ignored for RGB frames.

frame properties

property meaning
ImgSeqPath original file path
ImgSeqIndex input-list index
ImgSeqOriginalColorType decoder's original color type
ImgSeqHasICC whether the source has an ICC profile
ICCProfile raw embedded ICC bytes when icc_profile=True
ImgSeqOrientation orientation code carried by the source
ImgSeqAlpha 1 on frames from the alpha clip

container color metadata is mapped to _Primaries, _Transfer, _Matrix, _Range, and, when explicitly named, _ChromaLocation. sources include avif and heif/heic nclx, png cICP, jxl codestream headers, and av1 sequence headers. unknown or unspecified codes are left unset. RGB frames always keep _Matrix=0 and _Range=1. ImgSeqHasICC reports an embedded profile; icc_profile=True also exposes its raw bytes as ICCProfile without changing pixels or applying a color transform.

performance

benchmark a sequence against bestsource with:

.venv/Scripts/python.exe tests/bench-imgseqs-vs-bestsource.vpy --reps 3 --dir images --pattern "i_%04d.webp"
.venv/Scripts/python.exe tests/bench-imgseqs-vs-bestsource.vpy --reps 3 --extra --prefetch 16 --dir sandbox/webp --pattern "snek - p%03d.webp"

see docs/BENCH.md for the measurements. on the same 35 pages stored in six containers, imgseqs is 2.73x faster than bestsource on jpeg (4.98x including open), 1.37x faster on png, and 1.80x slower on webp because ffmpeg slice-threads each vp8 frame while libwebp decodes one frame at a time. that bestsource build cannot open avif, heic, or jxl.

clip creation only probes container metadata; decoding happens when frames are requested, using the background pool. for a fair bestsource comparison, use cachemode=0, apply_rotation=False on both plugins, and omit fpsnum and fpsden because ffmpeg's image-sequence rate is 25 fps. opening 35 avif pages costs about 2 ms, 35 heic pages about 6 ms, and the other listed formats less in the recorded benchmark.

build

python -m pip install ".[dev]"
python -m build

linux and macos need these native packages:

# debian/ubuntu
sudo apt-get install --yes cmake ninja-build pkg-config libdav1d-dev libde265-dev libwebp-dev

# macos with homebrew
brew install cmake ninja pkg-config dav1d libde265 webp

libwebp is linked from its static archive when available, so it is not needed at runtime. JPEG 2000 uses the OpenJPEG sources bundled by openjpeg-sys. dav1d and libde265 remain shared on unix; windows uses the static vcpkg triplet.

license

vs-imageseqs is licensed under the Mozilla Public License 2.0. see LICENSE.

the plugin also contains native code under these licenses:

  • dav1d and OpenJPEG: BSD-2-Clause;
  • libwebp: BSD-3-Clause with its accompanying patent grant;
  • libheif and libde265: LGPL-3.0.

see THIRD_PARTY_NOTICES and LICENSES/ for the dependency versions, notices, and complete license texts. when a binary uses static LGPL linkage, these files are not the entire obligation: the release must also provide the applicable corresponding source and a practical way to relink the plugin with modified LGPL libraries.

Metadata

Release files for vapoursynth-imageseqs 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vapoursynth-imageseqs 0.1.0
File Size Uploaded
vapoursynth_imageseqs-0.1.0.tar.gz 250.4 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for vapoursynth-imageseqs 0.1.0
File Interpreter ABI Platform
vapoursynth_imageseqs-0.1.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
vapoursynth_imageseqs-0.1.0-py3-none-manylinux_2_43_x86_64.whl Python 3 none Linux glibc 2.43+ x86-64 Details
vapoursynth_imageseqs-0.1.0-py3-none-macosx_26_0_arm64.whl Python 3 none macOS 26.0+ ARM64 Details

Total release size: 9.8 MB

Release files / vapoursynth_imageseqs-0.1.0.tar.gz

Download URL vapoursynth_imageseqs-0.1.0.tar.gz
Size 250.4 kB
Tags Source
SHA-256 checksum
How to use checksums
06e9b43d79d85fd58a2670f7988722def70e3364d7894fa20b0af914d05070a8
BLAKE2b-256 checksum
How to use checksums
58ebc8c8683b20b946c4ef8456eeebf82b15052e3f8109b1d18e01fe94c1245a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release files / vapoursynth_imageseqs-0.1.0-py3-none-win_amd64.whl

Download URL vapoursynth_imageseqs-0.1.0-py3-none-win_amd64.whl
Size 3.6 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
ad3643c886409aafa084407eec08ef86557154f4f8378b29b75d47d056312a13
BLAKE2b-256 checksum
How to use checksums
95cd6ea40649a419d51340136afc877f77d406f454bf5b8fb9f422a376c25fa3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release files / vapoursynth_imageseqs-0.1.0-py3-none-manylinux_2_43_x86_64.whl

Download URL vapoursynth_imageseqs-0.1.0-py3-none-manylinux_2_43_x86_64.whl
Size 3.5 MB
Tags Linux glibc 2.43+ x86-64 Python 3
SHA-256 checksum
How to use checksums
6f5b43c35c751a5f878f11e3953262700f1cc57ce301f9efe93b5a0c7699a567
BLAKE2b-256 checksum
How to use checksums
d78ec5d2b69ea1427dcae5a5e4c2608c34abdaf0cbc40ac238d167058b3d5a4c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release files / vapoursynth_imageseqs-0.1.0-py3-none-macosx_26_0_arm64.whl

Download URL vapoursynth_imageseqs-0.1.0-py3-none-macosx_26_0_arm64.whl
Size 2.4 MB
Tags Python 3 macOS 26.0+ ARM64
SHA-256 checksum
How to use checksums
e877d140c2cecdb9a5922c0bae0eb76a86b3d08279a0bf9fe1a7e4ae60d46f88
BLAKE2b-256 checksum
How to use checksums
d082197824013f0d5539af6cfde6f6c81a9270f642f7f0acdb5d6e3ee37ad20d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release history Release notifications | RSS feed

0.3.0

5 release files

0.2.1

4 release files

0.2.0

4 release files

This release

0.1.0 This release

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page