[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