[FFmpeg-devel] [PATCH] lavfi: add blackdetect filter

Clément Bœsch ubitux at gmail.com
Sat Mar 3 00:54:40 CET 2012


On Fri, Mar 02, 2012 at 04:46:20PM +0100, Stefano Sabatini wrote:
> Address trac ticket #901.
> ---
>  doc/filters.texi             |   50 ++++++++++
>  libavfilter/Makefile         |    1 +
>  libavfilter/allfilters.c     |    1 +
>  libavfilter/vf_blackdetect.c |  209 ++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 261 insertions(+), 0 deletions(-)
>  create mode 100644 libavfilter/vf_blackdetect.c
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 238401a..b5e8954 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -761,6 +761,56 @@ video, use the command:
>  ass=sub.ass
>  @end example
>  
> + at section blackdetect
> +

Global comment: couldn't we have a "unified color" detector instead? So we
could detect bright frames for instance, or full green ones.

> +Detect black video intervals that are (almost) completely black. Can
> +be useful to detect chapter transitions or commercials. Output lines
> +consist of the frame number of the detected frame, the start, end and
> +duration of the detected black interval expressed in seconds.
> +
> +In order to display the output lines, you need to set the loglevel at
> +least to the AV_LOG_INFO value.
> +
> +This filter accepts a list of options in the form of
> + at var{key}=@var{value} pairs separated by ":". A description of the
> +accepted options follows.
> +
> + at table @option
> + at item min_black_duration, d
> +Set the minimum detected black duration expressed in seconds. It must
> +be a non-negative floating point number.
> +
> +Default value is 2.0 seconds.
> +
> + at item picture_black_ratio_th, pic_th
> +Set the threshold for considering a picture "black".
> +Express the minimum value for the ratio:
> + at example
> + at var{nb_black_pixels} / @var{nb_pixels}
> + at end example
> +
> +for which a picture is considered black.
> +Default value is 0.98.
> +
> + at item pixel_black_th, pix_th
> +Set the threshold for considering a pixel "black".
> +
> +Express the maximum pixel luminance value for which a pixel is
> +considered "black". The value is scaled according to the pixel format
> +luminance range, following the equation below:
> + at example
> + at var{absolute_threshold} = @var{minimum_luminance_value} + @var{pixel_black_th} * @var{luminance_range}
> + at end example
> +

The luminance_range is a nice thing; you might want to comment on it just
a bit.

