[FFmpeg-devel] [PATCH] Port mp=eq/eq2 to FFmpeg

Stefano Sabatini stefasab at gmail.com
Fri Jan 23 15:35:48 CET 2015


On date Thursday 2015-01-22 01:38:11 +0530, Arwa Arif encoded:
> On Thu, Jan 22, 2015 at 12:09 AM, Paul B Mahol <onemda at gmail.com> wrote:
[...]
> From 703cc1887903c2868537e19e99b76927bec07884 Mon Sep 17 00:00:00 2001
> From: Arwa Arif <arwaarif1994 at gmail.com>
> Date: Mon, 19 Jan 2015 03:56:48 +0530
> Subject: [PATCH] Port mp=eq/eq2 to FFmpeg
> 

> Code adapted from James Darnley's previous commits

Code adapted from James Darnley's port.

There are no related commits in the FFmpeg master repo.

> ---
>  configure                |    1 +
>  doc/filters.texi         |   44 ++++++++
>  libavfilter/Makefile     |    1 +
>  libavfilter/allfilters.c |    1 +
>  libavfilter/vf_eq.c      |  282 ++++++++++++++++++++++++++++++++++++++++++++++
>  libavfilter/vf_eq.h      |   63 +++++++++++
>  libavfilter/x86/Makefile |    1 +
>  libavfilter/x86/vf_eq.c  |   94 ++++++++++++++++
>  8 files changed, 487 insertions(+)
>  create mode 100644 libavfilter/vf_eq.c
>  create mode 100644 libavfilter/vf_eq.h
>  create mode 100644 libavfilter/x86/vf_eq.c
> 
> diff --git a/configure b/configure
> index c73562b..138852e 100755
> --- a/configure
> +++ b/configure
> @@ -2579,6 +2579,7 @@ delogo_filter_deps="gpl"
>  deshake_filter_select="pixelutils"
>  drawtext_filter_deps="libfreetype"
>  ebur128_filter_deps="gpl"
> +eq_filter_deps="gpl"
>  flite_filter_deps="libflite"
>  frei0r_filter_deps="frei0r dlopen"
>  frei0r_src_filter_deps="frei0r dlopen"
> diff --git a/doc/filters.texi b/doc/filters.texi
> index d7b2273..70e0557 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -4320,6 +4320,50 @@ edgedetect=mode=colormix:high=0
>  @end example
>  @end itemize
>  
> + at section eq

> +Equalizer that uses lookup tables (very slow), allowing gamma correction
> +in addition to simple brightness and contrast adjustment.

