[FFmpeg-devel] [PATCH] Unsharp filter
Stefano Sabatini
stefano.sabatini-lala
Wed Mar 31 22:50:29 CEST 2010
On date Friday 2010-03-26 11:21:39 -0400, Daniel G. Taylor encoded:
> On 03/25/2010 10:18 PM, Bobby Bingham wrote:
[...]
> Index: doc/libavfilter.texi
> ===================================================================
> --- doc/libavfilter.texi (revision 22666)
> +++ doc/libavfilter.texi (working copy)
> @@ -217,6 +217,31 @@
> 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{luma_msize_x}:@var{luma_msize_y}:@var{luma_amount}:
> + at var{chroma_msize_x}:@var{chroma_msize_y}:@var{chroma_amount}.
> + at var{luma_msize_x}, @var{luma_msize_y}, @var{chroma_msize_x},
> +and @var{chroma_msize_y} define the effect matrix size for luma and chroma
> +respectively, and should be integer values between 3 and 13.
> + at var{luma_amount} and @var{chroma_amount} define the strength of the effect
> +with sane values in the -2.0 to 5.0 range, where negative values cause a
> +blur effect and positive values cause a sharpen effect on the image.
> +All parameters are optional and default to the equivalent of
> +'5:5:1.0:0:0:0.0'.
Uhm uhm, with all these parameters I'd say to use a table like it's
done for the pad docs.
BTW I believe we should have man page for filters (something a la git
of the kind man ffmpeg filters, man ffmpeg protocols, man ffmpeg
codecs etc...).
> +
> + at example
> +# Strong luma sharpen effect parameters
> +unsharp=7:7:2.5
> +
> +# Strong blur of both luma and chroma parameters
> +unsharp=7:7:-2.5:7:7:-2.5
> +
> +# Use the default values with @file{ffmpeg}
> +./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 22666)
> +++ 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,238 @@
> +/*
> + * 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"
> +#include "libavutil/pixdesc.h"
> +
> +#define MIN_SIZE 3
> +#define MAX_SIZE 13
> +
> +#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)
while at it this could be aligned
> +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
> +
> +*/
This may be placed at the top of the file, just inside the @file
comment block.
> +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->scalebits - 1);
> +}
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> +{
> + UnsharpContext *unsharp = ctx->priv;
> + int lmsize_x = 5, cmsize_x = 0;
> + int lmsize_y = 5, cmsize_y = 0;
> + double lamount = 1.0f, camount = 0.0f;
> +
> + if (args)
> + sscanf(args, "%d:%d:%lf:%d:%d:%lf", &lmsize_x, &lmsize_y, &lamount,
> + &cmsize_x, &cmsize_y, &camount);
> +
> + set_filter_param(&unsharp->luma, lmsize_x, lmsize_y, lamount);
> + set_filter_param(&unsharp->chroma, cmsize_x, cmsize_y, camount);
> +
> + return 0;
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> + enum PixelFormat pix_fmts[] = {
> + PIX_FMT_YUV420P, PIX_FMT_YUV422P, PIX_FMT_YUV444P, PIX_FMT_YUV410P,
> + PIX_FMT_YUV411P, PIX_FMT_YUV440P, PIX_FMT_YUVJ420P, PIX_FMT_YUVJ422P,
> + PIX_FMT_YUVJ444P, PIX_FMT_YUVJ440P, 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, "effect:%s type:%s msize_x:%d msize_y:%d amount:%0.2f\n", effect, effect_type, fp->msize_x, fp->msize_y, fp->amount / 65535.0);
long line, please broke it.
> + 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", CHROMA_WIDTH(link));
> +
> + 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);
> + unsharpen(out->data[1], in->data[1], out->linesize[1], in->linesize[1], CHROMA_WIDTH(link), CHROMA_HEIGHT(link), &unsharp->chroma);
weird align.
> + unsharpen(out->data[2], in->data[2], out->linesize[2], in->linesize[2], CHROMA_WIDTH(link), CHROMA_HEIGHT(link), &unsharp->chroma);
> +
> + avfilter_unref_pic(in);
> + avfilter_draw_slice(link->dst->outputs[0], 0, link->h, 1);
> + avfilter_end_frame(link->dst->outputs[0]);
> + avfilter_unref_pic(out);
> +}
> +
> +static void draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
> +{
> +}
> +
> +AVFilter avfilter_vf_unsharp = {
> + .name = "unsharp",
> + .description = NULL_IF_CONFIG_SMALL("Sharpen or blur the input video."),
> +
> + .priv_size = sizeof(UnsharpContext),
> +
> + .init = init,
> + .uninit = uninit,
> + .query_formats = query_formats,
> +
> + .inputs = (AVFilterPad[]) {{ .name = "default",
> + .type = CODEC_TYPE_VIDEO,
This is AVMEDIA_TYPE_VIDEO, here and below.
> + .draw_slice = draw_slice,
> + .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}},
> +};
> Index: libavfilter/Makefile
> ===================================================================
> --- libavfilter/Makefile (revision 22666)
> +++ libavfilter/Makefile (working copy)
> @@ -22,6 +22,7 @@
> OBJS-$(CONFIG_PIXELASPECT_FILTER) += vf_aspect.o
> OBJS-$(CONFIG_SCALE_FILTER) += vf_scale.o
> OBJS-$(CONFIG_SLICIFY_FILTER) += vf_slicify.o
> +OBJS-$(CONFIG_UNSHARP_FILTER) += vf_unsharp.o
> OBJS-$(CONFIG_VFLIP_FILTER) += vf_vflip.o
>
> OBJS-$(CONFIG_NULLSRC_FILTER) += vsrc_nullsrc.o
Apart from these pedantic nits I don't have more to say, but wait for
Bobby/Michael review.
Also is OK to apply this in the main SVN or should it go to the GSOC
one?
Regards.
--
FFmpeg = Friendly and Freak Mega Problematic Eretic Gorilla
More information about the ffmpeg-devel
mailing list