[FFmpeg-devel] [PATCH] Port gradfun to libavfilter (GCI)

Stefano Sabatini stefano.sabatini-lala
Wed Dec 1 01:41:06 CET 2010


On date Tuesday 2010-11-30 19:27:14 -0500, Nolan L encoded:
> On Mon, Nov 29, 2010 at 9:48 PM, Nolan L <nol888 at gmail.com> wrote:
> >
> > Here is the resulting patch from the first round of feedback.
> >
> 
> Pushing a third revision of the code, addressing missing documentation
> (mostly) and slight code changes related to confusing macros.

> diff --git a/doc/filters.texi b/doc/filters.texi
> index 1cba2d6..f266ff0 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -333,6 +333,32 @@ frei0r=perspective:0.2/0.2:0.8/0.2
>  For more information see:
>  @url{http://piksel.org/frei0r}
>  
> + at section gradfun
> +
> +Fix the banding artifacts that are sometimes introduced into nearly flat
> +regions by truncation to 8bit colordepth.
> +Interpolates the gradients that should go where the bands are, and
> +dithers them.
> +
> +The filter takes two optional parameters, separated by ':',
> +"@var{strength}:@var{radius}"
> +
> + at var{strength} is the maximum amount by which the filter will change
> +any one pixel. Also the threshold for detecting nearly flat
> +regions (default: 1.2).
> +
> + at var{radius} is the neighborhood to fid the gradient to. A larger radius
> +makes for smoother gradients, but also prevents the filter from modifying
> +the pixels near detailed regions (default: 16).
> +
> + at example
> +# Default parameters.
> +gradfun=1.2:16
> +
> +# Omitting radius
> +gradfun=1.2
> + at end example
> +
>  @section hflip
>  
>  Flip the input video horizontally.
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 210510f..7e317c8 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -28,6 +28,7 @@ OBJS-$(CONFIG_FORMAT_FILTER)                 += vf_format.o
>  OBJS-$(CONFIG_FREI0R_FILTER)                 += vf_frei0r.o
>  OBJS-$(CONFIG_HFLIP_FILTER)                  += vf_hflip.o
>  OBJS-$(CONFIG_NOFORMAT_FILTER)               += vf_format.o
> +OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
>  OBJS-$(CONFIG_NULL_FILTER)                   += vf_null.o
>  OBJS-$(CONFIG_OCV_SMOOTH_FILTER)             += vf_libopencv.o
>  OBJS-$(CONFIG_OVERLAY_FILTER)                += vf_overlay.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index c3067b8..6ac517c 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -48,6 +48,7 @@ void avfilter_register_all(void)
>      REGISTER_FILTER (FORMAT,      format,      vf);
>      REGISTER_FILTER (FREI0R,      frei0r,      vf);
>      REGISTER_FILTER (HFLIP,       hflip,       vf);
> +    REGISTER_FILTER (GRADFUN,     gradfun,     vf);
>      REGISTER_FILTER (NOFORMAT,    noformat,    vf);
>      REGISTER_FILTER (NULL,        null,        vf);
>      REGISTER_FILTER (OCV_SMOOTH,  ocv_smooth,  vf);
> diff --git a/libavfilter/gradfun.h b/libavfilter/gradfun.h
> new file mode 100644
> index 0000000..0f6044d
> --- /dev/null
> +++ b/libavfilter/gradfun.h
> @@ -0,0 +1,49 @@
> +/*
> + * Copyright (c) 2010 Nolan Lum <nol888 at gmail.com>
> + *               2009 Loren Merritt <lorenm at u.washignton.edu>
> + *
> + * 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
> + */
> +
> +#ifndef AVFILTER_GRADFUN_H
> +#define AVFILTER_GRADFUN_H
> +
> +#include "avfilter.h"
> +
> +/// Holds instance-specific information for gradfun.
> +typedef struct {
> +    int thresh; ///< Threshold for gradient algorithm.
> +    int radius; ///< Blur radius
> +    int chroma_w; ///< Width of the chroma planes.
> +    int chroma_h; ///< Height of the chroma planes.
> +    int chroma_r; ///< Blur radius for the chroma planes.
> +    uint16_t *buf; ///< Holds image data for blur algorithm passed into filter.
> +    /// The implementation of the filter algorithm we are using.
> +    void (*filter_line) (uint8_t *dst, uint8_t *src, uint16_t *dc, int width, int thresh, const uint16_t *dithers);
> +    /// The implementation of the blur algorithm we are using.
> +    void (*blur_line) (uint16_t *dc, uint16_t *buf, uint16_t *buf1, uint8_t *src, int src_linesize, int width);
> +} GradFunContext;
> +
> +void ff_gradfun_filter_line_c(uint8_t *dst, uint8_t *src, uint16_t *dc, int width, int thresh, const uint16_t *dithers);
> +void ff_gradfun_blur_line_c(uint16_t *dc, uint16_t *buf, uint16_t *buf1, uint8_t *src, int src_linesize, int width);
> +
> +void ff_gradfun_filter_line_mmx2(uint8_t *dst, uint8_t *src, uint16_t *dc, int width, int thresh, const uint16_t *dithers);
> +void ff_gradfun_filter_line_ssse3(uint8_t *dst, uint8_t *src, uint16_t *dc, int width, int thresh, const uint16_t *dithers);
> +
> +void ff_gradfun_blur_line_sse2(uint16_t *dc, uint16_t *buf, uint16_t *buf1, uint8_t *src, int src_linesize, int width);
> +
> +#endif /* AVFILTER_GRADFUN_H */
> diff --git a/libavfilter/vf_gradfun.c b/libavfilter/vf_gradfun.c
> new file mode 100644
> index 0000000..6b99789
> --- /dev/null
> +++ b/libavfilter/vf_gradfun.c
> @@ -0,0 +1,245 @@
> +/*
> + * Copyright (c) 2010 Nolan Lum <nol888 at gmail.com>
> + *               2009 Loren Merritt <lorenm at u.washignton.edu>
> + *
> + * 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
> + * gradfun debanding filter
> + * Ported from MPlayer libmpcodecs/vf_gradfun.c
> + */
> +
> +/*
> + * Debanding algorithm (from gradfun2db by prunedtree):
> + * Boxblur.
> + * Foreach pixel, if it's within threshold of the blurred value, make it closer.
> + * So now we have a smoothed and higher bitdepth version of all the shallow
> + * gradients, while leaving detailed areas untouched.
> + * Dither it back to 8bit.
> + */
> +
> +#include "libavcore/imgutils.h"
> +#include "libavutil/cpu.h"
> +#include "libavutil/pixdesc.h"
> +#include "avfilter.h"
> +#include "gradfun.h"
> +
> +DECLARE_ALIGNED(16, static const uint16_t, dither)[8][8] = {
> +    {0x00,0x60,0x18,0x78,0x06,0x66,0x1E,0x7E},
> +    {0x40,0x20,0x58,0x38,0x46,0x26,0x5E,0x3E},
> +    {0x10,0x70,0x08,0x68,0x16,0x76,0x0E,0x6E},
> +    {0x50,0x30,0x48,0x28,0x56,0x36,0x4E,0x2E},
> +    {0x04,0x64,0x1C,0x7C,0x02,0x62,0x1A,0x7A},
> +    {0x44,0x24,0x5C,0x3C,0x42,0x22,0x5A,0x3A},
> +    {0x14,0x74,0x0C,0x6C,0x12,0x72,0x0A,0x6A},
> +    {0x54,0x34,0x4C,0x2C,0x52,0x32,0x4A,0x2A},
> +};
> +
> +void ff_gradfun_filter_line_c(uint8_t *dst, uint8_t *src, uint16_t *dc, int width, int thresh, const uint16_t *dithers)
> +{
> +    int x;
> +    for (x=0; x<width; x++, dc += x & 1) {
> +        int pix = src[x] << 7;
> +        int delta = dc[0] - pix;
> +        int m = abs(delta) * thresh >> 16;
> +        m = FFMAX(0, 127 - m);
> +        m = m * m * delta >> 14;
> +        pix += m + dithers[x & 7];
> +        dst[x] = av_clip_uint8(pix >> 7);
> +    }
> +}
> +
> +void ff_gradfun_blur_line_c(uint16_t *dc, uint16_t *buf, uint16_t *buf1, uint8_t *src, int src_linesize, int width)
> +{
> +    int x, v, old;
> +    for (x=0; x < width; x++) {
> +        v = buf1[x] + src[2 * x] + src[2 * x + 1] + src[2 * x + src_linesize] + src[2 * x + 1 + src_linesize];
> +        old = buf[x];
> +        buf[x] = v;
> +        dc[x] = v - old;
> +    }
> +}
> +
> +static void filter(GradFunContext *ctx, uint8_t *dst, uint8_t *src, int width, int height, int dst_linesize, int src_linesize, int r)
> +{
> +    int bstride = FFALIGN(width, 16) / 2;
> +    int y;
> +    uint32_t dc_factor = (1 << 21) / (r * r);
> +    uint16_t *dc = ctx->buf + 16;
> +    uint16_t *buf = ctx->buf + bstride + 32;
> +    int thresh = ctx->thresh;
> +
> +    memset(dc, 0, (bstride + 16) * sizeof(*buf));
> +    for (y = 0; y < r; y++)
> +        ctx->blur_line(dc, buf + y * bstride, buf + (y - 1) * bstride, src + 2 * y * src_linesize, src_linesize, width / 2);
> +    for (;;) {
> +        if (y < height - r) {
> +            int mod = ((y + r) / 2) % r;
> +            uint16_t *buf0 = buf + mod * bstride;
> +            uint16_t *buf1 = buf + (mod ? mod - 1 : r - 1) * bstride;
> +            int x, v;
> +            ctx->blur_line(dc, buf0, buf1, src + (y + r) * src_linesize, src_linesize, width / 2);
> +            for (x = v = 0; x < r; x++)
> +                v += dc[x];
> +            for (; x < width / 2; x++) {
> +                v += dc[x] - dc[x-r];
> +                dc[x-r] = v * dc_factor >> 16;
> +            }
> +            for (; x < (width + r + 1) / 2; x++)
> +                dc[x-r] = v * dc_factor >> 16;
> +            for (x = -r / 2; x < 0; x++)
> +                dc[x] = dc[0];
> +        }
> +        if (y == r) {
> +            for (y = 0; y < r; y++)
> +                ctx->filter_line(dst + y * dst_linesize, src + y * src_linesize, dc - r / 2, width, thresh, dither[y & 7]);
> +        }
> +        ctx->filter_line(dst + y * dst_linesize, src + y * src_linesize, dc - r / 2, width, thresh, dither[y & 7]);
> +        if (++y >= height) break;
> +        ctx->filter_line(dst + y * dst_linesize, src + y * src_linesize, dc - r / 2, width, thresh, dither[y & 7]);
> +        if (++y >= height) break;
> +    }
> +}
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> +{
> +    GradFunContext *gf = ctx->priv;
> +    float thresh = 1.2;
> +    int radius = 16;
> +    av_unused int cpu_flags = av_get_cpu_flags();
> +
> +    if (args) sscanf(args, "%f:%d", &thresh, &radius);
> +    
> +    thresh = av_clipf(thresh, 0.51, 255);
> +    gf->thresh = (1 << 15) / thresh;
> +    gf->radius = av_clip((radius + 1) & ~1, 4, 32);
> +
> +    gf->blur_line = ff_gradfun_blur_line_c;
> +    gf->filter_line = ff_gradfun_filter_line_c;
> +
> +    if (HAVE_MMX && cpu_flags & AV_CPU_FLAG_MMX2)
> +        gf->filter_line = ff_gradfun_filter_line_mmx2;
> +    if (HAVE_SSSE3 && cpu_flags & AV_CPU_FLAG_SSSE3)
> +        gf->filter_line = ff_gradfun_filter_line_ssse3;
> +    if (HAVE_SSE && cpu_flags & AV_CPU_FLAG_SSE2)
> +        gf->blur_line = ff_gradfun_blur_line_sse2;
> +        
> +    av_log(ctx, AV_LOG_INFO, "threshold:%.2f radius:%d filter_mmx2:%s filter_ssse3:%s blur_sse2:%s\n",
> +        thresh, gf->radius, gf->filter_line == ff_gradfun_filter_line_mmx2 ? "yes" : "no",
> +        gf->filter_line == ff_gradfun_filter_line_ssse3 ? "yes" : "no",
> +        gf->blur_line == ff_gradfun_blur_line_sse2 ? "yes" : "no");
> +
> +    return 0;
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    GradFunContext *gf = ctx->priv;
> +    av_free(gf->buf);
> +}
> +
> +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,
> +        PIX_FMT_NONE
> +    };
> +
> +    avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
> +
> +    return 0;
> +}
> +
> +static int config_input(AVFilterLink *inlink)
> +{
> +    GradFunContext *gf = inlink->dst->priv;
> +    
> +    av_free(gf->buf);
> +    gf->buf = av_mallocz((FFALIGN(inlink->w, 16) * (gf->radius + 1) / 2 + 32) * sizeof(uint16_t));
> +    

> +    gf->chroma_w = -((-inlink->w) >> av_pix_fmt_descriptors[inlink->format].log2_chroma_w);
> +    gf->chroma_h = -((-inlink->h) >> av_pix_fmt_descriptors[inlink->format].log2_chroma_h);
> +    gf->chroma_r = av_clip(
> +                    ( ( ( (gf->radius >> av_pix_fmt_descriptors[inlink->format].log2_chroma_w) +
> +                          (gf->radius >> av_pix_fmt_descriptors[inlink->format].log2_chroma_h)
> +                        ) / 2
> +                      ) + 1
> +                    ) & ~1, 4, 32);

int hsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_w;
int vsub = av_pix_fmt_descriptors[inlink->format].log2_chroma_h;

...

should be easier on the eyes.
-- 
FFmpeg = Furious & Furious Mysterious Perennial Evangelical Guru



More information about the ffmpeg-devel mailing list