[FFmpeg-devel] avfilter: add colorhold filter

Submitted by Paul B Mahol on May 4, 2019, 2:13 p.m.

Details

Message ID 20190504141338.17834-1-onemda@gmail.com
State New
Headers show

Commit Message

Paul B Mahol May 4, 2019, 2:13 p.m.
Signed-off-by: Paul B Mahol <onemda@gmail.com>
---
 doc/filters.texi          |  18 +++++++
 libavfilter/Makefile      |   1 +
 libavfilter/allfilters.c  |   1 +
 libavfilter/vf_colorkey.c | 102 +++++++++++++++++++++++++++++++++++++-
 4 files changed, 121 insertions(+), 1 deletion(-)

Comments

Paul B Mahol May 11, 2019, 2:17 p.m.
On 5/4/19, Paul B Mahol <onemda@gmail.com> wrote:
> Signed-off-by: Paul B Mahol <onemda@gmail.com>
> ---
>  doc/filters.texi          |  18 +++++++
>  libavfilter/Makefile      |   1 +
>  libavfilter/allfilters.c  |   1 +
>  libavfilter/vf_colorkey.c | 102 +++++++++++++++++++++++++++++++++++++-
>  4 files changed, 121 insertions(+), 1 deletion(-)
>

7 days passed, nobody voted against. So I will apply this filter soon.
Nicolas George May 11, 2019, 2:29 p.m.
Paul B Mahol (12019-05-11):
> 7 days passed, nobody voted against. So I will apply this filter soon.

Two people have objected to the vote itself. That is more than one.
Please realize you are using intimidation techniques that have
absolutely no place in a project like this one. If you insist on
legitimizing an invalid vote, then I will act on the fake invalid vote
that I posted.

Ask, politely, that somebody review this patch. That is easy.
Paul B Mahol May 11, 2019, 3:20 p.m.
On 5/11/19, Nicolas George <george@nsup.org> wrote:
> Paul B Mahol (12019-05-11):
>> 7 days passed, nobody voted against. So I will apply this filter soon.
>
> Two people have objected to the vote itself. That is more than one.
> Please realize you are using intimidation techniques that have
> absolutely no place in a project like this one. If you insist on
> legitimizing an invalid vote, then I will act on the fake invalid vote
> that I posted.
>
> Ask, politely, that somebody review this patch. That is easy.
>

I will kindly and slowly going to apply this splendid patch very soon.

If you want to object this patch inclusion into main FFmpeg tree than
kindly review it.
Nicolas George May 11, 2019, 3:25 p.m.
Paul B Mahol (12019-05-11):
> I will kindly and slowly going to apply this splendid patch very soon.

By doing so, you will acknowledge that you do not respect the rules of
the project and of cooperation. As a consequence, you forfeit any right
to complain if these rules are not followed when it comes to you. And I
can assure you that I will not hesitate to make use of that forfeiture
to revert any patch from you that I do not like.

At this point, I almost hope that you will follow through and give me
that power on a platter. But I will only say this: if you spent half as
much effort to try to work WITH the people here as you spent to try to
work AGAINST us, you would achieve much more.

> If you want to object this patch inclusion into main FFmpeg tree than
> kindly review it.

