Skip to main content

Manipulate Motion Vectors

A vapoursynth plugin to do potentially useful things with motion vectors that have already been generated.

Installation

The plugin is available on PyPI and can be installed with:

python -m pip install vapoursynth-manipmv

Overview

Motion vectors are useful for a variety of video processing tasks, and there is a long history of tooling related generating and using them. This historical tooling has generally coupled creating the motion vectors and consuming them into one plugin, MVTools. But it is the author's opinion that decoupled tooling with a standard interface is reasonable and desirable. There is not quite a standard interface among the various versions and ports of MVTools so for now this plugin specifically targets the conventions used by dubhater/vapoursynth-mvtools (see the assumed conventions section for details).

Functions

ExpandAnalysisData

Expands the binary MVTools_MVAnalysisData frame prop into separate frame props for convenience.

int       Analysis_MagicKey
int       Analysis_Version
int[x, y] Analysis_BlockSize
int       Analysis_Pel
int       Analysis_LevelCount
int       Analysis_DeltaFrame
int       Analysis_Backwards
int       Analysis_CpuFlags
int       Analysis_MotionFlags
int[x, y] Analysis_FrameSize
int[x, y] Analysis_Overlap
int[x, y] Analysis_BlockCount
int       Analysis_BitsPerSample
int[x, y] Analysis_ChromaRatio
int[x, y] Analysis_Padding

Usage

mvmanip.ExpandAnalysisData(vnode clip)

Parameters

  • clip:
    A clip generated by mv.Analyse() or which otherwise follows its conventions.

Examples

# Programmatically reference block size used
clip = vs.core.std.BlankClip()
msuper = clip.mv.Super()
forward = msuper.mv.Analyse()

annotated = forward.manipmv.ExpandAnalysisData()
block_size_x, block_size_y = annotated.get_frame(0).props["Analysis_BlockSize"]
print(f"Blocks used for analysis are {block_size_x}x{block_size_y}")

ScaleVect

[!WARNING] Arbitrary scaling of motion vectors is possible and conceptually reasonable and is thus allowed by this plugin. However subsequent operations which use the motion vectors may not support all of the scaled properties. For example scaling 8x8 blocks by 5x results in 40x40 blocks which aren't something Analyse supports generating.

Scales image_size, block_size, overlap, padding, and the individual motion_vectors contained in Analyse output by arbitrary and independent x and y factors. This is mostly useful to use motion vectors generated on a downscaled clip to perform operations on the full size clip (e.g. calculating motion vectors at 1080p and applying them at 4K).

Usage

manipmv.ScaleVect(vnode clip[, int scaleX=1, int scaleY=scaleX])

Parameters

  • clip:
    A clip generated by mv.Analyse() or which otherwise follows its conventions.
  • scaleX:
    The scale factor to use horizontally.
    This is limited to an 8 bit value, but practical scale factors are likely single digit.
    Default value is 1 (no scaling).
  • scaleY:
    The scale factor to use vertically.
    This is limited to an 8 bit value, but practical scale factors are likely single digit.
    Default value is the same as scaleX.

Examples

