[FFmpeg-cvslog] avfilter/vf_colorcorrect: add initial automatic filtering

Paul B Mahol git at videolan.org
Mon Aug 16 02:20:37 EEST 2021


ffmpeg | branch: master | Paul B Mahol <onemda at gmail.com> | Sun Aug 15 23:48:30 2021 +0200| [d42b49fc87e97bf0d95232868b69295333bd818f] | committer: Paul B Mahol

avfilter/vf_colorcorrect: add initial automatic filtering

> http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=d42b49fc87e97bf0d95232868b69295333bd818f
---

 doc/filters.texi              |   5 ++
 libavfilter/vf_colorcorrect.c | 123 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 126 insertions(+), 2 deletions(-)

diff --git a/doc/filters.texi b/doc/filters.texi
index 138cc85f8f..0c399f1d35 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -8224,6 +8224,11 @@ Default value is 0.
 @item saturation
 Set the amount of saturation. Allowed range is from -3.0 to 3.0.
 Default value is 1.
+
+ at item analyze
+If set to anything other than @code{manual} it will analyze every frame and use derived
+parameters for filtering output frame. Can be @code{manual} or @code{average}.
+Default value is @code{manual}.
 @end table
 
 @subsection Commands
diff --git a/libavfilter/vf_colorcorrect.c b/libavfilter/vf_colorcorrect.c
index 0105886124..1019d431ce 100644
--- a/libavfilter/vf_colorcorrect.c
+++ b/libavfilter/vf_colorcorrect.c
@@ -27,12 +27,19 @@
 #include "internal.h"
 #include "video.h"
 
+typedef enum AnalyzeMode {
+    MANUAL,
+    AVERAGE,
+    NB_ANALYZE
+} AnalyzeMode;
+
 typedef struct ColorCorrectContext {
     const AVClass *class;
 
     float rl, bl;
     float rh, bh;
     float saturation;
+    int analyze;
 
     int depth;
 
@@ -40,10 +47,80 @@ typedef struct ColorCorrectContext {
     int planeheight[4];
     int planewidth[4];
 
+    float (*analyzeret)[2];
+
+    int (*do_analyze)(AVFilterContext *s, void *arg,
+                      int jobnr, int nb_jobs);
     int (*do_slice)(AVFilterContext *s, void *arg,
                     int jobnr, int nb_jobs);
 } ColorCorrectContext;
 
+static int average_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
+{
+    ColorCorrectContext *s = ctx->priv;
+    AVFrame *frame = arg;
+    const int depth = s->depth;
+    const float max = (1 << depth) - 1;
+    const float imax = 1.f / max;
+    const int width = s->planewidth[1];
+    const int height = s->planeheight[1];
+    const int slice_start = (height * jobnr) / nb_jobs;
+    const int slice_end = (height * (jobnr + 1)) / nb_jobs;
+    const int ulinesize = frame->linesize[1];
+    const int vlinesize = frame->linesize[2];
+    const uint8_t *uptr = (const uint8_t *)frame->data[1] + slice_start * ulinesize;
+    const uint8_t *vptr = (const uint8_t *)frame->data[2] + slice_start * vlinesize;
+    int sum_u = 0, sum_v = 0;
+
+    for (int y = slice_start; y < slice_end; y++) {
+        for (int x = 0; x < width; x++) {
+            sum_u += uptr[x];
+            sum_v += vptr[x];
+        }
+
+        uptr += ulinesize;
+        vptr += vlinesize;
+    }
+
+    s->analyzeret[jobnr][0] = imax * sum_u / (float)((slice_end - slice_start) * width) - 0.5f;
+    s->analyzeret[jobnr][1] = imax * sum_v / (float)((slice_end - slice_start) * width) - 0.5f;
+
+    return 0;
+}
+
+static int average_slice16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
+{
+    ColorCorrectContext *s = ctx->priv;
+    AVFrame *frame = arg;
+    const int depth = s->depth;
+    const float max = (1 << depth) - 1;
+    const float imax = 1.f / max;
+    const int width = s->planewidth[1];
+    const int height = s->planeheight[1];
+    const int slice_start = (height * jobnr) / nb_jobs;
+    const int slice_end = (height * (jobnr + 1)) / nb_jobs;
+    const int ulinesize = frame->linesize[1] / 2;
+    const int vlinesize = frame->linesize[2] / 2;
+    const uint16_t *uptr = (const uint16_t *)frame->data[1] + slice_start * ulinesize;
+    const uint16_t *vptr = (const uint16_t *)frame->data[2] + slice_start * vlinesize;
+    int64_t sum_u = 0, sum_v = 0;
+
+    for (int y = slice_start; y < slice_end; y++) {
+        for (int x = 0; x < width; x++) {
+            sum_u += uptr[x];
+            sum_v += vptr[x];
+        }
+
+        uptr += ulinesize;
+        vptr += vlinesize;
+    }
+
+    s->analyzeret[jobnr][0] = imax * sum_u / (float)((slice_end - slice_start) * width) - 0.5f;
+    s->analyzeret[jobnr][1] = imax * sum_v / (float)((slice_end - slice_start) * width) - 0.5f;
+
+    return 0;
+}
+
 #define PROCESS()                            \
     float y = yptr[x * chroma_w] * imax;     \
     float u = uptr[x] * imax - .5f;          \
@@ -139,9 +216,26 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
 {
     AVFilterContext *ctx = inlink->dst;
     ColorCorrectContext *s = ctx->priv;
+    const int nb_threads = FFMIN(s->planeheight[1], ff_filter_get_nb_threads(ctx));
+
+    if (s->analyze) {
+        float bl = 0.f, rl = 0.f;
+
+        ff_filter_execute(ctx, s->do_analyze, frame, NULL, nb_threads);
+
+        for (int i = 0; i < nb_threads; i++) {
+            bl += s->analyzeret[i][0];
+            rl += s->analyzeret[i][0];
+        }
+
+        bl /= nb_threads;
+        rl /= nb_threads;
+
+        s->bl = -bl;
+        s->rl = -rl;
+    }
 
-    ff_filter_execute(ctx, s->do_slice, frame, NULL,
-                      FFMIN(s->planeheight[1], ff_filter_get_nb_threads(ctx)));
+    ff_filter_execute(ctx, s->do_slice, frame, NULL, nb_threads);
 
     return ff_filter_frame(ctx->outputs[0], frame);
 }
@@ -176,6 +270,20 @@ static av_cold int config_input(AVFilterLink *inlink)
     s->depth = desc->comp[0].depth;
     s->do_slice = s->depth <= 8 ? colorcorrect_slice8 : colorcorrect_slice16;
 
+    s->analyzeret = av_calloc(inlink->h, sizeof(*s->analyzeret));
+    if (!s->analyzeret)
+        return AVERROR(ENOMEM);
+
+    switch (s->analyze) {
+    case MANUAL:
+        break;
+    case AVERAGE:
+        s->do_analyze = s->depth <= 8 ? average_slice8 : average_slice16;
+        break;
+    default:
+        return AVERROR_BUG;
+    }
+
     s->chroma_w = 1 << desc->log2_chroma_w;
     s->chroma_h = 1 << desc->log2_chroma_h;
     s->planeheight[1] = s->planeheight[2] = AV_CEIL_RSHIFT(inlink->h, desc->log2_chroma_h);
@@ -186,6 +294,13 @@ static av_cold int config_input(AVFilterLink *inlink)
     return 0;
 }
 
