Skip to content

Entry point

vidmag.magnify(video, *, preset=None, backend='auto', precision='fp32', fps=None, out=None, drop_last=0, **overrides)

Magnify a clip with a named preset, on the best available backend.

Parameters:

Name Type Description Default
video str | PathLike[str] | ndarray | Iterable[ndarray]

a path to a video file, a (T, H, W, 3) uint8 BGR array, or any iterable of such frames (it is materialised into one array — the temporal filters need the whole clip).

required
preset str | None

which preset to run; see :data:vidmag.presets.PRESETS for the names, the numbers, and where each came from. Required.

None
backend str

"auto" (native CUDA if it can run, else the CPU baseline), or a registered name such as "cpu" or "cuda". A named backend that cannot run here raises :class:vidmag.backend.BackendUnavailableError with the reason; it is never quietly replaced by another.

'auto'
precision str

"fp32" or "fp16". "fp16" exists on the CUDA backend for the pulse and motion-IIR pipelines only, and asking for it elsewhere raises rather than silently computing in fp32. The CPU oracle computes in float64 regardless — it is the reference, and its precision is not a knob.

'fp32'
fps float | None

the clip's frame rate. Read from the file for path input (pass it to override); required for array input whenever the pipeline's temporal band is in Hz, or whenever out is given, because the encoder needs a rate.

None
out str | PathLike[str] | None

write the result to this path as H.264 as well as returning it.

None
drop_last int

drop this many frames from the end before magnifying. Defaults to 0 — see the module docstring, plan decision D8. Pass 10 to reproduce the MATLAB reference and the magnify_* functions.

0
**overrides Any

individual preset parameters to replace, e.g. alpha=25, fl=0.5. A name the pipeline does not take raises TypeError from the core, naming it.

{}

Returns:

Type Description
ndarray

The magnified clip, (T, H, W, 3) uint8 BGR — same shape and dtype as

ndarray

the input frames.

Raises:

Type Description
ValueError

no preset, an unusable precision, or a missing frame rate.

KeyError

an unknown preset name (the message lists the known ones).

BackendError

the named backend is unknown or unavailable.

Source code in src/vidmag/api.py
def magnify(
    video: str | os.PathLike[str] | np.ndarray | Iterable[np.ndarray],
    *,
    preset: str | None = None,
    backend: str = "auto",
    precision: str = "fp32",
    fps: float | None = None,
    out: str | os.PathLike[str] | None = None,
    drop_last: int = 0,
    **overrides: Any,
) -> np.ndarray:
    """Magnify a clip with a named preset, on the best available backend.

    Args:
        video: a path to a video file, a ``(T, H, W, 3)`` uint8 BGR array, or
            any iterable of such frames (it is materialised into one array —
            the temporal filters need the whole clip).
        preset: which preset to run; see :data:`vidmag.presets.PRESETS` for the
            names, the numbers, and where each came from. Required.
        backend: ``"auto"`` (native CUDA if it can run, else the CPU baseline),
            or a registered name such as ``"cpu"`` or ``"cuda"``. A named
            backend that cannot run here raises
            :class:`vidmag.backend.BackendUnavailableError` with the reason; it is
            never quietly replaced by another.
        precision: ``"fp32"`` or ``"fp16"``. ``"fp16"`` exists on the CUDA
            backend for the pulse and motion-IIR pipelines only, and asking for
            it elsewhere raises rather than silently computing in fp32. The CPU
            oracle computes in float64 regardless — it is the reference, and its
            precision is not a knob.
        fps: the clip's frame rate. Read from the file for path input (pass it
            to override); required for array input whenever the pipeline's
            temporal band is in Hz, or whenever ``out`` is given, because the
            encoder needs a rate.
        out: write the result to this path as H.264 as well as returning it.
        drop_last: drop this many frames from the end before magnifying.
            Defaults to 0 — see the module docstring, plan decision D8. Pass 10
            to reproduce the MATLAB reference and the ``magnify_*`` functions.
        **overrides: individual preset parameters to replace, e.g.
            ``alpha=25``, ``fl=0.5``. A name the pipeline does not take raises
            ``TypeError`` from the core, naming it.

    Returns:
        The magnified clip, ``(T, H, W, 3)`` uint8 BGR — same shape and dtype as
        the input frames.

    Raises:
        ValueError: no preset, an unusable precision, or a missing frame rate.
        KeyError: an unknown preset name (the message lists the known ones).
        vidmag.backend.BackendError: the named backend is unknown or unavailable.
    """
    if preset is None:
        raise ValueError(
            "magnify() needs a preset, e.g. preset='pulse'; available: "
            f"{', '.join(sorted(_presets.PRESETS))}. Each one's parameters and "
            "provenance are in vidmag.presets.PRESETS."
        )
    spec = _presets.get(preset)
    params = {**spec.params, **overrides}

    frames, rate = _read_input(video, fps=fps, drop_last=drop_last)

    name, impl = _backend.select(backend)
    core = _resolve_core(impl, name, spec.pipeline, precision)
    rate = _resolve_rate(rate, core=core, params=params, stem=spec.pipeline,
                         writing=out is not None)

    _log.info(
        "vidmag.magnify: backend=%r pipeline=%r precision=%s frames=%d fps=%s "
        "preset=%r",
        name, spec.pipeline, precision, len(frames),
        rate if rate else "unused by this pipeline", preset,
    )

    # The cores are reached through the registry, so their type is Any here.
    # This is the one place the promise in the docstring — (T, H, W, 3) uint8 —
    # is written down for the checker.
    result: np.ndarray = core(frames, rate, **params)

    if out is not None:
        from .io.video import encode_video

        encode_video(result, os.fspath(out), rate)
    return result

