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

Stefano Sabatini stefano.sabatini-lala
Mon Nov 29 15:00:14 CET 2010


On date Monday 2010-11-29 07:18:14 -0500, Nolan L encoded:
> As part of a GCI task, I've ported the gradfun debanding filter from mplayer
> to libavfilter.

Just for curiosity, to which project is the GCI task related?

> The patch includes changes to the build system to account for CPU
> optimizations that weren't present previously.
> 
> There is a SSE2 method that remains unported due to lack of SSE2 detection
> in the configure script that I wasn't quite sure how to add.

> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 210510f..f50c100 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
> @@ -52,6 +53,9 @@ OBJS-$(CONFIG_NULLSINK_FILTER)               += vsink_nullsink.o
>  
>  -include $(SUBDIR)$(ARCH)/Makefile
>  
> +OBJS-$(HAVE_MMX2)  += $(MMX2-OBJS-yes)
> +OBJS-$(HAVE_SSSE3) += $(SSSE3-OBJS-yes)
> +
>  DIRS = x86
>  
>  include $(SUBDIR)../subdir.mak
> 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..f67292b
> --- /dev/null
> +++ b/libavfilter/gradfun.h
> @@ -0,0 +1,62 @@
> +/*
> + * 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_YADIF_H
> +#define AVFILTER_YADIF_H

Ehm...

> +
> +#include "avfilter.h"
> +
> +#define CHROMA_WIDTH(link)     -((-link->w) >> av_pix_fmt_descriptors[link->format].log2_chroma_w)
> +#define CHROMA_HEIGHT(link)    -((-link->h) >> av_pix_fmt_descriptors[link->format].log2_chroma_h)
> +#define RADIUS_CHROMA(r, link) (((r >> av_pix_fmt_descriptors[link->format].log2_chroma_w) + \
> +                                (r >> av_pix_fmt_descriptors[link->format].log2_chroma_w)) / 2)

This can be moved to the implementation, or even better it can be
avoided.

> +
> +typedef struct {
> +    int thresh;
> +    int radius;
> +    uint16_t *buf;
> +    void (*filter_line) (uint8_t *dst, uint8_t *src, uint16_t *dc, int width, int thresh, const uint16_t *dithers);
> +    void (*blur_line) (uint16_t *dc, uint16_t *buf, uint16_t *buf1, uint8_t *src, int sstride, int width);

Nit:
sstride -> src_stride (or src_linesize)

> +} GradFunContext;
> +
> +static const uint16_t __attribute__((aligned(16))) pw_7f[8] = {127,127,127,127,127,127,127,127};
> +static const uint16_t __attribute__((aligned(16))) pw_ff[8] = {255,255,255,255,255,255,255,255};
> +static const uint16_t __attribute__((aligned(16))) dither[8][8] = {
> +    {  0, 96, 24,120,  6,102, 30,126 },
> +    { 64, 32, 88, 56, 70, 38, 94, 62 },
> +    { 16,112,  8,104, 22,118, 14,110 },
> +    { 80, 48, 72, 40, 86, 54, 78, 46 },
> +    {  4,100, 28,124,  2, 98, 26,122 },
> +    { 68, 36, 92, 60, 66, 34, 90, 58 },
> +    { 20,116, 12,108, 18,114, 10,106 },
> +    { 84, 52, 76, 44, 82, 50, 74, 42 },
> +};
> +
> +void filter_line_c(uint8_t *dst, uint8_t *src, uint16_t *dc, int width, int thresh, const uint16_t *dithers);
> +void blur_line_c(uint16_t *dc, uint16_t *buf, uint16_t *buf1, uint8_t *src, int sstride, int width);
> +
> +void filter_line_mmx2(uint8_t *dst, uint8_t *src, uint16_t *dc,
> +                             int width, int thresh, const uint16_t *dithers);
> +
> +void filter_line_ssse3(uint8_t *dst, uint8_t *src, uint16_t *dc,
> +                              int width, int thresh, const uint16_t *dithers);
> +
> +void blur_line_sse2(uint16_t *dc, uint16_t *buf, uint16_t *buf1,
> +                           uint8_t *src, int sstride, int width);
> +
> +#endif /* AVFILTER_YADIF_H */
> diff --git a/libavfilter/vf_gradfun.c b/libavfilter/vf_gradfun.c
> new file mode 100644
> index 0000000..369c893
> --- /dev/null
> +++ b/libavfilter/vf_gradfun.c
> @@ -0,0 +1,257 @@
> +/*
> + * copyright (c) 2010 Nolan Lum 
> + *               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
> + * scale video filter

It's not a scale video filter.

> + */
> +
> +/*
> + * 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 "avfilter.h"
> +#include "libavutil/cpu.h"
> +#include "libavutil/pixdesc.h"
> +#include "gradfun.h"

Nit++:
#include "libavutil/cpu.h"
#include "libavutil/pixdesc.h"
#include "avfilter.h"
#include "gradfun.h"

> +
> +void 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 blur_line_c(uint16_t *dc, uint16_t *buf, uint16_t *buf1, uint8_t *src, int sstride, 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 + sstride] + src[2 * x + 1 + sstride];
> +        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 dstride, int sstride, int r)
> +{
> +    int bstride = ((width + 15) & ~15) / 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 * sstride, sstride, 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) * sstride, sstride, 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 * dstride, src + y * sstride, dc - r / 2, width, thresh, dither[y & 7]);
> +        }
> +        ctx->filter_line(dst + y * dstride, src + y * sstride, dc - r / 2, width, thresh, dither[y & 7]);
> +        if (++y >= height) break;
> +        ctx->filter_line(dst + y * dstride, src + y * sstride, dc - r / 2, width, thresh, dither[y & 7]);
> +        if (++y >= height) break;
> +    }
> +}
> +

> +static inline void memcpy_pic(void* dst, const void* src, int bytesPerLine, int height, int dstStride, int srcStride)
> +{
> +    int i;
> +
> +    if(dstStride == srcStride) {
> +        if (srcStride < 0) {
> +            src = (const uint8_t*) src + (height - 1) * srcStride;
> +            dst = (uint8_t*) dst + (height - 1) * dstStride;
> +            srcStride = -srcStride;
> +        }
> +        memcpy(dst, src, srcStride * height);
> +    } else {
> +        for(i = 0; i < height; i++) {
> +            memcpy(dst, src, bytesPerLine);
> +            src = (const uint8_t*)src + srcStride;
> +            dst = (uint8_t*)dst + dstStride;
> +        }
> +    }
> +}

Maybe av_image_copy_plane() can be used instead.

> +
> +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);
> +    
> +    av_log(ctx, AV_LOG_INFO, "threshold:%.2f radius:%d\n", thresh, gf->radius);
> +
> +    gf->blur_line = blur_line_c;
> +    gf->filter_line = filter_line_c;
> +
> +#if HAVE_MMX2
> +    if (cpu_flags & AV_CPU_FLAG_MMX2)
> +        gf->filter_line = filter_line_mmx2;

if (HAVE_MMX2 && cpu_flags & AV_CPU_FLAG_MMX2)

check also vf_yadif.c

> +#endif
> +#if HAVE_SSSE3
> +    if (cpu_flags & AV_CPU_FLAG_SSSE3)
> +        gf->filter_line = filter_line_ssse3;
> +#endif /*
> +#if HAVE_SSE2
> +    if (cpu_flags & AV_CPU_FLAG_SSE2)
> +        gf->blur_line = blur_line_sse2;
> +    //no support for sse2 in ./configure.
> +#endif */
> +
> +    return 0;
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    GradFunContext *gf = ctx->priv;
> +    if(gf->buf) 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((((inlink->w + 15) & ~15) * (gf->radius + 1) / 2 + 32) * sizeof(uint16_t));
> +    
> +    return !gf->buf;