# Use vectors from 1080p clip to denoise 4K clip
SCALE = 2
HPAD = 16
VPAD = 16
clip = vs.core.std.BlankClip(width=3840, height=2160)
small_clip = clip.resize.Bilinear(clip.width // SCALE, clip.height // SCALE)

small_msuper = small_clip.mv.Super(hpad=HPAD, vpad=VPAD)
small_forward = small_msuper.mv.Analyse()
small_backward = small_msuper.mv.Analyse(isb=True)

big_msuper = clip.mv.Super(hpad=HPAD * SCALE, vpad=VPAD * SCALE)
upscaled_forward = small_forward.manipmv.ScaleVect(SCALE)
upscaled_backward = small_backward.manipmv.ScaleVect(SCALE)

denoised = clip.mv.Degrain1(big_msuper, upscaled_backward, upscaled_forward)
denoised.set_output()

ShowVect

Draws generated vectors onto a clip.

Usage

manipmv.ShowVect(vnode clip, vnode vectors[, bool useSceneChangeProps=True])

Parameters

  • clip:
    A YUV clip with 8/10/12/16 bit integer bitdepth and dimensions which match the vectors.
  • vectors:
    A clip generated by mv.Analyse() or which otherwise follows its conventions. The first frame of this clip will be fetched with the filter is instantiated to determine the pel and block_size used by the vectors.
  • useSceneChangeProps:
    Skips drawing vectors if frame props indicate they are from a different scene than the current frame of the clip. Specifically if the vectors are forwards then _SceneChangePrev and Scenechange are examined, and if the vectors are backwards _SceneChangeNext is examined. There are a few different filters which can stamp these frame props, but mv.SCDetection() should be preferred when visualizing vectors generated with delta greater than 1.

Examples

# This is not a terribly exciting example since the BlankClip doesn't move...
clip = vs.core.std.BlankClip(width=1920, height=1080)

msuper = clip.mv.Super()
forward = msuper.mv.Analyse()

clip = clip.mv.SCDetection(forward)
debug = clip.manipmv.ShowVect(forward)
debug.set_output()

Assumed Conventions

[!NOTE] No MVTools version really documents its conventions explicitly since they are considered to be internal. So the descriptions in this section should not be considered official, but they are hopefully correct, and serve to at least document the assumptions this plugin is making.

The dubhater/vapoursynth-mvtools plugin stores all of its working data for motion vectors as binary data in vapoursynth frame props on the clip which results from calling mv.Analyse(). Specifically there are two props of interest MVTools_MVAnalysisData and MVTools_vectors.

All of this data is serialized rather implictly from C++ structs. Most of these structs contain only signed integers (even for fields which do not have logical negative interpretations) and bytes are written with native endianness. For deserialization I have chosen to interpret fields that should not be negative (e.g. width, height, size) as unsigned and always use little endian byte order. These nuances are hopefully not relevant in practice as the positive integer range of a signed 32 bit integer is still much larger than practical video sizes and almost every host running MVTools is likely to be little endian natively. Still it would be nice conceptually if future motion vector work could make these conventions explicit; for this reason the types below will be listed with the signedness I think they should have.

MVTools_MVAnalysisData

This just contains some metadata about the context in which the vectors were generated. The length of this data is expected to always be 84 bytes (21 32-bit integers) in the following order:

[!IMPORTANT] The magic_key and version appear to be uninitialized by the MVTools plugin and so have no usable data in them.

u32 magic_key
u32 version
u32 block_size_x
u32 block_size_y
u32 pel
u32 level_count
i32 delta_frame
u32 backwards
u32 cpu_flags
u32 motion_flags
u32 width
u32 height
u32 overlap_x
u32 overlap_y
u32 block_count_x
u32 block_count_y
u32 bits_per_sample
u32 chroma_ratio_y
u32 chroma_ratio_x
u32 padding_x
u32 padding_y

MVTools_vectors

This contains the actual motion vector data for all levels of motion vector calculation. The structure of a single motion vector is simply:

i32 x
i32 y
u64 sad

These are serialized without any padding (16 bytes per vector). Each level is structured as:

u32 size
[] vectors

Again without any padding. So for example a level with 10 vectors would have a size value of 164 (16 bytes per vector * 10 vectors + 4 bytes for the size).

This is ultimately structured as:

u32 size
u32 valid
[] levels

Again without any padding. The value stored in size is therefore expected to be equivalent to the size which vapoursynth reports for the frame property. The valid value is expected to be 0 for situations where motion vectors are not present (e.g. the first frame of forwards vectors) and 1 for situations where motion vectors are present.

Maintenance notes

[!NOTE] This section is just so the maintainer doesn't forget how the repo is setup. This isn't really relevant for users.

Tests can be run with

zig build test --summary all

In theory everything is setup such that the following steps are sufficient to build the library, create a github release, and publish all the wheels to PyPI.

zig build version -- inc --patch
git commit -am "Bumping patch version"
git push

The release body can then be edited to whatever seems appropriate.

This depends on a number of third party github actions so any one of those could break. Zig could also make it easier or harder to get the version from build.zig.zon and the approach may need to be tweaked. For now it seems usable.

Metadata

Release files for vapoursynth-manipmv 1.5.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-manipmv 1.5.0
File Size Uploaded
vapoursynth_manipmv-1.5.0.tar.gz 67.6 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for vapoursynth-manipmv 1.5.0
File
vapoursynth_manipmv-1.5.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
vapoursynth_manipmv-1.5.0-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details
vapoursynth_manipmv-1.5.0-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details
vapoursynth_manipmv-1.5.0-py3-none-manylinux_2_17_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details
vapoursynth_manipmv-1.5.0-py3-none-manylinux_2_17_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details
vapoursynth_manipmv-1.5.0-py3-none-macosx_11_0_x86_64.whl Python 3 none macOS 11.0+ x86-64 Details
vapoursynth_manipmv-1.5.0-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details

Total release size: 341.4 kB

Release files / vapoursynth_manipmv-1.5.0.tar.gz

Download URL vapoursynth_manipmv-1.5.0.tar.gz
Size 67.6 kB
Tags Source
SHA-256 checksum
How to use checksums
21858a01ccaf7c1afe47d1e79a69139e5e6c7685398fbfaaa63710dbaf7b4892
BLAKE2b-256 checksum
How to use checksums
37f08e4486fc4c827f00171f6ddd04d84668d2f386533cdafc060ddfe14f4a85
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 2, 2026.

Transparency log

Release files / vapoursynth_manipmv-1.5.0-py3-none-win_amd64.whl

Download URL vapoursynth_manipmv-1.5.0-py3-none-win_amd64.whl
Size 134.6 kB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
4fb3113a3877390ae5cc54915dc37ccb26b6dbf911cc3b8b3a50ce3acacbe875
BLAKE2b-256 checksum
How to use checksums
d7ea1e7bb8d2b8d89d2380a418a0f21dc681d9199266cb5d9daf872cd40b004f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 2, 2026.

Transparency log

Release files / vapoursynth_manipmv-1.5.0-py3-none-musllinux_1_2_x86_64.whl

Download URL vapoursynth_manipmv-1.5.0-py3-none-musllinux_1_2_x86_64.whl
Size 30.2 kB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
8ead0685e86d297ad5dda9adaa5188491b250a767e302e544d2cf92e7d0b3da6
BLAKE2b-256 checksum
How to use checksums
83cb17ef56da38433f7b2b7a5df7f77b37bf24b35547e990516c13fc0ff3a5c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 2, 2026.

Transparency log

Release files / vapoursynth_manipmv-1.5.0-py3-none-musllinux_1_2_aarch64.whl

Download URL vapoursynth_manipmv-1.5.0-py3-none-musllinux_1_2_aarch64.whl
Size 19.5 kB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
8c19247ea8a8cda6689410503e4cd9c5c112ce5451b409a2061ba15b8c4df48a
BLAKE2b-256 checksum
How to use checksums
40a0a745fee3f88c992c5d1b3c8996271538ed03c668401eaee3a9fe7d2170f8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 2, 2026.

Transparency log

Release files / vapoursynth_manipmv-1.5.0-py3-none-manylinux_2_17_x86_64.whl

Download URL vapoursynth_manipmv-1.5.0-py3-none-manylinux_2_17_x86_64.whl
Size 30.5 kB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
9d953a5c16e48af417fcd6baa92c117a423e123d24712455356e766ba3792aef
BLAKE2b-256 checksum
How to use checksums
752371a510b227c4ebf727f41fc58f7de78ebcfdbbde88434b2131e464ef816a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 2, 2026.

Transparency log

Release files / vapoursynth_manipmv-1.5.0-py3-none-manylinux_2_17_aarch64.whl

Download URL vapoursynth_manipmv-1.5.0-py3-none-manylinux_2_17_aarch64.whl
Size 19.6 kB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
10d6c4a2a835bfe06631e4be1dcd99b8fcc1296423bf382d8a30ef4226a36417
BLAKE2b-256 checksum
How to use checksums
c6005c4f8259e4758a4eff286194e09d25c73acb735d922307abb0ec425504e2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 2, 2026.

Transparency log

Release files / vapoursynth_manipmv-1.5.0-py3-none-macosx_11_0_x86_64.whl

Download URL vapoursynth_manipmv-1.5.0-py3-none-macosx_11_0_x86_64.whl
Size 19.4 kB
Tags Python 3 macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
cb6a2f1e1fbe48659152507ab37bc432ad94597995bae3c3459264ccd7cdcaa5
BLAKE2b-256 checksum
How to use checksums
b2b3f8636ab3208ddcc605d29ef6af03c0d1547bf4d5a5540038076d7ce3d69f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 2, 2026.

Transparency log

Release files / vapoursynth_manipmv-1.5.0-py3-none-macosx_11_0_arm64.whl

Download URL vapoursynth_manipmv-1.5.0-py3-none-macosx_11_0_arm64.whl
Size 19.9 kB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
95301548c1d5ba33f9e69cf282d542cd031cc930476bda9ccf1b30070efc38a7
BLAKE2b-256 checksum
How to use checksums
f62de49f244f5ae180fd923aa599ad597d18400c5f7c7ef57538d70e46f43f8b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 2, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.5.0 This release

8 release files

1.4.0

8 release files

1.3.0

8 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