[FFmpeg-devel] [PATCH] Unsharp filter

Stefano Sabatini stefano.sabatini-lala
Thu Mar 25 01:01:11 CET 2010


On date Wednesday 2010-03-24 12:03:54 -0400, Daniel G. Taylor encoded:
> On 03/23/2010 07:31 PM, Stefano Sabatini wrote:
> >On date Tuesday 2010-03-23 17:37:21 -0400, Daniel G. Taylor encoded:
> >>Hey,
> >Hi, and thanks for the patch!
> >
> >Follows a list of nitpicks and maybe some more useful comment.
> 
> Thanks for the comments! Update patch attached, comments inline.
[...]
> Index: doc/libavfilter.texi
> ===================================================================
> --- doc/libavfilter.texi	(revision 22654)
> +++ doc/libavfilter.texi	(working copy)
> @@ -217,6 +217,28 @@
>  Adding this in the beginning of filter chains should make filtering
>  faster due to better use of the memory cache.
>  
> + at section unsharp
> +
> +Sharpen or blur the input video. It accepts the following parameters:
> + at var{effect_type}:@var{msize_x}:@var{msize_y}:@var{amount}.
> + at var{effect_type} can be one of 'luma', 'chroma', or 'both'.
> + at var{msize_x} and @var{msize_y} define the effect matrix size should be

missing "," before "size should be..."

> +integer values between 3 and 13. @var{amount} defines the strength of
> +the effect with sane values in the -2.0 to 5.0 range with negative values

missing ", " before "with negative values..."

> +causing a blur effect and positive values sharpening the image. All
> +parameters are optional and default to the equivalent of 'luma:5:5:1.0'.
> +
> + at example
> +# Strong sharpen effect parameters
> +unsharp=luma:7:7:2.5
> +
> +# Strong blur of both luma and chroma parameters
> +unsharp=both:7:7:-2.5
> +
> +# Use the default values with FFmpeg

I prefer just ffmpeg (maybe @filename{ffmpeg} or the equivalent),
FFmpeg is mainly used for referring the the whole project.

