[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