AVERROR(ENOMEM)

> +}
> +
> +static int config_output(AVFilterLink *outlink)
> +{
> +    AVFilterLink *inlink = outlink->src->inputs[0];
> +    
> +    outlink->w = inlink->w;
> +    outlink->h = inlink->h;
> +    
> +    return 0;
> +}

useless

> +
> +static void end_frame(AVFilterLink *link)

nit: inlink

> +{
> +    GradFunContext *gf = link->dst->priv;
> +    AVFilterBufferRef *inpic = link->cur_buf;
> +    AVFilterBufferRef *outpic = link->dst->outputs[0]->out_buf;
> +    int p;
> +    
> +    for (p = 0; p < 4 && inpic->data[p]; p++) {
> +        int w = link->w;
> +        int h = link->h;
> +        int r = gf->radius;
> +        if (p) {
> +            w = CHROMA_WIDTH(link);
> +            h = CHROMA_HEIGHT(link);
> +            r = av_clip((RADIUS_CHROMA(r, link) + 1) & ~1, 4, 32);
> +        }
> +        
> +        if (FFMIN(w, h) > 2 * r)
> +            filter(gf, outpic->data[p], inpic->data[p], w, h, outpic->linesize[p], inpic->linesize[p], r);

> +        else if (outpic->data[p] != inpic->data[p])

Can this actually happen?

> +            memcpy_pic(outpic->data[p], inpic->data[p], w, h, outpic->linesize[p], inpic->linesize[p]);     
> +    }
> +
> +    avfilter_unref_buffer(inpic);
> +    avfilter_draw_slice(link->dst->outputs[0], 0, link->h, 1);
> +    avfilter_end_frame(link->dst->outputs[0]);
> +    avfilter_unref_buffer(outpic);
> +}
> +
> +static void draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
> +{
> +}
> +
> +AVFilter avfilter_vf_gradfun = {
> +    .name      = "gradfun",

> +    .description = NULL_IF_CONFIG_SMALL("Fast, simple debander using gradients."),

Nit: change this to a sentence (verbal form, rather than a nominal form).

[...]

Thanks for the patch, regards.
-- 
FFmpeg = Fast and Frenzy Most Plastic Everlasting Gymnast



More information about the ffmpeg-devel mailing list