PR #25097 opened by romainhedouin URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25097 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25097.patch
# Summary of changes The luma coefficients (av_q2d of cr/cg/cb, used by the desaturation step) and the mobius constants only depend on the options and the peak of the frame, but they were recalculated for every pixel. Compute them once per frame in filter_frame(). The expressions are unchanged. 1080p, one thread, arm64, desat=2(default): none is 1.33x faster hable is 1.39x faster reinhard is 1.64x faster mobius is 1.69x faster gamma is 1.1x faster With desat=0 the biggest change is mobius being 1.11x faster Output is byte-identical to master on 5376 combinations of algorithm, param, desat, peak, colourspace, pixel format (gbrpf32/gbrapf32) and thread count. Only tested on arm64 with clang: the result relies on the default floating point contraction (with -ffp-contract=off only NaN payloads differ). Noticed during a code review sweep of ffmpeg using an LLM. <!-- If this PR requires new FATE test samples, attach them to the PR and list their target paths below (relative to the fate-suite root). Attached filenames must match the sample's filename: ```fate-samples # e.g. vorbis/new-sample.ogg ``` --> >From 0a850e8f1fd2f8dae7e89411cc1bdd673c433c7c Mon Sep 17 00:00:00 2001 From: romainhedouin <[email protected]> Date: Thu, 15 Oct 2026 13:23:30 +0200 Subject: [PATCH] avfilter/vf_tonemap: compute per-frame values once per frame The luma coefficients and the mobius constants only depend on the options and the peak of the frame, but they were recalculated for every pixel. Calculate them once per frame. The expressions are unchanged. Found during a code review sweep of ffmpeg using an LLM. --- libavfilter/vf_tonemap.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/libavfilter/vf_tonemap.c b/libavfilter/vf_tonemap.c index c5be4b1510..0a14c51e3c 100644 --- a/libavfilter/vf_tonemap.c +++ b/libavfilter/vf_tonemap.c @@ -60,6 +60,10 @@ typedef struct TonemapContext { double peak; const AVLumaCoefficients *coeffs; + + /* constant for a whole frame, computed in filter_frame() */ + double cr, cg, cb; + float mobius_a, mobius_b, mobius_k; } TonemapContext; static av_cold int init(AVFilterContext *ctx) @@ -93,17 +97,24 @@ static float hable(float in) return (in * (in * a + b * c) + d * e) / (in * (in * a + b) + d * f) - e / f; } -static float mobius(float in, float j, double peak) +static void mobius_params(TonemapContext *s, float j, double peak) { float a, b; - if (in <= j) - return in; - a = -j * j * (peak - 1.0f) / (j * j - 2.0f * j + peak); b = (j * j - 2.0f * j * peak + peak) / FFMAX(peak - 1.0f, 1e-6); - return (b * b + 2.0f * b * j + j * j) / (b - a) * (in + a) / (in + b); + s->mobius_a = a; + s->mobius_b = b; + s->mobius_k = (b * b + 2.0f * b * j + j * j) / (b - a); +} + +static float mobius(float in, float j, float a, float b, float k) +{ + if (in <= j) + return in; + + return k * (in + a) / (in + b); } #define MIX(x,y,a) (x) * (1 - (a)) + (y) * (a) @@ -126,7 +137,7 @@ static void tonemap(TonemapContext *s, AVFrame *out, const AVFrame *in, /* desaturate to prevent unnatural colors */ if (s->desat > 0) { - float luma = av_q2d(s->coeffs->cr) * *r_in + av_q2d(s->coeffs->cg) * *g_in + av_q2d(s->coeffs->cb) * *b_in; + float luma = s->cr * *r_in + s->cg * *g_in + s->cb * *b_in; float overbright = FFMAX(luma - s->desat, 1e-6) / FFMAX(luma, 1e-6); *r_out = MIX(*r_in, luma, overbright); *g_out = MIX(*g_in, luma, overbright); @@ -161,7 +172,7 @@ static void tonemap(TonemapContext *s, AVFrame *out, const AVFrame *in, sig = sig / (sig + s->param) * (peak + s->param) / peak; break; case TONEMAP_MOBIUS: - sig = mobius(sig, s->param, peak); + sig = mobius(sig, s->param, s->mobius_a, s->mobius_b, s->mobius_k); break; } @@ -251,6 +262,15 @@ static int filter_frame(AVFilterLink *link, AVFrame *in) s->desat = 0; } + /* compute what is the same for every pixel of the frame only once */ + if (s->desat > 0) { + s->cr = av_q2d(s->coeffs->cr); + s->cg = av_q2d(s->coeffs->cg); + s->cb = av_q2d(s->coeffs->cb); + } + if (s->tonemap == TONEMAP_MOBIUS) + mobius_params(s, s->param, peak); + /* do the tone map */ td.out = out; td.in = in; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