This is not fitting (it's not always using LUTs). Also tells what the
filter does, not what the filter is, as in the rest of the filters
documentation. Something like this:

Set brightness, contrast, saturation and gamma adjustment.

> +
> +The filter accepts the following options:
> +
> + at table @option
> + at item brightness
> +Set the brightness value. It accepts a float value in range @code{-1.0} to
> + at code{1.0}. The default value is @code{0.0}.
> +
> + at item contrast
> +Set the contrast value. It accepts a float value in range @code{-2.0} to
> + at code{2.0}. The default value is @code{0.0}.
> +
> + at item gamma
> +Set the gamma value. It accepts a float value in range @code{0.1} to @code{10.0}.
> +The default value is @code{1.0}.
> +
> + at item gamma_y
> +Set the gamma value for the luma plane. It accepts a float value in range
> + at code{0.1} to @code{10.0}. The default value is @code{1.0}.
> +
> + at item gamma_u
> +Set the gamma value for 1st chroma plane. It accepts a float value in range
> + at code{0.1} to @code{10.0}. The default value is @code{1.0}.
> +
> + at item gamma_v
> +Set the gamma value for 2nd chroma plane. It accepts a float value in range
> + at code{0.1} to @code{10.0}. The default value is @code{1.0}.
> +
> + at item saturation
> +Set the saturation value. It accepts a float value in range @code{0.0} to
> + at code{3.0}. The default value is @code{1.0}.
> +
> + at item weight

probably gamma_weight it's better, to make clear it is related to
gamma correction.

> +Can be used to reduce the effect of a high gamma value on bright image areas,
> +e.g. keep them from getting overamplified and just plain white. It accepts a
> +float value in range @code{0.0} to @code{1.0}.A value of @code{0.0} turns the
> +gamma correction all the way down while @code{1.0} leaves it at its full strength.
> +Default is @code{1.0}.
> +
> + at end table
> +
>  @section extractplanes
>  
>  Extract color channel components from input video stream into
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index e43d76d..8e94033 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -116,6 +116,7 @@ OBJS-$(CONFIG_DRAWGRID_FILTER)               += vf_drawbox.o
>  OBJS-$(CONFIG_DRAWTEXT_FILTER)               += vf_drawtext.o
>  OBJS-$(CONFIG_ELBG_FILTER)                   += vf_elbg.o
>  OBJS-$(CONFIG_EDGEDETECT_FILTER)             += vf_edgedetect.o
> +OBJS-$(CONFIG_EQ_FILTER)                     += vf_eq.o
>  OBJS-$(CONFIG_EXTRACTPLANES_FILTER)          += vf_extractplanes.o
>  OBJS-$(CONFIG_FADE_FILTER)                   += vf_fade.o
>  OBJS-$(CONFIG_FIELD_FILTER)                  += vf_field.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index 381da4f..db34cb9 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -132,6 +132,7 @@ void avfilter_register_all(void)
>      REGISTER_FILTER(DRAWTEXT,       drawtext,       vf);
>      REGISTER_FILTER(EDGEDETECT,     edgedetect,     vf);
>      REGISTER_FILTER(ELBG,           elbg,           vf);
> +    REGISTER_FILTER(EQ,             eq,             vf);
>      REGISTER_FILTER(EXTRACTPLANES,  extractplanes,  vf);
>      REGISTER_FILTER(FADE,           fade,           vf);
>      REGISTER_FILTER(FIELD,          field,          vf);
> diff --git a/libavfilter/vf_eq.c b/libavfilter/vf_eq.c
> new file mode 100644
> index 0000000..fa4a2ea
> --- /dev/null
> +++ b/libavfilter/vf_eq.c
> @@ -0,0 +1,282 @@
> +/*
> + * Original MPlayer filters by Richard Felker, Hampa Hug, Daniel Moreno,
> + * and Michael Niedermeyer.
> + *
> + * Copyright (c) 2014 James Darnley <james.darnley at gmail.com>
> + * Copyright (c) 2015 Arwa Arif <arwaarif1994 at gmail.com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU 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
> + * very simple video equalizer
> + */
> +
> +/**
> + * TODO:
> + * - Add support to process_command
> + */
> +
> +#include "libavfilter/internal.h"
> +#include "libavutil/common.h"
> +#include "libavutil/imgutils.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/pixdesc.h"
> +#include "vf_eq.h"
> +
> +static void create_lut(EQParameters *param)
> +{
> +    int i;
> +    double g = param->gamma;
> +
> +    g = 1.0 / g;
> +
> +    for (i = 0; i < 256; i++) {
> +        double v = i / 255.0;
> +        v = param->contrast * (v - 0.5) + 0.5 + param->brightness;
> +
> +        if (v <= 0.0)
> +            param->lut[i] = 0;
> +        else {
> +            v = v * (1.0 - param->weight) + pow(v, g) * param->weight;
> +
> +            if (v >= 1.0)
> +                param->lut[i] = 255;
> +            else
> +                param->lut[i] = 256.0 * v;
> +        }
> +    }
> +
> +    param->lut_clean = 1;
> +}
> +
> +static void apply_lut(EQParameters *param, uint8_t *dst, int dst_stride,
> +                      uint8_t *src, int src_stride, int w, int h)
> +{
> +    int x, y;
> +
> +    if (!param->lut_clean)
> +        create_lut(param);
> +
> +    for (y = 0; y < h; y++) {
> +        for (x = 0; x < w; x++) {
> +            dst[y*dst_stride+x] = param->lut[src[y*src_stride+x]];
> +        }
> +    }
> +}
> +
> +static void process_c(EQParameters *param, uint8_t *dst, int dst_stride,
> +                      uint8_t *src, int src_stride, int w, int h)
> +{
> +    int x, y, pel;
> +
> +    for (y = 0; y < h; y++) {
> +        for (x = 0; x < w; x++) {
> +            pel = ((src[y * src_stride + x] * param->c) >> 16) + param->b;
> +
> +            if (pel & 768)
> +                pel = (-pel) >> 31;
> +
> +            dst[y * dst_stride + x] = pel;
> +        }
> +    }
> +}
> +
> +static void check_values(EQParameters *param, EQContext *eq)
> +{
> +    if (param->contrast == 1.0 && param->brightness == 0.0 && param->gamma == 1.0)
> +        param->adjust = NULL;
> +    else if (param->gamma == 1.0)
> +        param->adjust = eq->process;
> +    else
> +        param->adjust = apply_lut;
> +}
> +
> +static void set_contrast(EQContext *eq)
> +{
> +    eq->param[0].contrast = eq->contrast;
> +    eq->param[0].lut_clean = 0;
> +    check_values(&eq->param[0], eq);
> +}
> +
> +static void set_brightness(EQContext *eq)
> +{
> +    eq->param[0].brightness = eq->brightness;
> +    eq->param[0].lut_clean = 0;
> +    check_values(&eq->param[0], eq);
> +}
> +
> +static void set_gamma(EQContext *eq)
> +{
> +    int i;
> +    eq->param[0].gamma = eq->gamma * eq->gamma_u;
> +    eq->param[1].gamma = sqrt(eq->gamma_y / eq->gamma_u);
> +    eq->param[2].gamma = sqrt(eq->gamma_v / eq->gamma_u);
> +
> +    for (i = 0; i < 3; i++) {
> +        eq->param[i].weight = eq->weight;
> +        eq->param[i].lut_clean = 0;
> +        check_values(&eq->param[i], eq);
> +    }
> +}
> +
> +static void set_saturation(EQContext *eq)
> +{
> +    int i;
> +    for (i = 1; i < 3; i++) {
> +        eq->param[i].contrast = eq->saturation;
> +        eq->param[i].lut_clean = 0;
> +        check_values(&eq->param[i], eq);
> +    }
> +}
> +
> +static int initialize(AVFilterContext *ctx)
> +{
> +    EQContext *eq = ctx->priv;
> +    int i;
> +

> +    set_gamma(eq);
> +    set_contrast(eq);
> +    set_brightness(eq);
> +    set_saturation(eq);
> +
> +    for (i = 0; i < 3; i++) {
> +        eq->param[i].c = (eq->param[i].contrast) * 65536.0;
> +        eq->param[i].b = (eq->param[i].brightness + 1.0) * 255.5 - 128.0 - (eq->param[i].contrast) * 128.0;
> +    }
> +
> +    eq->process = process_c;

As already stated in another review, set_* are using eq->process, but
you're only defining it here.

> +
> +    if (ARCH_X86)
> +        ff_eq_init_x86(eq);
> +
> +    return 0;
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    static const enum AVPixelFormat pixel_fmts_eq[] = {
> +        AV_PIX_FMT_GRAY8,
> +        AV_PIX_FMT_YUV410P,
> +        AV_PIX_FMT_YUV411P,
> +        AV_PIX_FMT_YUV420P,
> +        AV_PIX_FMT_YUV422P,
> +        AV_PIX_FMT_YUV444P,
> +        AV_PIX_FMT_NONE
> +    };
> +
> +    ff_set_common_formats(ctx, ff_make_format_list(pixel_fmts_eq));
> +
> +    return 0;
> +}
> +
> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    AVFilterLink *outlink = inlink->dst->outputs[0];
> +    EQContext *eq = ctx->priv;
> +    AVFrame *out;
> +    const AVPixFmtDescriptor *desc;
> +    int i;
> +
> +    out = ff_get_video_buffer(outlink, inlink->w, inlink->h);
> +    if (!out)
> +        return AVERROR(ENOMEM);
> +
> +    av_frame_copy_props(out, in);
> +    desc = av_pix_fmt_desc_get(inlink->format);
> +
> +    for (i = 0; i < desc->nb_components; i++) {
> +        int w = inlink->w;
> +        int h = inlink->h;
> +
> +        if (i == 1 || i == 2) {
> +            w = FF_CEIL_RSHIFT(w, desc->log2_chroma_w);
> +            h = FF_CEIL_RSHIFT(h, desc->log2_chroma_h);
> +        }
> +
> +        if (eq->param[i].adjust)
> +            eq->param[i].adjust(&eq->param[i], out->data[i], out->linesize[i],
> +                                 in->data[i], in->linesize[i], w, h);
> +        else
> +            av_image_copy_plane(out->data[i], out->linesize[i],
> +                                in->data[i], in->linesize[i], w, h);
> +    }
> +

> +    if (in != out) {
> +        if (in->data[3])
> +            av_image_copy_plane(out->data[3], out->linesize[3],
> +                                in ->data[3], in ->linesize[3],
> +                                inlink->w, inlink->h);
> +        av_frame_free(&in);
> +    }

provided that alpha is not supported, this should be probably unneded
(but keep it if you think it's a good idea)

> +
> +    return ff_filter_frame(outlink, out);
> +}
> +static const AVFilterPad eq_inputs[] = {
> +    {
> +        .name = "default",
> +        .type = AVMEDIA_TYPE_VIDEO,
> +        .filter_frame = filter_frame,
> +    },
> +    { NULL }
> +};
> +
> +static const AVFilterPad eq_outputs[] = {
> +    {
> +        .name = "default",
> +        .type = AVMEDIA_TYPE_VIDEO,
> +    },
> +    { NULL }
> +};
> +
> +#define OFFSET(x) offsetof(EQContext, x)
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> +
> +static const AVOption eq_options[] = {
> +    { "brightness", "set the brightness adjustment",
> +        OFFSET(brightness), AV_OPT_TYPE_DOUBLE, {.dbl = 0.0}, -1.0, 1.0, FLAGS },
> +    { "contrast",   "set the contrast adjustment, negative values give a negative image",
> +        OFFSET(contrast),   AV_OPT_TYPE_DOUBLE, {.dbl = 1.0}, -2.0, 2.0, FLAGS },
> +    { "gamma",      "set the initial gamma value",
> +        OFFSET(gamma),      AV_OPT_TYPE_DOUBLE, {.dbl = 1.0},  0.1, 10.0, FLAGS },
> +    { "gamma_y",    "gamma value for the luma plane",
> +        OFFSET(gamma_y),    AV_OPT_TYPE_DOUBLE, {.dbl = 1.0},  0.1, 10.0, FLAGS },
> +    { "gamma_u",    "gamma value for the 1st chroma plane",
> +        OFFSET(gamma_u),    AV_OPT_TYPE_DOUBLE, {.dbl = 1.0},  0.1, 10.0, FLAGS },
> +    { "gamma_v",    "gamma value for the 2st chroma plane",
> +        OFFSET(gamma_v),    AV_OPT_TYPE_DOUBLE, {.dbl = 1.0},  0.1, 10.0, FLAGS },
> +    { "saturation", "set the saturation adjustment",
> +        OFFSET(saturation), AV_OPT_TYPE_DOUBLE, {.dbl = 1.0},  0.0, 3.0, FLAGS },
> +    { "weight",     "set the gamma weight which reduces the effect of gamma on bright areas",
> +        OFFSET(weight),     AV_OPT_TYPE_DOUBLE, {.dbl = 1.0},  0.0, 1.0, FLAGS },
> +    { NULL }
> +};
> +
> +AVFILTER_DEFINE_CLASS(eq);
> +
> +AVFilter ff_vf_eq = {
> +    .name          = "eq",
> +    .description   = NULL_IF_CONFIG_SMALL("Adjust brightness, contrast, gamma, and saturation."),
> +    .priv_size     = sizeof(EQContext),
> +    .priv_class    = &eq_class,
> +    .inputs        = eq_inputs,
> +    .outputs       = eq_outputs,
> +    .query_formats = query_formats,
> +    .init          = initialize,
> +};
> diff --git a/libavfilter/vf_eq.h b/libavfilter/vf_eq.h
> new file mode 100644
> index 0000000..dcf8b5e
> --- /dev/null
> +++ b/libavfilter/vf_eq.h
> @@ -0,0 +1,63 @@
> +/*
> + * Original MPlayer filters by Richard Felker, Hampa Hug, Daniel Moreno,
> + * and Michael Niedermeyer.
> + *
> + * Copyright (c) 2014 James Darnley <james.darnley at gmail.com>
> + * Copyright (c) 2015 Arwa Arif <arwaarif1994 at gmail.com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU 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.
> + */
> +
> +#ifndef AVFILTER_EQ_H
> +#define AVFILTER_EQ_H
> +
> +#include "avfilter.h"
> +
> +typedef struct EQParameters {
> +    
> +    void (*adjust)(struct EQParameters *eq, uint8_t *dst, int dst_stride,
> +                   uint8_t *src, int src_stride, int w, int h);
> +
> +    uint8_t lut[256];
> +    uint16_t lut16[256*256];
> +
> +    double brightness, contrast, gamma, weight;
> +    int b, c, lut_clean;
> +
> +} EQParameters;
> +
> +typedef struct {
> +    const AVClass *class;
> +
> +    EQParameters param[3];
> +
> +    double contrast;
> +    double brightness;
> +    double saturation;
> +
> +    double gamma;
> +    double weight;
> +    double gamma_y, gamma_u, gamma_v;
> +
> +    void (*process)(struct EQParameters *par, uint8_t *dst, int dst_stride,
> +                    uint8_t *src, int src_stride, int w, int h);
> +
> +} EQContext;
> +
> +void ff_eq_init_x86(EQContext *eq);
> +
> +#endif /* AVFILTER_EQ_H */
> diff --git a/libavfilter/x86/Makefile b/libavfilter/x86/Makefile
> index b93154e..8222e3f 100644
> --- a/libavfilter/x86/Makefile
> +++ b/libavfilter/x86/Makefile
> @@ -1,3 +1,4 @@
> +OBJS-$(CONFIG_EQ_FILTER)                     += x86/vf_eq.o
>  OBJS-$(CONFIG_FSPP_FILTER)                   += x86/vf_fspp.o
>  OBJS-$(CONFIG_GRADFUN_FILTER)                += x86/vf_gradfun_init.o
>  OBJS-$(CONFIG_HQDN3D_FILTER)                 += x86/vf_hqdn3d_init.o

This is not updated against latest git. Please *always* send patches
updated against latest master, so that we can easily apply the patch.

[...]

Looks good otherwise, assuming it is bitexact with the mp=eq2.

Also, what about adding expressions support (like done in hue)? (this
will go of course to a separate patch).
-- 
FFmpeg = Fundamentalist and Fanciful Muttering Peaceless Eccentric Generator


More information about the ffmpeg-devel mailing list