[FFmpeg-devel] Fwd: [PATCH] lavfi: add xbr filter

Stefano Sabatini stefasab at gmail.com
Sun Nov 9 01:13:18 CET 2014


On date Sunday 2014-11-09 00:59:36 +0530, arwa arif encoded:
[...]
> From 1a55477c392e481d3ee6c80696e925046ff5385f Mon Sep 17 00:00:00 2001
> From: Arwa Arif <arwaarif1994 at gmail.com>
> Date: Thu, 30 Oct 2014 22:06:20 +0530
> Subject: [PATCH] [PATCH]lavfi: add xbr filter xBR

Repeated [PATCH]

> 
> ---
>  configure                |    1 +
>  doc/filters.texi         |    5 +
>  libavfilter/Makefile     |    1 +
>  libavfilter/allfilters.c |    1 +
>  libavfilter/vf_xbr.c     |  379 ++++++++++++++++++++++++++++++++++++++++++++++

Missing Changelog and LICENSE.md updates.

>  5 files changed, 387 insertions(+)
>  create mode 100644 libavfilter/vf_xbr.c
> 
> diff --git a/configure b/configure
> index e6e3de3..7d4fa6b 100755
> --- a/configure
> +++ b/configure
> @@ -2607,6 +2607,7 @@ vidstabtransform_filter_deps="libvidstab"
>  pixfmts_super2xsai_test_deps="super2xsai_filter"
>  tinterlace_merge_test_deps="tinterlace_filter"
>  tinterlace_pad_test_deps="tinterlace_filter"
> +xbr_filter_deps="gpl"
>  zmq_filter_deps="libzmq"
>  zoompan_filter_deps="swscale"
>  
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 7be29de..2905e5d 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -9163,6 +9163,11 @@ Only deinterlace frames marked as interlaced.
>  Default value is @samp{all}.
>  @end table
>  
> + at section xbr
> +Apply high-quality magnification filter which is designed for pixel art. It follows a set
> +of edge-detection rules @url{http://www.libretro.com/forums/viewtopic.php?f=6&t=134}.

of edge-detection, see @url{...}.

> +This filter was originally created by Hyllian.

Drop this, doesn't belong to user docs.

> +
>  @anchor{yadif}
>  @section yadif
>  
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 6d868e7..2c56e38 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -198,6 +198,7 @@ OBJS-$(CONFIG_VIDSTABDETECT_FILTER)          += vidstabutils.o vf_vidstabdetect.
>  OBJS-$(CONFIG_VIDSTABTRANSFORM_FILTER)       += vidstabutils.o vf_vidstabtransform.o
>  OBJS-$(CONFIG_VIGNETTE_FILTER)               += vf_vignette.o
>  OBJS-$(CONFIG_W3FDIF_FILTER)                 += vf_w3fdif.o
> +OBJS-$(CONFIG_XBR_FILTER)                    += vf_xbr.o
>  OBJS-$(CONFIG_YADIF_FILTER)                  += vf_yadif.o
>  OBJS-$(CONFIG_ZMQ_FILTER)                    += f_zmq.o
>  OBJS-$(CONFIG_ZOOMPAN_FILTER)                += vf_zoompan.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index d88a9ad..2352d44 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -213,6 +213,7 @@ void avfilter_register_all(void)
>      REGISTER_FILTER(VIDSTABTRANSFORM, vidstabtransform, vf);
>      REGISTER_FILTER(VIGNETTE,       vignette,       vf);
>      REGISTER_FILTER(W3FDIF,         w3fdif,         vf);
> +    REGISTER_FILTER(XBR,            xbr,            vf);
>      REGISTER_FILTER(YADIF,          yadif,          vf);
>      REGISTER_FILTER(ZMQ,            zmq,            vf);
>      REGISTER_FILTER(ZOOMPAN,        zoompan,        vf);
> diff --git a/libavfilter/vf_xbr.c b/libavfilter/vf_xbr.c
> new file mode 100644
> index 0000000..25bdb3a
> --- /dev/null
> +++ b/libavfilter/vf_xbr.c
> @@ -0,0 +1,379 @@
> +/*
> + * This file is part of FFmpeg.
> + *

> + * Hyllian's 2xBR v3.3b

Drop this line.

> + * 
> + * Copyright (C) 2011, 2012 Hyllian/Jararaca - sergiogdb at gmail.com
> + *

> + * License - GPLv2+.

License is stated below, and this is conflicting with the below
text. See the blurb in the other GPL filters.

> + *
> + * Copyright (c) 2014 Arwa Arif <arwaarif1994 at gmail.com>
> + *
> + * 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
> + * XBR Filter is used for depixelization of image.
> + * This is based on Hyllian's 2xBR shader.
> + *
> + * @see : http://www.libretro.com/forums/viewtopic.php?f=6&t=134
> + * @see : https://github.com/yoyofr/iFBA/blob/master/fba_src/src/intf/video/scalers/xbr.cpp
> + * Future work : To implement x3 and x4 scale, and threading.
> + */
> +
> +#include "libavutil/opt.h"
> +#include "libavutil/avassert.h"
> +#include "libavutil/pixdesc.h"
> +#include "internal.h"
> +
> +#define RGB_MASK         0x00FFFFFF

> +#define pg_lbmask        0x00FEFEFE
> +#define pg_red_blue_mask 0x00FF00FF
> +#define pg_green_mask    0x0000FF00

pg stands for? Also usually macros and defines are UPCASED.

> +
> +typedef struct {
> +    uint32_t rgbtoyuv[1<<24];
> +} xBRContext;

nit++: XBRContext

> +static uint32_t df(uint32_t x, uint32_t y, const uint32_t *r2y)
> +{
> +
> +#define YMASK 0xff0000
> +#define UMASK 0x00ff00
> +#define VMASK 0x0000ff
> +
> +    uint32_t yuv1 = r2y[x & 0xffffff];
> +    uint32_t yuv2 = r2y[y & 0xffffff];

Note: is the mask needed?

> +    return (abs((yuv1 & YMASK) - (yuv2 & YMASK)) >> 16) +
> +           (abs((yuv1 & UMASK) - (yuv2 & UMASK)) >>  8) +
> +           abs((yuv1 & VMASK) - (yuv2 & VMASK));
> +}
> +

> +#define ALPHA_BLEND_128_W(dst, src) dst = ((src & pg_lbmask) >> 1) + ((dst & pg_lbmask) >> 1)
> +
> +#define ALPHA_BLEND_32_W(dst, src) \
> +        dst = ( \
> +                    (pg_red_blue_mask & ((dst & pg_red_blue_mask) + \
> +                                                 ((((src & pg_red_blue_mask) - \
> +                                                            (dst & pg_red_blue_mask))) >>3))) | \
> +                    (pg_green_mask & ((dst & pg_green_mask) + \
> +                                              ((((src & pg_green_mask) - \
> +                                                         (dst & pg_green_mask))) >>3))))
> + 
> +#define ALPHA_BLEND_64_W(dst, src) \
> +        dst = ( \
> +                    (pg_red_blue_mask & ((dst & pg_red_blue_mask) + \
> +                                                 ((((src & pg_red_blue_mask) - \
> +                                                            (dst & pg_red_blue_mask))) >>2))) | \
> +                    (pg_green_mask & ((dst & pg_green_mask) + \
> +                                              ((((src & pg_green_mask) - \
> +                                                         (dst & pg_green_mask))) >>2))))
> +
> +#define ALPHA_BLEND_192_W(dst, src) \
> +        dst = ( \
> +                    (pg_red_blue_mask & ((dst & pg_red_blue_mask) + \
> +                                                 ((((src & pg_red_blue_mask) - \
> +                                                            (dst & pg_red_blue_mask)) * 3) >>2))) | \
> +                    (pg_green_mask & ((dst & pg_green_mask) + \
> +                                              ((((src & pg_green_mask) - \
> +                                                         (dst & pg_green_mask)) * 3) >>2))))
> + 
> +#define ALPHA_BLEND_224_W(dst, src) \
> +        dst = ( \
> +                    (pg_red_blue_mask & ((dst & pg_red_blue_mask) + \
> +                                                 ((((src & pg_red_blue_mask) - \
> +                                                            (dst & pg_red_blue_mask)) * 7) >>3))) | \
> +                    (pg_green_mask & ((dst & pg_green_mask) + \
> +                                              ((((src & pg_green_mask) - \
> +                                                         (dst & pg_green_mask)) * 7) >>3)))) 
> +

It would be nice to give a short explanation of these macros.

> +#define LEFT_UP_2_2X(N3, N2, N1, PIXEL)\
> +             ALPHA_BLEND_224_W(E[N3], PIXEL); \
> +             ALPHA_BLEND_64_W( E[N2], PIXEL); \
> +             E[N1] = E[N2]; \
> +
> +        
> +#define LEFT_2_2X(N3, N2, PIXEL)\
> +             ALPHA_BLEND_192_W(E[N3], PIXEL); \
> +             ALPHA_BLEND_64_W( E[N2], PIXEL); \
> +
> +#define UP_2_2X(N3, N1, PIXEL)\
> +             ALPHA_BLEND_192_W(E[N3], PIXEL); \
> +             ALPHA_BLEND_64_W( E[N1], PIXEL); \
> +
> +#define DIA_2X(N3, PIXEL)\
> +             ALPHA_BLEND_128_W(E[N3], PIXEL); \
> +
> +#define eq(A, B, r2y)\
> +        (df(A, B, r2y) < 155)\
> +
> +
> +#define FILTRO(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, N0, N1, N2, N3,r2y) \
> +     ex   = (PE!=PH && PE!=PF); \
> +     if ( ex )\
> +     {\
> +          e = (df(PE,PC,r2y)+df(PE,PG,r2y)+df(PI,H5,r2y)+df(PI,F4,r2y))+(df(PH,PF,r2y)<<2); \
> +          i = (df(PH,PD,r2y)+df(PH,I5,r2y)+df(PF,I4,r2y)+df(PF,PB,r2y))+(df(PE,PI,r2y)<<2); \
> +          if ((e<i)  && ( !eq(PF,PB,r2y) && !eq(PH,PD,r2y) || eq(PE,PI,r2y) && (!eq(PF,I4,r2y) && !eq(PH,I5,r2y)) || eq(PE,PG,r2y) || eq(PE,PC,r2y)) )\
> +          {\
> +              ke=df(PF,PG,r2y); ki=df(PH,PC,r2y); \
> +              ex2 = (PE!=PC && PB!=PC); ex3 = (PE!=PG && PD!=PG); px = (df(PE,PF,r2y) <= df(PE,PH,r2y)) ? PF : PH; \
> +              if ( ((ke<<1)<=ki) && ex3 && (ke>=(ki<<1)) && ex2 ) \
> +              {\
> +                     LEFT_UP_2_2X(N3, N2, N1, px)\
> +              }\
> +              else if ( ((ke<<1)<=ki) && ex3 ) \
> +              {\
> +                     LEFT_2_2X(N3, N2, px);\
> +              }\
> +              else if ( (ke>=(ki<<1)) && ex2 ) \
> +              {\
> +                     UP_2_2X(N3, N1, px);\
> +              }\
> +              else \
> +              {\
> +                     DIA_2X(N3, px);\
> +              }\
> +          }\
> +          else if (e<=i)\
> +          {\
> +               ALPHA_BLEND_128_W( E[N3], ((df(PE,PF,r2y) <= df(PE,PH,r2y)) ? PF : PH)); \
> +          }\
> +     }\
> +
> +static void xbr2x(AVFrame * input, AVFrame * output, const uint32_t * r2y)
> +{
> +    unsigned int e, i,px;
> +    unsigned int ex, ex2, ex3;
> +    unsigned int ke, ki;
> +    int x,y;
> +

> +    int nextOutputLine = output->linesize[0]>>2;

nit: next_output_line or simply "next_line"

> +
> +    for (y = 0; y < input->height; y++) {
> +
> +        uint32_t pprev;
> +        uint32_t pprev2;
> +
> +        uint32_t * E = (uint32_t *)(output->data[0] + y * output->linesize[0] * 2);
> +

> +        /* middle. the -2 just makes the offsets later on work out */
> +        uint32_t * sa2 = (uint32_t *)(input->data[0] + y * input->linesize[0] - 8);

what "-2"?

> +        /* up one */
> +        uint32_t * sa1 = sa2 - (input->linesize[0]>>2);
> +        /* up two */
> +        uint32_t * sa0 = sa1 - (input->linesize[0]>>2);
> +        /* down one */
> +        uint32_t * sa3 = sa2 + (input->linesize[0]>>2);
> +        /* down two */
> +        uint32_t * sa4 = sa3 + (input->linesize[0]>>2);
> +
> +        if (y <= 1) { 
> +            sa0 = sa1;
> +            if (y == 0) {
> +                sa0 = sa1 = sa2;
> +            }
> +        }
> +
> +        if (y >= input->height - 2) {
> +            sa4 = sa3;
> +            if (y == input->height - 1) {
> +                sa4 = sa3 = sa2;
> +            }
> +        }
> +
> +        pprev = pprev2 = 2;
> +
> +        for (x = 0; x < input->width; x++) {
> +            uint32_t B1 = sa0[2];
> +            uint32_t PB = sa1[2];
> +            uint32_t PE = sa2[2];
> +            uint32_t PH = sa3[2];
> +            uint32_t H5 = sa4[2];
> +
> +            uint32_t A1 = sa0[pprev];
> +            uint32_t PA = sa1[pprev];
> +            uint32_t PD = sa2[pprev];
> +            uint32_t PG = sa3[pprev];
> +            uint32_t G5 = sa4[pprev];
> +
> +            uint32_t A0 = sa1[pprev2];
> +            uint32_t D0 = sa2[pprev2];
> +            uint32_t G0 = sa3[pprev2];
> +
> +            uint32_t C1 = 0;
> +            uint32_t PC = 0;
> +            uint32_t PF = 0;
> +            uint32_t PI = 0;
> +            uint32_t I5 = 0;
> +
> +            uint32_t C4 = 0;
> +            uint32_t F4 = 0;
> +            uint32_t I4 = 0;
> +
> +            if (x >= input->width - 2) {
> +                if (x == input->width - 1) {
> +                    C1 = sa0[2];
> +                    PC = sa1[2];
> +                    PF = sa2[2];
> +                    PI = sa3[2];
> +                    I5 = sa4[2];
> +
> +                    C4 = sa1[2];
> +                    F4 = sa2[2];
> +                    I4 = sa3[2];
> +                } else {
> +                    C1 = sa0[3];
> +                    PC = sa1[3];
> +                    PF = sa2[3];
> +                    PI = sa3[3];
> +                    I5 = sa4[3];
> +
> +                    C4 = sa1[3];
> +                    F4 = sa2[3];
> +                    I4 = sa3[3];
> +                }
> +            } else {
> +                C1 = sa0[3];
> +                PC = sa1[3];
> +                PF = sa2[3];
> +                PI = sa3[3];
> +                I5 = sa4[3];
> +
> +                C4 = sa1[4];
> +                F4 = sa2[4];
> +                I4 = sa3[4];
> +            }
> +
> +            E[0] = E[1] = E[nextOutputLine] = E[nextOutputLine + 1] = PE; // 0, 1, 2, 3
> +
> +            FILTRO(PE, PI, PH, PF, PG, PC, PD, PB, PA, G5, C4, G0, D0, C1, B1, F4, I4, H5, I5, A0, A1, 0, 1, nextOutputLine, nextOutputLine+1,r2y);
> +            FILTRO(PE, PC, PF, PB, PI, PA, PH, PD, PG, I4, A1, I5, H5, A0, D0, B1, C1, F4, C4, G5, G0, nextOutputLine, 0, nextOutputLine+1, 1,r2y);
> +            FILTRO(PE, PA, PB, PD, PC, PG, PF, PH, PI, C1, G0, C4, F4, G5, H5, D0, A0, B1, A1, I4, I5, nextOutputLine+1, nextOutputLine, 1, 0,r2y);
> +            FILTRO(PE, PG, PD, PH, PA, PI, PB, PF, PC, A0, I5, A1, B1, I4, F4, H5, G5, D0, G0, C1, C4, 1, nextOutputLine+1, 0, nextOutputLine,r2y);        	
> +
> +            sa0 += 1;
> +            sa1 += 1;
> +            sa2 += 1;
> +            sa3 += 1;
> +            sa4 += 1;
> +
> +            E += 2;
> +
> +            if (pprev2){
> +                pprev2--;
> +                pprev = 1;
> +            }
> +        }
> +    }
> +}
> +#undef FILTRO
> +
> +
> +static int config_output(AVFilterLink *outlink)
> +{
> +    AVFilterContext *ctx = outlink->src;
> +    AVFilterLink *inlink = ctx->inputs[0];
> +
> +    outlink->w = inlink->w * 2 ;
> +    outlink->h = inlink->h * 2 ;

    outlink->w = inlink->w * 2;
    outlink->h = inlink->h * 2;

note spacing before ";", spacing is as in English prose

> +    return 0;
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    static const enum AVPixelFormat pix_fmts[] = {
> +        AV_PIX_FMT_0RGB32, AV_PIX_FMT_NONE,
> +    };
> +
> +    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
> +    return 0;
> +}
> +
> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    AVFilterLink *outlink = ctx->outputs[0];
> +    xBRContext *xBR = ctx->priv;
> +    const uint32_t *r2y = xBR->rgbtoyuv;
> +
> +    AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
> +    if (!out) {
> +        av_frame_free(&in);
> +        return AVERROR(ENOMEM);
> +    }
> +
> +    av_frame_copy_props(out, in);
> +    xbr2x(in,out,r2y);
> +
> +    out->width  = outlink->w;
> +    out->height = outlink->h;
> +
> +    av_frame_free(&in);
> +    return ff_filter_frame(outlink, out);
> +}
> +
> +static int init(AVFilterContext *ctx)
> +{
> +    xBRContext *xbr = ctx->priv;
> +    uint32_t c;
> +    int bg, rg, g;
> +
> +    for (bg=-255; bg<256; bg++) {
> +        for (rg=-255; rg<256; rg++) {
> +            const uint32_t u = (uint32_t)((-169*rg + 500*bg)/1000) + 128;
> +            const uint32_t v = (uint32_t)(( 500*rg -  81*bg)/1000) + 128;
> +            int startg = FFMAX3(-bg, -rg, 0);
> +            int endg = FFMIN3(255-bg, 255-rg, 255);
> +            uint32_t y = (uint32_t)(( 299*rg + 1000*startg + 114*bg)/1000);
> +            c = bg + (rg<<16) + 0x010101 * startg;
> +            for (g = startg; g <= endg; g++) {
> +                xbr->rgbtoyuv[c] = ((y++) << 16) + (u << 8) + v;
> +                c+= 0x010101;
> +            }
> +        }
> +    }
> +    return 0;
> +}
> +
> +static const AVFilterPad xbr_inputs[] = {
> +    {
> +        .name         = "default",
> +        .type         = AVMEDIA_TYPE_VIDEO,
> +        .filter_frame = filter_frame,
> +    },
> +    { NULL }
> +};
> +
> +static const AVFilterPad xbr_outputs[] = {
> +    {
> +        .name         = "default",
> +        .type         = AVMEDIA_TYPE_VIDEO,
> +        .config_props = config_output,
> +    },
> +    { NULL }
> +};
> +
> +AVFilter ff_vf_xbr = {
> +    .name          = "xbr",
> +    .description   = NULL_IF_CONFIG_SMALL("Scale the input by 2 using xbr algorithm."),
> +    .inputs        = xbr_inputs,
> +    .outputs       = xbr_outputs,
> +    .query_formats = query_formats,
> +    .priv_size     = sizeof(xBRContext),
> +    .init          = init,
> +};

Looks good otherwise, assuming the output is "visually" comparable
with the reference or with output references found on the web (please
link some reference input/output image on the web).
-- 
FFmpeg = Formidable and Fantastic Monstrous Picky Experimenting Generator


More information about the ffmpeg-devel mailing list