Presets

vidmag.presets

Named parameter sets for the four magnification pipelines.

One frozen table, :data:PRESETS, is the single source of truth for every "just magnify it" entry point — the facade, the CLI and the docs all read it, so a preset's numbers exist in exactly one place.

Each row records three things: which pipeline it selects, the parameters, and what it is for. A fourth field, source, names the place in this repository the numbers were taken from; nothing is in this table that cannot be traced to a call the project already makes and checks.

Preset.pipeline is a pipeline stem. It resolves two ways, and both are the same pipeline:

stem "motion_lpyr_iir"  ->  vidmag.cpu.magnify.motion_lpyr_iir_core(frames, fps, **params)
                        ->  vidmag.magnify_motion_lpyr_iir(in_path, out_path, **params)

fl / fh are in Hz and are interpreted against the clip's own frame rate. No preset pins sampling_rate: the reference calls pass it explicitly only because their clips happen to be 30 fps, and hard-coding 30 into a preset would silently mis-filter every clip that is not (see pulse below, where that difference is a no-op on the reference clip and a correctness fix on any other).

PRESETS = MappingProxyType({'pulse': Preset(pipeline='color_gdown_ideal', params=MappingProxyType({'alpha': 50.0, 'level': 4, 'fl': 50 / 60, 'fh': 60 / 60, 'chrom_attenuation': 1.0}), description='Human pulse: the colour change blood flow makes in skin, banded to 50-60 bpm. Faces, wrists, babies.', source="tests/test_against_mit_reference.py::test_face_color_matches_mit — reproduceResults.m's face.mp4 call, checked against MIT's own render face-ideal-from-0.83333-to-1-alpha-50-level-4-chromAtn-1.mp4. That call also passes sampling_rate=30.0; this preset omits it because face.mp4 is exactly 30.0 fps, so the core's default (the clip's own fps) reproduces the call byte for byte while staying correct on clips at other frame rates."), 'motion': Preset(pipeline='motion_lpyr_iir', params=MappingProxyType({'alpha': 10.0, 'lambda_c': 16.0, 'r1': 0.4, 'r2': 0.05, 'chrom_attenuation': 0.1}), description="Sub-pixel motion at everyday speeds: an infant's breathing, a chest rising, a swaying structure. The r1/r2 IIR band is sampling-rate free, so this preset needs no fps assumption.", source="tests/test_against_mit_reference.py::test_baby_iir_matches_mit — reproduceResults.m's baby.mp4 call, checked against MIT's own render baby-iir-r1-0.4-r2-0.05-alpha-10-lambda_c-16-chromAtn-0.1.mp4."), 'motion_phase': Preset(pipeline='phase', params=MappingProxyType({'alpha': 15.0, 'fl': 0.5, 'fh': 1.5, 'scales': 3, 'orientations': 4, 'sigma': 0.0}), description="The same sub-pixel motion as 'motion', but amplified by changing phase rather than by scaling image detail. Slower, and it holds together at amplifications where the other method tears into ripples at edges. Use it when 'motion' produces artefacts before it produces a visible movement.", source="Wadhwa, Rubinstein, Durand and Freeman, 'Phase-Based Video Motion Processing', SIGGRAPH 2013. The parameters here are a starting point rather than a reproduction of a published call: unlike the other presets, this one is NOT checked against the authors' own rendered output, because that output is not among the files this project can fetch. What is checked, in tests/test_phase_based.py, is that a clip built with a known sub-pixel movement comes out moved by the predicted amount."), 'vibration': Preset(pipeline='motion_lpyr_ideal', params=MappingProxyType({'alpha': 50.0, 'lambda_c': 10.0, 'fl': 72.0, 'fh': 92.0, 'chrom_attenuation': 0.0}), description='Mechanical vibration in a narrow band — the guitar low-E string at 72-92 Hz. REQUIRES A HIGH-SPEED CLIP: those cutoffs are above Nyquist for anything under ~184 fps, where the ideal bandpass passes nothing and the output is the input.', source='scripts/run_evm.py module docstring, the guitar.mp4 E-string example (--mode motion --alpha 50 --lambda-c 10 --fl 72 --fh 92 --chromatt 0). Weaker provenance than the two above: this repository downloads guitar.mp4 (scripts/download_samples.py) but holds no MIT render of it, so nothing here checks the result — unlike pulse and motion, which the MIT-reference tests measure.')}) module-attribute

