[FFmpeg-soc] [RFC] Apply function to pixel filter: vf_applyfn
Stefano Sabatini
stefano.sabatini-lala at poste.it
Sun May 24 23:46:02 CEST 2009
On date Sunday 2009-05-24 20:19:34 +0200, zeger at customsoft.nl encoded:
> Hi,
>
> A lot of image operations like gamma adjustment, color inversion or
> contrast enhancement can be expressed as a simple function applied
> to each pixel. I have written a YUV(A) only filter to apply these
> functions.
>
> For example: color inversion becomes applyfn=255-Y:255-U:255-V.
> If the pow function would be available a gamma correction would
> look like this: applyfn=255*pow(Y/255\,0.25):U:V
I believe pow *is* supported out of the box by the eval code.
> Zeger Knops
> Index: Makefile
> ===================================================================
> --- Makefile (revision 4305)
> +++ Makefile (working copy)
> @@ -14,6 +14,7 @@
> parseutils.o \
>
>
> +OBJS-$(CONFIG_APPLYFN_FILTER) += vf_applyfn.o
> OBJS-$(CONFIG_CROP_FILTER) += vf_crop.o
> OBJS-$(CONFIG_DRAWBOX_FILTER) += vf_drawbox.o
> OBJS-$(CONFIG_FPS_FILTER) += vf_fps.o
> Index: allfilters.c
> ===================================================================
> --- allfilters.c (revision 4305)
> +++ allfilters.c (working copy)
> @@ -34,6 +34,7 @@
> return;
> initialized = 1;
>
> + REGISTER_FILTER(APPLYFN,applyfn,vf);
> REGISTER_FILTER(CROP,crop,vf);
> REGISTER_FILTER(DRAWBOX,drawbox,vf);
> REGISTER_FILTER(FIFO,fifo,vf);
> Index: vf_applyfn.c
> ===================================================================
> --- vf_applyfn.c (revision 0)
> +++ vf_applyfn.c (revision 0)
> @@ -0,0 +1,227 @@
> +/*
> + * applyfn filter
> + * copyright (c) 2009 Zeger Knops
> + *
> + * 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
> + */
> +
> +/**
> + * Assume pow is available
> + * Gamma(g): applyfn=255*pow(Y/255\,g):U:V
> + *
> + * Assume the floor fn is available we can draw cartoons:
> + * applyfn=floor(Y/64)*64:floor(U/64)*64:floor(V/64)*64
> + *
> + * Assume the modulo fn is availabvle we can draw checkerboars
> + * applyfn=(modulo(floor(x/64)\,2)+modulo(floor(y/64)\,2))*Y
> + *
> + * B&W
> + * applyfn=Y:128:128
> + */
Docs and example go to doc/vfilters.texi.
> +#include "libavcodec/eval.h"
> +#include "avfilter.h"
> +#include "libavutil/avstring.h"
> +
> +enum PosOfValue {
> + POV_PI,
> + POV_E,
> + POV_N,
> + POV_W,
> + POV_H,
> + POV_x,
> + POV_y,
> + POV_Y,
> + POV_U,
> + POV_V,
> + POV_A,
> + POV_NULL
> +};
PosOfValue is not a good name, I believe:
enum VarName {
...
VARS_NB
};
is better.
And a PTS var may be useful too.
> +static const char *applyfn_symbols[POV_NULL+1]={
^^
Why +1?
> + "PI",
> + "E",
> + "N", ///< frame number (starting at zero)
> + "W", ///< frame width
> + "H", ///< frame height
> + "x", ///< x position of current pixel
> + "y", ///< y position of current pixel
> + "Y", ///< Y value of current pixel
> + "U", ///< U value of current pixel
> + "V", ///< V value of current pixel
> + "A", ///< A value of current pixel
> + NULL
> +};
>
> +typedef struct
> +{
> + AVEvalExpr *value_Y, *value_U, *value_V, *value_A;
> + int hsub, vsub;
> + double values[POV_NULL+1];
> +} ApplyfnContext;
> +
> +static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
> +{
> + ApplyfnContext *applyfn = ctx->priv;
> + char Y_expr[256], U_expr[256], V_expr[256], A_expr[256];
> + const char *error;
Weird indent.
> + applyfn->values[POV_PI ] = M_PI;
> + applyfn->values[POV_E ] = M_E;
> + applyfn->values[POV_N ] = 0.0;
> + applyfn->values[POV_NULL ] = 0.0;
> +
> + av_strlcpy(Y_expr, "Y", sizeof(Y_expr));
> + av_strlcpy(U_expr, "U", sizeof(U_expr));
> + av_strlcpy(V_expr, "V", sizeof(V_expr));
> + av_strlcpy(A_expr, "A", sizeof(V_expr));
> +
> + if (args)
> + sscanf(args, "%255[^:]:%255[^:]:%255[^:]:%255[^:]", Y_expr, U_expr, V_expr, A_expr)
I believe here it would be nice to use av_set_options_string, check
vf_pad.c in the archive for an usage example.
> +
> + applyfn->value_Y = ff_parse(Y_expr, applyfn_symbols,
> + NULL, NULL, NULL, NULL, &error);
> + if (!applyfn->value_Y )
> + av_log(ctx, AV_LOG_ERROR,
> + "init() cannot parse Y expression due to %s\n", error);
> +
> + applyfn->value_U = ff_parse(U_expr, applyfn_symbols,
> + NULL, NULL, NULL, NULL, &error);
> + if (!applyfn->value_U )
> + av_log(ctx, AV_LOG_ERROR,
> + "init() cannot parse U expression due to %s\n", error);
> +
> + applyfn->value_V = ff_parse(V_expr, applyfn_symbols,
> + NULL, NULL, NULL, NULL, &error);
> + if (!applyfn->value_V )
> + av_log(ctx, AV_LOG_ERROR,
> + "init() cannot parse V expression due to %s\n", error);
> +
> + applyfn->value_A = ff_parse(A_expr, applyfn_symbols,
> + NULL, NULL, NULL, NULL, &error);
> + if (!applyfn->value_A )
> + av_log(ctx, AV_LOG_ERROR,
> + "init() cannot parse A expression due to %s\n", error);
This can be factorized (check again vf_pad.c to see how), maybe even a
macro may be used.
Also I don't like the name of the function (init() in this case) to be
put in the error message, kinda redundant.
> + return !applyfn->value_Y || !applyfn->value_U || !applyfn->value_V || !applyfn->value_A;
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> + avfilter_set_common_formats(ctx,
> + avfilter_make_format_list(10,
> + PIX_FMT_YUV444P, PIX_FMT_YUV422P, PIX_FMT_YUV420P,
> + PIX_FMT_YUV411P, PIX_FMT_YUV410P,
> + PIX_FMT_YUVJ444P, PIX_FMT_YUVJ422P, PIX_FMT_YUVJ420P,
> + PIX_FMT_YUV440P, PIX_FMT_YUVJ440P,
> + PIX_FMT_YUVA420P));
> + return 0;
> +}
> +
> +static int config_props(AVFilterLink *link)
> +{
> + ApplyfnContext *applyfn = link->dst->priv;
> +
> + avcodec_get_chroma_sub_sample(link->format, &applyfn->hsub, &applyfn->vsub);
> +
> + return 0;
> +}
> +
> +static void draw_slice(AVFilterLink *link, int y, int h)
> +{
> + ApplyfnContext *applyfn = link->dst->priv;
> + AVFilterPicRef *in = link->cur_pic;
> + AVFilterPicRef *out = link->dst->outputs[0]->outpic;
> + uint8_t *inrow_0, *outrow_0, *alpharow, *outrow_1, *outrow_2, *inrow_1, *inrow_2;
> + int i, j, i_sub, j_sub, outrow_1_off, outrow_2_off, inrow_1_off, inrow_2_off;
> +
> + applyfn->values[POV_N] += 1.0;
Ouch, this is wrong, as draw_slice() is called for every slice(), and
you don't know how many slices there are. This update should be done
in start_frame() / end_frame() (same for W and H, since they're not
supposed to change between a slice and another one).
> + applyfn->values[POV_W] = link->w;
> + applyfn->values[POV_H] = h;
[...]
Regards and happy filtering.
More information about the FFmpeg-soc
mailing list