> +Default value is 0.10.
> + at end table
> +
> +The following example sets the maximum pixel threshold to the minimum
> +value, and detects only black intervals of 2 or more seconds:
> + at example
> +blackdetect=d=2:pix_th=0.00
> + at end example
> +
>  @section blackframe
>  
>  Detect frames that are (almost) completely black. Can be useful to
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 6e7379c..df3a27f 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -48,6 +48,7 @@ OBJS-$(CONFIG_ABUFFERSINK_FILTER)            += sink_buffer.o
>  OBJS-$(CONFIG_ANULLSINK_FILTER)              += asink_anullsink.o
>  
>  OBJS-$(CONFIG_ASS_FILTER)                    += vf_ass.o
> +OBJS-$(CONFIG_BLACKDETECT_FILTER)            += vf_blackdetect.o
>  OBJS-$(CONFIG_BLACKFRAME_FILTER)             += vf_blackframe.o
>  OBJS-$(CONFIG_BOXBLUR_FILTER)                += vf_boxblur.o
>  OBJS-$(CONFIG_COPY_FILTER)                   += vf_copy.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 487738a..f6230be 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -56,6 +56,7 @@ void avfilter_register_all(void)
>      REGISTER_FILTER (ANULLSINK,   anullsink,   asink);
>  
>      REGISTER_FILTER (ASS,         ass,  vf);
> +    REGISTER_FILTER (BLACKDETECT, blackdetect, vf);
>      REGISTER_FILTER (BLACKFRAME,  blackframe,  vf);
>      REGISTER_FILTER (BOXBLUR,     boxblur,     vf);
>      REGISTER_FILTER (COPY,        copy,        vf);
> diff --git a/libavfilter/vf_blackdetect.c b/libavfilter/vf_blackdetect.c
> new file mode 100644
> index 0000000..63336cd
> --- /dev/null
> +++ b/libavfilter/vf_blackdetect.c
> @@ -0,0 +1,209 @@
> +/*
> + * Copyright (c) 2012 Stefano Sabatini
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * Video black detector, loosely based on blackframe with extended
> + * syntax and features
> + */
> +
> +#include <float.h>
> +#include "libavutil/opt.h"
> +#include "libavutil/timestamp.h"
> +#include "avfilter.h"
> +#include "internal.h"
> +
> +typedef struct {
> +    const AVClass *class;
> +    double  min_black_duration_time; ///< minimum duration of detected black, in seconds
> +    double  min_black_duration;      ///< minimum duration of detected black, in seconds

One of the comment looks wrong

> +    int64_t black_start;             ///< pts start time of the first black picture
> +    int64_t black_end;               ///< pts end time of the last black picture
> +    int black_started;
> +
> +    double       picture_black_ratio_th;
> +    double       pixel_black_th;
> +    unsigned int pixel_black_th_i;
> +
> +    unsigned int frame_count;       ///< frame number
> +    unsigned int nb_black_pixels;   ///< number of black pixels counted so far
> +} BlackDetectContext;
> +
> +#define OFFSET(x) offsetof(BlackDetectContext, x)
> +static const AVOption blackdetect_options[] = {
> +    { "d",                  "set minimum detected black duration in seconds",  OFFSET(min_black_duration_time), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 0, FLT_MAX},
> +    { "min_black_duration", "set minimum detected black duration in seconds",  OFFSET(min_black_duration_time), AV_OPT_TYPE_DOUBLE, {.dbl=2}, 0, FLT_MAX},

Why not just "duration" for the long version? These option are in the
filter string scope, I don't think they need a long namespace like this.

Also, why FLT_MAX and not DBL_MAX? (which BTW is used in only one single
place).

> +    { "picture_black_ratio_th", "set the picture black ratio threshold",  OFFSET(picture_black_ratio_th),  AV_OPT_TYPE_DOUBLE, {.dbl=.98}, 0, 1},
> +    { "pic_th",                 "set the picture black ratio threshold", OFFSET(picture_black_ratio_th), AV_OPT_TYPE_DOUBLE,   {.dbl=.98}, 0, 1},

Weird align around OFFSET()

> +    { "pixel_black_th", "set the pixel black threshold", OFFSET(pixel_black_th), AV_OPT_TYPE_DOUBLE, {.dbl=.10}, 0, 1},
> +    { "pix_th",         "set the pixel black threshold", OFFSET(pixel_black_th), AV_OPT_TYPE_DOUBLE, {.dbl=.10}, 0, 1},
> +    { NULL },
> +};
> +
> +static const char *blackdetect_get_name(void *ctx)
> +{
> +    return "blackdetect";
> +}
> +
> +static const AVClass blackdetect_class = {
> +    .class_name = "BlackDetectContext",
> +    .item_name  = blackdetect_get_name,
> +    .option     = blackdetect_options,
> +};
> +
> +#define YUVJ_FORMATS \
> +    PIX_FMT_YUVJ420P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ444P, PIX_FMT_YUVJ440P
> +
> +static enum PixelFormat yuvj_formats[] = {
> +    YUVJ_FORMATS, PIX_FMT_NONE
> +};
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    static const enum PixelFormat pix_fmts[] = {
> +        PIX_FMT_YUV410P, PIX_FMT_YUV420P, PIX_FMT_GRAY8, PIX_FMT_NV12,
> +        PIX_FMT_NV21, PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV411P,
> +        YUVJ_FORMATS,
> +        PIX_FMT_NONE
> +    };
> +
> +    avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
> +    return 0;
> +}
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> +{
> +    int ret;
> +    BlackDetectContext *blackdetect = ctx->priv;
> +
> +    blackdetect->class = &blackdetect_class;
> +    av_opt_set_defaults(blackdetect);
> +
> +    if ((ret = av_set_options_string(blackdetect, args, "=", ":")) < 0) {
> +        av_log(ctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args);
> +        return ret;
> +    }
> +
> +    return 0;
> +}
> +
> +static int config_input(AVFilterLink *inlink)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    BlackDetectContext *blackdetect = ctx->priv;
> +
> +    blackdetect->min_black_duration =
> +        blackdetect->min_black_duration_time / av_q2d(inlink->time_base);
> +
> +    blackdetect->pixel_black_th_i = ff_fmt_is_in(inlink->format, yuvj_formats) ?
> +        blackdetect->pixel_black_th * 255 :
> +        16 + blackdetect->pixel_black_th * (235 - 16);

This is all about the luminance range, right?

> +
> +    av_log(blackdetect, AV_LOG_INFO,
> +           "min_black_duration:%s pixel_black_th:%f pixel_black_th_i:%d picture_black_ratio_th:%f\n",
> +           av_ts2timestr(blackdetect->min_black_duration, &inlink->time_base),
> +           blackdetect->pixel_black_th, blackdetect->pixel_black_th_i,
> +           blackdetect->picture_black_ratio_th);
> +    return 0;
> +}
> +
> +static void draw_slice(AVFilterLink *inlink, int y, int h, int slice_dir)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    BlackDetectContext *blackdetect = ctx->priv;
> +    AVFilterBufferRef *picref = inlink->cur_buf;
> +    int x, i;
> +    uint8_t *p = picref->data[0] + y * picref->linesize[0];
> +

const uint8_t *p?

[...]

-- 
Clément B.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 490 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20120303/8b859dea/attachment.asc>


More information about the ffmpeg-devel mailing list