Preset

Bases: NamedTuple

One row of :data:PRESETS.

pipeline is the stem shared by the array core (<stem>_core in :mod:vidmag.cpu.magnify) and the path function (magnify_<stem>); params are keyword arguments for either.

Source code in src/vidmag/presets.py
class Preset(NamedTuple):
    """One row of :data:`PRESETS`.

    ``pipeline`` is the stem shared by the array core
    (``<stem>_core`` in :mod:`vidmag.cpu.magnify`) and the path function
    (``magnify_<stem>``); ``params`` are keyword arguments for either.
    """

    pipeline: str
    params: Mapping[str, float | int]
    description: str
    source: str

get(name)

Look up a preset by name, naming the alternatives when there is no match.

Source code in src/vidmag/presets.py
def get(name: str) -> Preset:
    """Look up a preset by name, naming the alternatives when there is no match."""
    try:
        return PRESETS[name]
    except KeyError:
        raise KeyError(
            f"unknown preset {name!r}; available: {', '.join(sorted(PRESETS))}"
        ) from None

Choosing a backend

vidmag.backend.registry

The backend registry — names to implementations, with capability flags.

docs/dev/PLAN.md section 3c, step 3.5. Three properties matter more than the code:

Registration is data. :func:register stores two callables and a capability record; neither is called. The implementation module is imported the first time somebody selects that backend, so import vidmag on a machine with no GPU never touches the CUDA extension.

Nothing fails quietly. An unknown name lists what is registered, an unavailable backend repeats the reason its probe gave (no driver, no device, extra not installed), and "auto" with nothing usable reports every candidate it tried and why each failed. Falling back from GPU to CPU by accident would cost roughly 700x, so it never happens implicitly — "auto" reports its choice through the returned name and an INFO log line, and an explicitly named backend that is unavailable raises rather than substituting another.

The order is fixed and documented: :data:PREFERENCE_ORDER.

Capabilities dataclass

What a backend can do, readable without importing it.

Source code in src/vidmag/backend/registry.py
@dataclass(frozen=True)
class Capabilities:
    """What a backend can do, readable without importing it."""

    #: NumPy dtype names the backend computes in, e.g. ``("float32", "float16")``.
    dtypes: tuple[str, ...]
    #: True if an FFT is available, so ``ideal_bandpass`` is the exact
    #: reference filter rather than a band-projection approximation.
    fft: bool
    #: True if the backend can run a causal, frame-at-a-time pipeline.
    streaming: bool