You have a lot of nerve to ask something like that from somebody you
insulted a few days ago. I refuse. And I will revert this patch if it is
applied without following the rules.
Timo Rothenpieler May 11, 2019, 3:51 p.m.
On 04.05.2019 16:13, Paul B Mahol wrote:
> Signed-off-by: Paul B Mahol <onemda@gmail.com>
> ---
>   doc/filters.texi          |  18 +++++++
>   libavfilter/Makefile      |   1 +
>   libavfilter/allfilters.c  |   1 +
>   libavfilter/vf_colorkey.c | 102 +++++++++++++++++++++++++++++++++++++-
>   4 files changed, 121 insertions(+), 1 deletion(-)
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index cd82869849..1ac37a32b8 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -6723,6 +6723,24 @@ ffmpeg -i background.png -i video.mp4 -filter_complex "[1:v]colorkey=0x3BBD1E:0.
>   @end example
>   @end itemize
>   
> +@section colorhold
> +Remove all color information for all RGB colors except for certain one.
> +
> +The filter accepts the following options:
> +
> +@table @option
> +@item color
> +The color which will not be replaced with neutral gray.
> +
> +@item similarity
> +Similarity percentage with the above color.
> +0.01 matches only the exact key color, while 1.0 matches everything.
> +
> +@item blend
> +Blend percentage. 0.0 makes pixels fully gray.
> +Higher values result in more preserved color.
> +@end table
> +
>   @section colorlevels
>   
>   Adjust video input frames using levels.
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 59d12ce069..b41304d480 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -179,6 +179,7 @@ OBJS-$(CONFIG_COLORCHANNELMIXER_FILTER)      += vf_colorchannelmixer.o
>   OBJS-$(CONFIG_COLORKEY_FILTER)               += vf_colorkey.o
>   OBJS-$(CONFIG_COLORKEY_OPENCL_FILTER)        += vf_colorkey_opencl.o opencl.o \
>                                                   opencl/colorkey.o
> +OBJS-$(CONFIG_COLORHOLD_FILTER)              += vf_colorkey.o
>   OBJS-$(CONFIG_COLORLEVELS_FILTER)            += vf_colorlevels.o
>   OBJS-$(CONFIG_COLORMATRIX_FILTER)            += vf_colormatrix.o
>   OBJS-$(CONFIG_COLORSPACE_FILTER)             += vf_colorspace.o colorspace.o colorspacedsp.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index ae725cb0e0..9bdfa7d1bc 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -167,6 +167,7 @@ extern AVFilter ff_vf_colorbalance;
>   extern AVFilter ff_vf_colorchannelmixer;
>   extern AVFilter ff_vf_colorkey;
>   extern AVFilter ff_vf_colorkey_opencl;
> +extern AVFilter ff_vf_colorhold;
>   extern AVFilter ff_vf_colorlevels;
>   extern AVFilter ff_vf_colormatrix;
>   extern AVFilter ff_vf_colorspace;
> diff --git a/libavfilter/vf_colorkey.c b/libavfilter/vf_colorkey.c
> index 3d65e59d42..b546cd4122 100644
> --- a/libavfilter/vf_colorkey.c
> +++ b/libavfilter/vf_colorkey.c
> @@ -34,6 +34,9 @@ typedef struct ColorkeyContext {
>       uint8_t colorkey_rgba[4];
>       float similarity;
>       float blend;
> +
> +    int (*do_slice)(AVFilterContext *ctx, void *arg,
> +                    int jobnr, int nb_jobs);
>   } ColorkeyContext;
>   
>   static uint8_t do_colorkey_pixel(ColorkeyContext *ctx, uint8_t r, uint8_t g, uint8_t b)
> @@ -77,15 +80,65 @@ static int do_colorkey_slice(AVFilterContext *avctx, void *arg, int jobnr, int n
>       return 0;
>   }
>   
> +static int do_colorhold_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
> +{
> +    AVFrame *frame = arg;
> +
> +    const int slice_start = (frame->height * jobnr) / nb_jobs;
> +    const int slice_end = (frame->height * (jobnr + 1)) / nb_jobs;
> +
> +    ColorkeyContext *ctx = avctx->priv;
> +
> +    int o, x, y;
> +
> +    for (y = slice_start; y < slice_end; ++y) {
> +        for (x = 0; x < frame->width; ++x) {
> +            int t, r, g, b;

Nit, but I'd prefer to not mix "declaration at top" with intermittent 
declaration of vars.
In this case, I'd just put o down here as well.

> +            o = frame->linesize[0] * y + x * 4;
> +            r = frame->data[0][o + ctx->co[0]];
> +            g = frame->data[0][o + ctx->co[1]];
> +            b = frame->data[0][o + ctx->co[2]];
> +
> +            t = do_colorkey_pixel(ctx, r, g, b);
> +
> +            if (t > 0) {
> +                int a = (r + g + b) / 3;
> +                int rt = 255 - t;
> +
> +                frame->data[0][o + ctx->co[0]] = (a * t + r * rt + 127) >> 8;
> +                frame->data[0][o + ctx->co[1]] = (a * t + g * rt + 127) >> 8;
> +                frame->data[0][o + ctx->co[2]] = (a * t + b * rt + 127) >> 8;
> +            }
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +static av_cold int init_filter(AVFilterContext *avctx)
> +{
> +    ColorkeyContext *ctx = avctx->priv;
> +
> +    if (!strcmp(avctx->filter->name, "colorkey")) {
> +        ctx->do_slice = do_colorkey_slice;
> +    } else {
> +        ctx->do_slice = do_colorhold_slice;
> +    }
> +
> +    return 0;
> +}
> +
>   static int filter_frame(AVFilterLink *link, AVFrame *frame)
>   {
>       AVFilterContext *avctx = link->dst;
> +    ColorkeyContext *ctx = avctx->priv;
>       int res;
>   
>       if (res = av_frame_make_writable(frame))
>           return res;
>   
> -    if (res = avctx->internal->execute(avctx, do_colorkey_slice, frame, NULL, FFMIN(frame->height, ff_filter_get_nb_threads(avctx))))
> +    if (res = avctx->internal->execute(avctx, ctx->do_slice, frame, NULL, FFMIN(frame->height, ff_filter_get_nb_threads(avctx))))
>           return res;
>   
>       return ff_filter_frame(avctx->outputs[0], frame);
> @@ -148,6 +201,8 @@ static const AVFilterPad colorkey_outputs[] = {
>   #define OFFSET(x) offsetof(ColorkeyContext, x)
>   #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
>   
> +#if CONFIG_COLORKEY_FILTER

Shouldn't this also cover at least colorkey_inputs and colorkey_outputs, 
since they'd be unused otherwise?

>   static const AVOption colorkey_options[] = {
>       { "color", "set the colorkey key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS },
>       { "similarity", "set the colorkey similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.01, 1.0, FLAGS },
> @@ -163,7 +218,52 @@ AVFilter ff_vf_colorkey = {
>       .priv_size     = sizeof(ColorkeyContext),
>       .priv_class    = &colorkey_class,
>       .query_formats = query_formats,
> +    .init          = init_filter,
>       .inputs        = colorkey_inputs,
>       .outputs       = colorkey_outputs,
>       .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
>   };
> +
> +#endif /* CONFIG_COLORKEY_FILTER */
> +#if CONFIG_COLORHOLD_FILTER
> +
> +static const AVFilterPad colorhold_inputs[] = {
> +    {
> +        .name = "default",
> +        .type = AVMEDIA_TYPE_VIDEO,
> +        .filter_frame = filter_frame,
> +    },
> +    { NULL }
> +};
> +
> +static const AVFilterPad colorhold_outputs[] = {
> +    {
> +        .name = "default",
> +        .type = AVMEDIA_TYPE_VIDEO,
> +        .config_props = config_output,
> +    },
> +    { NULL }
> +};
> +
> +static const AVOption colorhold_options[] = {
> +    { "color", "set the colorhold key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS },
> +    { "similarity", "set the colorhold similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.01, 1.0, FLAGS },
> +    { "blend", "set the colorhold blend value", OFFSET(blend), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, 0.0, 1.0, FLAGS },
> +    { NULL }
> +};
> +
> +AVFILTER_DEFINE_CLASS(colorhold);
> +
> +AVFilter ff_vf_colorhold = {
> +    .name          = "colorhold",
> +    .description   = NULL_IF_CONFIG_SMALL("Turns a certain color range into gray. Operates on RGB colors."),
> +    .priv_size     = sizeof(ColorkeyContext),
> +    .priv_class    = &colorhold_class,
> +    .query_formats = query_formats,
> +    .init          = init_filter,
> +    .inputs        = colorhold_inputs,
> +    .outputs       = colorhold_outputs,
> +    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
> +};
> +
> +#endif /* CONFIG_COLORHOLD_FILTER */
> 

Missing a version bump.

LGTM to me otherwise.

Patch hide | download patch | download mbox

diff --git a/doc/filters.texi b/doc/filters.texi
index cd82869849..1ac37a32b8 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -6723,6 +6723,24 @@  ffmpeg -i background.png -i video.mp4 -filter_complex "[1:v]colorkey=0x3BBD1E:0.
 @end example
 @end itemize
 
+@section colorhold
+Remove all color information for all RGB colors except for certain one.
+
+The filter accepts the following options:
+
+@table @option
+@item color
+The color which will not be replaced with neutral gray.
+
+@item similarity
+Similarity percentage with the above color.
+0.01 matches only the exact key color, while 1.0 matches everything.
+
+@item blend
+Blend percentage. 0.0 makes pixels fully gray.
+Higher values result in more preserved color.
+@end table
+
 @section colorlevels
 
 Adjust video input frames using levels.
diff --git a/libavfilter/Makefile b/libavfilter/Makefile
index 59d12ce069..b41304d480 100644
--- a/libavfilter/Makefile
+++ b/libavfilter/Makefile
@@ -179,6 +179,7 @@  OBJS-$(CONFIG_COLORCHANNELMIXER_FILTER)      += vf_colorchannelmixer.o
 OBJS-$(CONFIG_COLORKEY_FILTER)               += vf_colorkey.o
 OBJS-$(CONFIG_COLORKEY_OPENCL_FILTER)        += vf_colorkey_opencl.o opencl.o \
                                                 opencl/colorkey.o
+OBJS-$(CONFIG_COLORHOLD_FILTER)              += vf_colorkey.o
 OBJS-$(CONFIG_COLORLEVELS_FILTER)            += vf_colorlevels.o
 OBJS-$(CONFIG_COLORMATRIX_FILTER)            += vf_colormatrix.o
 OBJS-$(CONFIG_COLORSPACE_FILTER)             += vf_colorspace.o colorspace.o colorspacedsp.o
diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
index ae725cb0e0..9bdfa7d1bc 100644
--- a/libavfilter/allfilters.c
+++ b/libavfilter/allfilters.c
@@ -167,6 +167,7 @@  extern AVFilter ff_vf_colorbalance;
 extern AVFilter ff_vf_colorchannelmixer;
 extern AVFilter ff_vf_colorkey;
 extern AVFilter ff_vf_colorkey_opencl;
+extern AVFilter ff_vf_colorhold;
 extern AVFilter ff_vf_colorlevels;
 extern AVFilter ff_vf_colormatrix;
 extern AVFilter ff_vf_colorspace;
diff --git a/libavfilter/vf_colorkey.c b/libavfilter/vf_colorkey.c
index 3d65e59d42..b546cd4122 100644
--- a/libavfilter/vf_colorkey.c
+++ b/libavfilter/vf_colorkey.c
@@ -34,6 +34,9 @@  typedef struct ColorkeyContext {
     uint8_t colorkey_rgba[4];
     float similarity;
     float blend;
+
+    int (*do_slice)(AVFilterContext *ctx, void *arg,
+                    int jobnr, int nb_jobs);
 } ColorkeyContext;
 
 static uint8_t do_colorkey_pixel(ColorkeyContext *ctx, uint8_t r, uint8_t g, uint8_t b)
@@ -77,15 +80,65 @@  static int do_colorkey_slice(AVFilterContext *avctx, void *arg, int jobnr, int n
     return 0;
 }
 
+static int do_colorhold_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
+{
+    AVFrame *frame = arg;
+
+    const int slice_start = (frame->height * jobnr) / nb_jobs;
+    const int slice_end = (frame->height * (jobnr + 1)) / nb_jobs;
+
+    ColorkeyContext *ctx = avctx->priv;
+
+    int o, x, y;
+
+    for (y = slice_start; y < slice_end; ++y) {
+        for (x = 0; x < frame->width; ++x) {
+            int t, r, g, b;
+
+            o = frame->linesize[0] * y + x * 4;
+            r = frame->data[0][o + ctx->co[0]];
+            g = frame->data[0][o + ctx->co[1]];
+            b = frame->data[0][o + ctx->co[2]];
+
+            t = do_colorkey_pixel(ctx, r, g, b);
+
+            if (t > 0) {
+                int a = (r + g + b) / 3;
+                int rt = 255 - t;
+
+                frame->data[0][o + ctx->co[0]] = (a * t + r * rt + 127) >> 8;
+                frame->data[0][o + ctx->co[1]] = (a * t + g * rt + 127) >> 8;
+                frame->data[0][o + ctx->co[2]] = (a * t + b * rt + 127) >> 8;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static av_cold int init_filter(AVFilterContext *avctx)
+{
+    ColorkeyContext *ctx = avctx->priv;
+
+    if (!strcmp(avctx->filter->name, "colorkey")) {
+        ctx->do_slice = do_colorkey_slice;
+    } else {
+        ctx->do_slice = do_colorhold_slice;
+    }
+
+    return 0;
+}
+
 static int filter_frame(AVFilterLink *link, AVFrame *frame)
 {
     AVFilterContext *avctx = link->dst;
+    ColorkeyContext *ctx = avctx->priv;
     int res;
 
     if (res = av_frame_make_writable(frame))
         return res;
 
-    if (res = avctx->internal->execute(avctx, do_colorkey_slice, frame, NULL, FFMIN(frame->height, ff_filter_get_nb_threads(avctx))))
+    if (res = avctx->internal->execute(avctx, ctx->do_slice, frame, NULL, FFMIN(frame->height, ff_filter_get_nb_threads(avctx))))
         return res;
 
     return ff_filter_frame(avctx->outputs[0], frame);
@@ -148,6 +201,8 @@  static const AVFilterPad colorkey_outputs[] = {
 #define OFFSET(x) offsetof(ColorkeyContext, x)
 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
 
+#if CONFIG_COLORKEY_FILTER
+
 static const AVOption colorkey_options[] = {
     { "color", "set the colorkey key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS },
     { "similarity", "set the colorkey similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.01, 1.0, FLAGS },
@@ -163,7 +218,52 @@  AVFilter ff_vf_colorkey = {
     .priv_size     = sizeof(ColorkeyContext),
     .priv_class    = &colorkey_class,
     .query_formats = query_formats,
+    .init          = init_filter,
     .inputs        = colorkey_inputs,
     .outputs       = colorkey_outputs,
     .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
 };
+
+#endif /* CONFIG_COLORKEY_FILTER */
+#if CONFIG_COLORHOLD_FILTER
+
+static const AVFilterPad colorhold_inputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_VIDEO,
+        .filter_frame = filter_frame,
+    },
+    { NULL }
+};
+
+static const AVFilterPad colorhold_outputs[] = {
+    {
+        .name = "default",
+        .type = AVMEDIA_TYPE_VIDEO,
+        .config_props = config_output,
+    },
+    { NULL }
+};
+
+static const AVOption colorhold_options[] = {
+    { "color", "set the colorhold key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS },
+    { "similarity", "set the colorhold similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.01, 1.0, FLAGS },
+    { "blend", "set the colorhold blend value", OFFSET(blend), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, 0.0, 1.0, FLAGS },
+    { NULL }
+};
+
+AVFILTER_DEFINE_CLASS(colorhold);
+
+AVFilter ff_vf_colorhold = {
+    .name          = "colorhold",
+    .description   = NULL_IF_CONFIG_SMALL("Turns a certain color range into gray. Operates on RGB colors."),
+    .priv_size     = sizeof(ColorkeyContext),
+    .priv_class    = &colorhold_class,
+    .query_formats = query_formats,
+    .init          = init_filter,
+    .inputs        = colorhold_inputs,
+    .outputs       = colorhold_outputs,
+    .flags         = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
+};
+
+#endif /* CONFIG_COLORHOLD_FILTER */