PR #25199 opened by nmoinvaz
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25199
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25199.patch

# Summary of changes

`compute_sat_hue_metrics8()` called `hypotf()`, `atan2f()`, `floorf()` and 
`fmodf()` for every chroma sample. Both results depend only on the 8-bit (u, v) 
pair, so this builds two 64K tables once with the same expressions, and the 
per-sample loop becomes two loads. Higher bit depths keep the current code.

The frames and every `lavfi.signalstats.*` value are identical to master for 
yuv420p, yuv422p, yuv444p and yuvj420p input, and with the `tout`, `vrep` and 
`brng` options.

| 1080p, 48 frames, 1 thread | master | this PR | time |
|---|---|---|---|
| yuv420p | 1.03 s | 0.28 s | -71% |
| yuv444p | 3.52 s | 0.72 s | -79% |

Measured on an i7-1185G7 with GCC 16.



>From d00c185727d63cdae1c7c6c3c051a9b6b5e15b7a Mon Sep 17 00:00:00 2001
From: Nathan Moinvaziri <[email protected]>
Date: Sun, 18 Oct 2026 13:33:09 -0700
Subject: [PATCH] avfilter/vf_signalstats: look up the saturation and hue of
 8-bit chroma

Every chroma sample called hypotf(), atan2f(), floorf() and fmodf().
Tables of both values for every 8-bit (u, v) pair, built once with the
same expressions, keep the statistics identical and make the filter
about 3.5 times faster on 4:2:0 input.
---
 libavfilter/vf_signalstats.c | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/libavfilter/vf_signalstats.c b/libavfilter/vf_signalstats.c
index 5859b46478..b2e91da7be 100644
--- a/libavfilter/vf_signalstats.c
+++ b/libavfilter/vf_signalstats.c
@@ -24,6 +24,7 @@
 #include "libavutil/mem.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
+#include "libavutil/thread.h"
 #include "filters.h"
 
 enum FilterMode {
@@ -87,11 +88,28 @@ static const AVOption signalstats_options[] = {
 
 AVFILTER_DEFINE_CLASS(signalstats);
 
+/* saturation and hue of every 8-bit (u, v) pair, indexed by u << 8 | v */
+static uint8_t sat8[256 * 256];
+static int16_t hue8[256 * 256];
+
+static av_cold void init_sat_hue8(void)
+{
+    for (int yuvu = 0; yuvu < 256; yuvu++) {
+        for (int yuvv = 0; yuvv < 256; yuvv++) {
+            sat8[yuvu << 8 | yuvv] = hypotf(yuvu - 128, yuvv - 128); // int or 
round?
+            hue8[yuvu << 8 | yuvv] = fmodf(floorf((180.f / M_PI) * 
atan2f(yuvu-128, yuvv-128) + 180.f), 360.f);
+        }
+    }
+}
+
 static av_cold int init(AVFilterContext *ctx)
 {
+    static AVOnce init_static_once = AV_ONCE_INIT;
     uint8_t r, g, b;
     SignalstatsContext *s = ctx->priv;
 
+    ff_thread_once(&init_static_once, init_sat_hue8);
+
     if (s->outfilter != FILTER_NONE)
         s->filters |= 1 << s->outfilter;
 
@@ -479,10 +497,9 @@ static int compute_sat_hue_metrics8(AVFilterContext *ctx, 
void *arg, int jobnr,
 
     for (j = slice_start; j < slice_end; j++) {
         for (i = 0; i < s->chromaw; i++) {
-            const int yuvu = p_u[i];
-            const int yuvv = p_v[i];
-            p_sat[i] = hypotf(yuvu - 128, yuvv - 128); // int or round?
-            ((int16_t*)p_hue)[i] = fmodf(floorf((180.f / M_PI) * 
atan2f(yuvu-128, yuvv-128) + 180.f), 360.f);
+            const int uv = p_u[i] << 8 | p_v[i];
+            p_sat[i] = sat8[uv];
+            ((int16_t*)p_hue)[i] = hue8[uv];
         }
         p_u   += lsz_u;
         p_v   += lsz_v;
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to