select(name='auto')

Resolve a backend name to (resolved_name, implementation).

The resolved name is returned so the caller can show it — the choice is never silent. "auto" walks :data:PREFERENCE_ORDER and takes the first registered backend whose probe says it can run.

Raises:

Type Description
UnknownBackendError

the name was never registered.

BackendUnavailableError

the named backend cannot run here, or "auto" found nothing usable — the message carries every reason.

Source code in src/vidmag/backend/registry.py
def select(name: str = "auto") -> tuple[str, Any]:
    """Resolve a backend name to ``(resolved_name, implementation)``.

    The resolved name is returned so the caller can show it — the choice is
    never silent. ``"auto"`` walks :data:`PREFERENCE_ORDER` and takes the first
    registered backend whose probe says it can run.

    Raises:
        UnknownBackendError: the name was never registered.
        BackendUnavailableError: the named backend cannot run here, or
            ``"auto"`` found nothing usable — the message carries every reason.
    """
    if name == "auto":
        return _select_auto()

    entry = _REGISTRY.get(name)
    if entry is None:
        known = ", ".join(sorted(_REGISTRY)) or "none"
        raise UnknownBackendError(
            f"unknown backend {name!r}; registered backends: {known}"
        )
    reason = _probe(entry)
    if reason is not None:
        raise BackendUnavailableError(
            f"backend {name!r} is registered but unavailable: {reason}"
        )
    _log.info("vidmag: using backend %r (requested explicitly)", name)
    return name, _load(entry)

list_backends()

Every registered backend, in preference order, with its availability.

Probes each backend, so the reasons are current. Does not load any implementation.

Source code in src/vidmag/backend/registry.py
def list_backends() -> tuple[BackendInfo, ...]:
    """Every registered backend, in preference order, with its availability.

    Probes each backend, so the reasons are current. Does not load any
    implementation.
    """
    return tuple(
        BackendInfo(
            name=entry.name,
            capabilities=entry.capabilities,
            unavailable_reason=_probe(entry),
        )
        for entry in _ordered_entries()
    )

register(name, *, load, probe, capabilities)

Record a backend without importing it.

Parameters:

Name Type Description Default
name str

selection name, e.g. "cuda". Names in :data:PREFERENCE_ORDER take part in "auto" selection.

required
load Callable[[], Any]

called at most once, on first selection; returns the object implementing :class:~vidmag.backend.Ops (and optionally :class:~vidmag.backend.Pipelines). Import the heavy module inside this callable, never at registration time.

required
probe Callable[[], str | None]

called on every selection attempt; returns None if the backend can run here, otherwise a one-line reason ("extension not built", "no CUDA device", "install vidmag[torch]").

required
capabilities Capabilities

advertised without loading anything.

required

Raises:

Type Description
ValueError

if the name is already registered.

Source code in src/vidmag/backend/registry.py
def register(
    name: str,
    *,
    load: Callable[[], Any],
    probe: Callable[[], str | None],
    capabilities: Capabilities,
) -> None:
    """Record a backend without importing it.

    Args:
        name: selection name, e.g. ``"cuda"``. Names in
            :data:`PREFERENCE_ORDER` take part in ``"auto"`` selection.
        load: called at most once, on first selection; returns the object
            implementing :class:`~vidmag.backend.Ops` (and optionally
            :class:`~vidmag.backend.Pipelines`). Import the heavy module *inside*
            this callable, never at registration time.
        probe: called on every selection attempt; returns ``None`` if the
            backend can run here, otherwise a one-line reason ("extension not
            built", "no CUDA device", "install vidmag[torch]").
        capabilities: advertised without loading anything.

    Raises:
        ValueError: if the name is already registered.
    """
    if name in _REGISTRY:
        raise ValueError(
            f"backend {name!r} is already registered; "
            "each name may be registered exactly once"
        )
    _REGISTRY[name] = _Entry(
        name=name, load=load, probe=probe, capabilities=capabilities
    )