> +./ffmpeg -i in.avi -vfilters "unsharp" out.mp4
> + at end example
> +
>  @section vflip
>  
>  Flip the input video vertically.
> Index: libavfilter/allfilters.c
> ===================================================================
> --- libavfilter/allfilters.c	(revision 22654)
> +++ libavfilter/allfilters.c	(working copy)
> @@ -42,6 +42,7 @@
>      REGISTER_FILTER (PIXELASPECT, pixelaspect, vf);
>      REGISTER_FILTER (SCALE,       scale,       vf);
>      REGISTER_FILTER (SLICIFY,     slicify,     vf);
> +    REGISTER_FILTER (UNSHARP,     unsharp,     vf);
>      REGISTER_FILTER (VFLIP,       vflip,       vf);
>  
>      REGISTER_FILTER (NULLSRC,     nullsrc,     vsrc);
> Index: libavfilter/vf_unsharp.c
> ===================================================================
> --- libavfilter/vf_unsharp.c	(revision 0)
> +++ libavfilter/vf_unsharp.c	(revision 0)
> @@ -0,0 +1,250 @@
> +/*
> + * Ported to FFmpeg from MPlayer libmpcodecs/unsharp.c
> + * Original copyright (C) 2002 Remi Guyomarch <rguyom at pobox.com>
> + * Port copyright (C) 2010 Daniel G. Taylor <dan at programmer-art.org>
> + *
> + * 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
> + * Lesser 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 libavfilter/vf_unsharp.c
> + * blur / sharpen filter
> + */
> +
> +#include "avfilter.h"
> +#include "libavutil/common.h"
> +#include "libavutil/mem.h"
> +
> +#define MIN_SIZE 3
> +#define MAX_SIZE 13
> +
> +typedef struct FilterParam {
> +    int msize_x;                             ///< matrix width
> +    int msize_y;                             ///< matrix height
> +    int amount;                              ///< effect amount
> +    int steps_x;                             ///< horizontal step count
> +    int steps_y;                             ///< vertical step count
> +    int scalebits;                           ///< bits to shift pixel
> +    int32_t halfscale;                       ///< amount to add to pixel
> +    uint32_t *sc[(MAX_SIZE * MAX_SIZE) - 1]; ///< finite state machine storage
> +} FilterParam;
> +
> +typedef struct {
> +    FilterParam luma;   ///< luma parameters (width, height, amount)
> +    FilterParam chroma; ///< chroma parameters (width, height, amount)
> +} UnsharpContext;
> +
> +//===========================================================================//
> +
> +/* This code is based on:
> +
> +An Efficient algorithm for Gaussian blur using finite-state machines
> +Frederick M. Waltz and John W. V. Miller
> +
> +SPIE Conf. on Machine Vision Systems for Inspection and Metrology VII
> +Originally published Boston, Nov 98
> +
> +http://www.engin.umd.umich.edu/~jwvm/ece581/21_GBlur.pdf
> +
> +*/
> +
> +static void unsharpen(uint8_t *dst, uint8_t *src, int dst_stride, int src_stride, int width, int height, FilterParam *fp)
> +{
> +    uint32_t **sc = fp->sc;
> +    uint32_t sr[(MAX_SIZE * MAX_SIZE) - 1], tmp1, tmp2;
> +
> +    int32_t res;
> +    int x, y, z;
> +
> +    if (!fp->amount) {
> +        if (dst_stride == src_stride)
> +            memcpy(dst, src, src_stride * height);
> +        else
> +            for (y = 0; y < height; y++, dst += dst_stride, src += src_stride)
> +                memcpy(dst, src, width);
> +        return;
> +    }
> +
> +    for (y = 0; y < 2 * fp->steps_y; y++)
> +        memset(sc[y], 0, sizeof(sc[y][0]) * (width + 2 * fp->steps_x));
> +
> +    for (y =- fp->steps_y; y < height + fp->steps_y; y++) {
> +        memset(sr, 0, sizeof(sr[0]) * (2 * fp->steps_x - 1));
> +        for (x =- fp->steps_x; x < width + fp->steps_x; x++) {
> +            tmp1 = x <= 0 ? src[0] : x >= width ? src[width-1] : src[x];
> +            for (z = 0; z < fp->steps_x * 2; z += 2) {
> +                tmp2 = sr[z + 0] + tmp1; sr[z + 0] = tmp1;
> +                tmp1 = sr[z + 1] + tmp2; sr[z + 1] = tmp2;
> +            }
> +            for (z = 0; z < fp->steps_y * 2; z += 2) {
> +                tmp2 = sc[z + 0][x + fp->steps_x] + tmp1; sc[z + 0][x + fp->steps_x] = tmp1;
> +                tmp1 = sc[z + 1][x + fp->steps_x] + tmp2; sc[z + 1][x + fp->steps_x] = tmp2;
> +            }
> +            if (x >= fp->steps_x && y >= fp->steps_y) {
> +                uint8_t* srx = src - fp->steps_y * src_stride + x - fp->steps_x;
> +                uint8_t* dsx = dst - fp->steps_y * dst_stride + x - fp->steps_x;
> +
> +                res = (int32_t)*srx + ((((int32_t) * srx - (int32_t)((tmp1 + fp->halfscale) >> fp->scalebits)) * fp->amount) >> 16);
> +                *dsx = av_clip_uint8(res);
> +            }
> +        }
> +        if (y >= 0) {
> +            dst += dst_stride;
> +            src += src_stride;
> +        }
> +    }
> +}
> +
> +static void set_filter_param(FilterParam *fp, int msize_x, int msize_y, double amount)
> +{
> +    fp->msize_x = msize_x;
> +    fp->msize_y = msize_y;
> +    fp->amount = amount * 65536.0;
> +
> +    fp->steps_x = msize_x / 2;
> +    fp->steps_y = msize_y / 2;
> +    fp->scalebits = (fp->steps_x + fp->steps_y) * 2;
> +    fp->halfscale = 1 << ((fp->steps_x + fp->steps_y) * 2 - 1);
> +}
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> +{
> +    UnsharpContext *unsharp = ctx->priv;
> +    char type;
> +    int msize_x, msize_y;
> +    double amount;
> +
> +    type = 'l';
> +    msize_x = 5;
> +    msize_y = 5;
> +    amount = 1.0f;

you can merge definition and declaration and save some lines

> +
> +    if (args) {
> +        if (strncmp(args, "luma", 4) == 0) {
> +            type = 'l';
> +            args += 5;
> +        } else if (strncmp(args, "chroma", 6) == 0) {
> +            type = 'c';
> +            args += 7;
> +        } else if (strncmp(args, "both", 4) == 0) {
> +            type = 'b';
> +            args += 5;
> +        } else
> +            av_log(ctx, AV_LOG_ERROR, "Invalid argument, should be one of 'luma', 'chroma', or 'both' - using defaults instead\n");
> +
> +        sscanf(args, "%d:%d:%lf", &msize_x, &msize_y, &amount);
> +
> +        msize_x = FFMIN(MAX_SIZE, FFMAX(MIN_SIZE, msize_x));
> +        msize_y = FFMIN(MAX_SIZE, FFMAX(MIN_SIZE, msize_y));
> +    }
> +
> +    if (type == 'l' || type == 'b')
> +        set_filter_param(&unsharp->luma, msize_x, msize_y, amount);
> +
> +    if (type == 'c' || type == 'b')
> +        set_filter_param(&unsharp->chroma, msize_x, msize_y, amount);
> +
> +    return 0;
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    enum PixelFormat pix_fmts[] = {
> +        PIX_FMT_YUV420P, PIX_FMT_NONE
> +    };
> +
> +    avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts));
> +
> +    return 0;
> +}
> +
> +static void init_filter_param(AVFilterContext *ctx, FilterParam *fp, const char *effect_type, int width)
> +{
> +    int z;
> +    const char *effect;
> +
> +    effect = fp->amount == 0 ? "none" : fp->amount < 0 ? "blur" : "sharpen";
> +
> +    av_log(ctx, AV_LOG_INFO, "msize_x:%d msize_y:%d amount:%0.2f effect:%s type:%s\n", fp->msize_x, fp->msize_y, fp->amount / 65535.0, effect, effect_type);
> +
> +    memset(fp->sc, 0, sizeof(fp->sc));
> +    for (z = 0; z < 2 * fp->steps_y; z++)
> +        fp->sc[z] = av_malloc(sizeof(*(fp->sc[z])) * (width + 2 * fp->steps_x));
> +}
> +
> +static int config_props(AVFilterLink *link)
> +{
> +    UnsharpContext *unsharp = link->dst->priv;
> +
> +    init_filter_param(link->dst, &unsharp->luma,   "luma",   link->w);
> +    init_filter_param(link->dst, &unsharp->chroma, "chroma", link->w);
> +
> +    return 0;
> +}
> +
> +static void free_filter_param(FilterParam *fp)
> +{
> +    int z;
> +
> +    for (z = 0; z < 2 * fp->steps_y; z++)
> +        av_free(fp->sc[z]);
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    UnsharpContext *unsharp = ctx->priv;
> +
> +    free_filter_param(&unsharp->luma);
> +    free_filter_param(&unsharp->chroma);
> +}
> +
> +static void end_frame(AVFilterLink *link)
> +{
> +    UnsharpContext *unsharp = link->dst->priv;
> +    AVFilterPicRef *in  = link->cur_pic;
> +    AVFilterPicRef *out = link->dst->outputs[0]->outpic;
> +

> +    unsharpen(out->data[0], in->data[0], out->linesize[0], in->linesize[0], link->w, link->h, &unsharp->luma);

NIT+ vertical align

> +    unsharpen(out->data[1], in->data[1], out->linesize[1], in->linesize[1], link->w / 2, link->h / 2, &unsharp->chroma);
> +    unsharpen(out->data[2], in->data[2], out->linesize[2], in->linesize[2], link->w / 2, link->h / 2, &unsharp->chroma);
> +
> +    avfilter_unref_pic(in);
> +    avfilter_end_frame(link->dst->outputs[0]);
> +}
> +
> +AVFilter avfilter_vf_unsharp = {
> +    .name      = "unsharp",
> +    .description = NULL_IF_CONFIG_SMALL("Sharpen or blur the input video"),

missing final dot.

> +
> +    .priv_size = sizeof(UnsharpContext),
> +
> +    .init = init,
> +    .uninit = uninit,
> +    .query_formats = query_formats,
> +
> +    .inputs    = (AVFilterPad[]) {{ .name             = "default",
> +                                    .type             = CODEC_TYPE_VIDEO,
> +                                    .end_frame        = end_frame,
> +                                    .config_props     = config_props,
> +                                    .min_perms        = AV_PERM_READ, },
> +                                  { .name = NULL}},
> +
> +    .outputs   = (AVFilterPad[]) {{ .name             = "default",
> +                                    .type             = CODEC_TYPE_VIDEO, },
> +                                  { .name = NULL}},
> +};

Missing empty definition of draw_slice(), try for example 
"unsharp, hflip" to see what happens, also don't forget that you can
trace what the hell is going in the filterchain enabling DEBUG in
avfilter.c and using -loglevel debug in the ff* tools.

Also remember the GPL issue, check the function die_license_disabled()
in the configure.

Another very minor reserve which I have is the name of the filter,
maybe "sharpenblur" or something mentioning also "blur" may help
someone to immediately detect the filter, but then I leave to you the
final choice as the author of the filter (also maybe is a good idea to
keep the same name as the original MPlayer filter).

Regards.
-- 
FFmpeg = Fabulous and Friendly Martial Purposeless Evanescent God



More information about the ffmpeg-devel mailing list