+static av_cold void uninit(AVFilterContext *ctx)
+{
+    ColorCorrectContext *s = ctx->priv;
+
+    av_freep(&s->analyzeret);
+}
+
 static const AVFilterPad colorcorrect_inputs[] = {
     {
         .name           = "default",
@@ -214,6 +329,9 @@ static const AVOption colorcorrect_options[] = {
     { "rh", "set the red highlight spot",           OFFSET(rh), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF },
     { "bh", "set the blue highlight spot",          OFFSET(bh), AV_OPT_TYPE_FLOAT, {.dbl=0}, -1, 1, VF },
     { "saturation", "set the amount of saturation", OFFSET(saturation), AV_OPT_TYPE_FLOAT, {.dbl=1}, -3, 3, VF },
+    { "analyze", "set the analyze mode",            OFFSET(analyze), AV_OPT_TYPE_INT, {.i64=0}, 0, NB_ANALYZE, VF, "analyze" },
+    {   "manual",  "manually set options", 0, AV_OPT_TYPE_CONST, {.i64=MANUAL},  0, 0, VF, "analyze" },
+    {   "average", "use average pixels",   0, AV_OPT_TYPE_CONST, {.i64=AVERAGE}, 0, 0, VF, "analyze" },
     { NULL }
 };
 
@@ -225,6 +343,7 @@ const AVFilter ff_vf_colorcorrect = {
     .priv_size     = sizeof(ColorCorrectContext),
     .priv_class    = &colorcorrect_class,
     .query_formats = query_formats,
+    .uninit        = uninit,
     .inputs        = colorcorrect_inputs,
     .outputs       = colorcorrect_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,



More information about the ffmpeg-cvslog mailing list