This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit 0dc120b360b604c3a1505644f0863fb23c35e08c
Author:     Niklas Haas <[email protected]>
AuthorDate: Sat Aug 29 14:54:29 2026 +0200
Commit:     Niklas Haas <[email protected]>
CommitDate: Tue Sep 1 13:15:06 2026 +0200

    avfilter/vf_colordetect: add threshold option
    
    This is useful to filter false positives due to encoding noise in lossily
    compressed sources.
    
    The reason this is limited to 0.05 (= 5%) is twofold:
    
    1. Higher values would mostly defeat the purpose of this filter, since a
       deviation of >5% is enough to fully blur the distinction between JPEG and
       MPEG range, let alone premultiplied and straight alpha. There's no useful
       signal to extract with such a high tolerance for noise.
    
    2. Higher values would overflow the `offset` value in the 8-bit SIMD. This
       could also be solved by clamping, but the first point is the salient one
       anyways.
    
    Sponsored-by: nxtedition AB
    Signed-off-by: Niklas Haas <[email protected]>
---
 doc/filters.texi             |  4 ++++
 libavfilter/vf_colordetect.c | 20 +++++++++++++-------
 2 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index de1aca5257..4cd673eae0 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -9957,6 +9957,10 @@ premultiplied. Also detects if the alpha plane is fully 
opaque or not.
 Enable detection of all of the above properties. This is the default.
 @end table
 
+@item threshold
+Allow pixels to exceed the expected bounds by this fraction of the full scale
+value range before treating it as evidence of (respectively) full range or
+straight alpha. Value range is from 0 to 0.05. Defaults to 0.0.
 @end table
 
 @section colorize
diff --git a/libavfilter/vf_colordetect.c b/libavfilter/vf_colordetect.c
index b36ba34079..3750b7d86a 100644
--- a/libavfilter/vf_colordetect.c
+++ b/libavfilter/vf_colordetect.c
@@ -48,6 +48,7 @@ typedef struct ColorDetectContext {
     const AVClass *class;
     FFColorDetectDSPContext dsp;
     unsigned mode;
+    float threshold;
 
     const AVPixFmtDescriptor *desc;
     int nb_threads;
@@ -75,6 +76,9 @@ static const AVOption colordetect_options[] = {
         { "color_range", "Detect (YUV) color range", 0, AV_OPT_TYPE_CONST, 
{.i64 = COLOR_DETECT_COLOR_RANGE}, 0, 0, FLAGS, .unit = "mode" },
         { "alpha_mode",  "Detect alpha mode",        0, AV_OPT_TYPE_CONST, 
{.i64 = COLOR_DETECT_ALPHA_MODE }, 0, 0, FLAGS, .unit = "mode" },
         { "all",         "Detect all supported properties", 0, 
AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, FLAGS, .unit = "mode" },
+
+    /* Note: threshold should not be increased past ~0.4 as it overflows 8-bit 
SIMD otherwise */
+    { "threshold", "Detection threshold, as a fraction of the full range", 
OFFSET(threshold), AV_OPT_TYPE_FLOAT, {.dbl = 0.0}, 0.0, 0.05, FLAGS },
     { NULL }
 };
 
@@ -106,6 +110,7 @@ static int config_input(AVFilterLink *inlink)
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
     const int depth = desc->comp[0].depth;
     const int range = (1 << depth) - 1;
+    const int threshold = lrintf(s->threshold * range);
     const int mpeg_min =  16 << (depth - 8);
     const int mpeg_max = 235 << (depth - 8);
     if (depth > 16) /* not currently possible; prevent future bugs */
@@ -117,31 +122,32 @@ static int config_input(AVFilterLink *inlink)
     s->nb_threads = ff_filter_get_nb_threads(ctx);
 
     /* Color range detection: */
-    s->mpeg_min = mpeg_min;
-    s->mpeg_max = mpeg_max;
+    s->mpeg_min = av_clip_uintp2(mpeg_min - threshold, depth);
+    s->mpeg_max = av_clip_uintp2(mpeg_max + threshold, depth);
 
     /**
      * Alpha mode detection:
      *
      * To check if a value is out of range, we need to compare the color value
      * against the maximum possible color for a given alpha value.
-     *   x > ((mpeg_max - mpeg_min) / pixel_max) * a + mpeg_min
+     *   x > ((mpeg_max - mpeg_min) / pixel_max) * a + mpeg_min + threshold
      *
      * This simplifies to:
-     *   (x - mpeg_min) * pixel_max > (mpeg_max - mpeg_min) * a
+     *   (x - mpeg_min - threshold) * pixel_max > (mpeg_max - mpeg_min) * a
      *   = range * x - offset > mpeg_range * a in the below formula.
      *
      * We subtract an additional offset of (1 << (depth - 1)) to account for
      * rounding errors in the value of `x`.
      *
-     * For full range input this degenerates to `x > a`, so the offset is 0.
+     * For full range input this degenerates to `x > a + threshold`, so the
+     * threshold is passed through directly, without the `range` scaling.
      */
     if (inlink->color_range == AVCOL_RANGE_JPEG) {
         s->mpeg_range = range;
-        s->offset = 0;
+        s->offset = threshold;
     } else {
         s->mpeg_range = mpeg_max - mpeg_min;
-        s->offset = range * mpeg_min + (1 << (s->depth - 1));
+        s->offset = range * (mpeg_min + threshold) + (1 << (depth - 1));
     }
 
     if (desc->flags & AV_PIX_FMT_FLAG_RGB) {

-- 
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to