Hi everyone I have used claude (Fable 5) for auditing our filters for premultiplied support Niklas, has written the Prompt (see below) and ive used claude to create a table based on it and run it over the filters starting with a* ive also run an adverserical agent over it trying to poke holes in the choices
My main question to you is, should we change something on the prompt or design?
Should we run this over all the filters ? (that is, will this give us enough
confidence on what to set each filter to)
Prompt:
Go through the list of filters in libavfilter/ and categorize them based on
how they would interact with premultiplied alpha.
Categorize filters into the following types:
* Color-agnostic filters that don't touch or read the pixel data at all
(e.g. truly metadata-only filters)
* Metadata-only filters that read, but don't write, pixel values (e.g.
measuring a histogram). Subcategorize as best you can into filters that would
prefer premul, prefer independent, or genuinely don't care. (Though note that
this is ultimately a subjective judgement call in many cases)
* Filters modify pixel data in a way that's truly invariant under alpha
premultiplication (e.g. applying a linear scaling factor or swapping RGB
channels), and thus permute under (premultiply,filter) -> (filter,premultiply)
* "Alpha-dependent" filters that inherently preserve the rgb <= alpha
property, and would thus work with either alpha mode, but may give different
results under premultiplied vs nonpremultiplied by virtue of some non-linearity
that affects how pixel values are processed. These would be filters that
*don't* commute under premultiplication. This includes e.g. scaling filters,
crossfades, and other "linear"-type functions of several pixels that mix
channels according to the same relative weights, as well as anything that
applies a monotonic (non-linear) function to all channels (including A). Again,
subcaterorize them as best you can into their alpha mode preference. (e.g.
scaling and crossfading should always be done with premultiplied alpha, to
avoid e.g. bleeding transparent pink padding pixels into actual pixel data)
* Premul-incompatible filters that would definitely give broken results
(e.g. rgb > alpha) when fed premultiplied pixel values. This would include any
filter that does some nontrivial per-channel processing that might break the
alpha-scaling, e.g. a brightening filter that adds some offset to the RGB
values, or applying a nonlinear transfer function to only the R,G,B channels
while leaving the A channel intact.
For filters that may span multiple types depending on the selected options,
list them under both headings; and clarify under which condition/option it
belongs to that category. Flag edge cases and filters you're unsure about
explicitly for human review.
Adverserial agent finding:
1. alphamerge — wrong mechanism and wrong I/O. The straight-alpha pin is on
the output only and does not propagate to the inputs: a premultiplied main
input is negotiated as premul and then merely relabeled straight in
do_alphamerge — no
unpremultiply is inserted upstream, contrary to what the row claimed. Also
the filter has two video inputs, so I/O is now VV->V, not V->V.
2. avgblur-family E conditions were proper subsets of the truth. Not just
"planes without bit 3": any mask that blurs A and color unequally (e.g.
planes=8, alpha blurred under sharp premul RGB) is E; planes=0xF is the only D
mask. All three D/E row
pairs reworded.
3. avgblur_vulkan "OOB reads return 0" was unsupported. FFmpeg enables no
Vulkan robustness feature, so out-of-bounds imageLoad is undefined per spec;
zero-return is common driver behavior, not a guarantee. Row now says so and
keeps a human-review
flag.
4. Header convention gap: Alpha pref was undefined for E rows — now
documented as "the only mode in which the filter is safe".
And the current results
# libavfilter video filters: premultiplied-alpha survey
Every filter from libavfilter/allfilters.c with a video INPUT (prefixes vf,
vaf, vsink), FFmpeg master 88bc27643a, 392 filters. Pure sources and
audio-to-video visualizers are excluded.
The I/O column is derived from the extern symbol prefix; a few multi-media
filters carry more streams than shown (e.g. concat is N media in/out).
Categories (how the filter behaves on premultiplied-alpha input):
- A-agnostic: does not read or write pixel data at all (truly metadata-only)
- B-meta: reads but never writes pixels (measurements); Alpha pref says on
which representation the measurement is more meaningful
- C-invariant: pixel changes commute with premultiplication
(premultiply,filter) == (filter,premultiply)
- D-alpha-dep: preserves rgb <= alpha, works in either mode, but does NOT
commute (resampling/mixing with shared weights, monotonic
functions applied to all channels incl. A); Alpha pref says
which mode gives the better result (e.g. scaling: premul)
- E-incompat: can produce broken results (rgb > alpha) on premultiplied input
(per-channel offsets/nonlinearities on RGB with A untouched,
writing A independently of RGB)
- sink: consumes frames
Option-dependent filters get one row per category with the condition in Notes.
Columns:
- Alpha formats: whether the filter accepts/produces pixel formats with an
alpha channel at all ("none" = alpha never passes through it)
- Alpha pref: n/a | premul | straight | dont-care | option-dependent
(for E rows: the only mode in which the filter is safe)
- Confidence: high | medium | low (how sure the categorization is)
- Checked: empty until a human or an adversarial agent verified the row
| Filter | I/O | Alpha formats | Category
| Alpha pref | Confidence | Checked | Notes
|---------------------|------------|------------------------------|-------------|------------------|------------|------------------|-------
| addroi | V->V | all (no constraint) | A-agnostic
| n/a | high | agent 2026-08-05 | edits ROI side data only;
AVFILTER_FLAG_METADATA_ONLY, never touches frame->data
| alphaextract | V->V | all alpha fmts | C-invariant
| dont-care | high | agent 2026-08-05 | copies the A plane only,
output has no alpha -- premultiplied input changes nothing; graph still pins
input straight (constraint shared with extractplanes)
| alphamerge | VV->V | YUVA420/22/44,GBRAP,RGBA-fam | E-incompat
| straight | high | agent 2026-08-05 | overwrites A verbatim from
2nd input luma, RGB untouched -> rgb>alpha on premul; pins alpha_mode=straight
on the OUTPUT only -- the pin does not propagate to the inputs, a premul main
input is just relabeled straight (no conversion inserted)
| amplify | V->V | planar YUVA/GBRAP 8-16,f32 | E-incompat
| straight | high | agent 2026-08-05 | temporal push from mean,
threshold-gated per plane; default planes=7 leaves A untouched; nonlinear
per-plane even with planes=15 -> still E
| ass | V->V | all alpha fmts (drawutils) | E-incompat
| straight | high | agent 2026-08-05 | default alpha=0: A plane
excluded from blending, subtitle RGB written under transparent premul px ->
rgb>alpha; harmless on opaque video
| ass | V->V | all alpha fmts (drawutils) | D-alpha-dep
| option-dependent | high | agent 2026-08-05 | alpha=1: A blended, src
color premultiplied when negotiated mode=premul; blend carries a_c^2 term
(non-textbook OVER, exact only for opaque subtitle colors) -- confirmed in
drawutils blend_pixel/ff_blend_mask
| atadenoise | V->V | planar YUVA/GBRAP 8-16 | E-incompat
| straight | high | agent 2026-08-05 | default planes=7: color
denoised per-plane (frame subsets differ per channel), A copied -> rgb>alpha
reachable; planes=15: plane-3 ptrs never filled -> NULL deref, confirmed by 2nd
independent code read, never executed -- human test wanted
| avgblur | V->V | planar YUVA/GBRAP 8-16 | D-alpha-dep
| premul | high | agent 2026-08-05 | planes=0xF (default) is
the ONLY D mask: box blur, uniform weights shared across all planes incl. A;
420/422 chroma footprint differs from A plane
| avgblur | V->V | planar YUVA/GBRAP 8-16 | E-incompat
| straight | high | agent 2026-08-05 | any mask blurring A and
color unequally (e.g. 7: A stays sharp; 8: only A blurred) -> rgb>alpha at edges
| avgblur_opencl | V->V | hw (device-dependent) | D-alpha-dep
| premul | medium | agent 2026-08-05 | planes=0xF (default) is
the ONLY D mask: uniform box kernel per plane, edge-renormalized; packed RGBA
is one image, A never blurred separately (bit 0 gates the whole pixel)
| avgblur_opencl | V->V | hw (device-dependent) | E-incompat
| straight | medium | agent 2026-08-05 | any mask blurring a planar
alpha plane and color unequally (e.g. 7 or 8 on GBRAP) -> rgb>alpha
| avgblur_vulkan | V->V | hw (device-dependent) | D-alpha-dep
| premul | medium | agent 2026-08-05 | planes=0xF (default) is
the ONLY D mask: shared filter_norm for all channels; border imageLoad is OOB
and FFmpeg enables no Vulkan robustness -- undefined per spec, common driver
zero-return hits RGB and A equally (invariant kept if so) -- human review
| avgblur_vulkan | V->V | hw (device-dependent) | E-incompat
| straight | medium | agent 2026-08-05 | any mask blurring a planar
alpha plane and color unequally (e.g. 7 or 8 on GBRAP) -> rgb>alpha
| backgroundkey | V->V | |
| | | |
| bbox | V->V | |
| | | |
| bench | V->V | |
| | | |
| bilateral | V->V | |
| | | |
| bilateral_cuda | V->V | |
| | | |
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
Take away the freedom of one citizen and you will be jailed, take away
the freedom of all citizens and you will be congratulated by your peers
in Parliament.
signature.asc
Description: PGP signature
_______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
