Browse code

lavfi: add geq filter.

Clément Bœsch authored on 2012/05/15 02:03:19
Showing 8 changed files
... ...
@@ -22,6 +22,7 @@ version <next>:
22 22
 - metadata (info chunk) support in CAF muxer
23 23
 - field filter ported from libmpcodecs
24 24
 - AVR demuxer
25
+- geq filter ported from libmpcodecs
25 26
 
26 27
 
27 28
 version 1.0:
... ...
@@ -30,6 +30,7 @@ Specifically, the GPL parts of FFmpeg are
30 30
     - vf_cropdetect.c
31 31
     - vf_decimate.c
32 32
     - vf_delogo.c
33
+    - vf_geq.c
33 34
     - vf_hqdn3d.c
34 35
     - vf_hue.c
35 36
     - vf_mp.c
... ...
@@ -1939,6 +1939,7 @@ frei0r_filter_deps="frei0r dlopen"
1939 1939
 frei0r_filter_extralibs='$ldl'
1940 1940
 frei0r_src_filter_deps="frei0r dlopen"
1941 1941
 frei0r_src_filter_extralibs='$ldl'
1942
+geq_filter_deps="gpl"
1942 1943
 hqdn3d_filter_deps="gpl"
1943 1944
 hue_filter_deps="gpl"
1944 1945
 movie_filter_deps="avcodec avformat"
... ...
@@ -2364,6 +2364,81 @@ frei0r=perspective:0.2/0.2:0.8/0.2
2364 2364
 For more information see:
2365 2365
 @url{http://frei0r.dyne.org}
2366 2366
 
2367
+@section geq
2368
+
2369
+The filter takes one, two or three equations as parameter, separated by ':'.
2370
+The first equation is mandatory and applies to the luma plane. The two
2371
+following are respectively for chroma blue and chroma red planes.
2372
+
2373
+The filter syntax allows named parameters:
2374
+
2375
+@table @option
2376
+@item lum_expr
2377
+the luminance expression
2378
+@item cb_expr
2379
+the chrominance blue expression
2380
+@item cr_expr
2381
+the chrominance red expression
2382
+@end table
2383
+
2384
+If one of the chrominance expression is not defined, it falls back on the other
2385
+one. If none of them are specified, they will evaluate the luminance
2386
+expression.
2387
+
2388
+The expressions can use the following variables and functions:
2389
+
2390
+@table @option
2391
+@item N
2392
+The sequential number of the filtered frame, starting from @code{0}.
2393
+
2394
+@item X, Y
2395
+The coordinates of the current sample.
2396
+
2397
+@item W, H
2398
+The width and height of the image.
2399
+
2400
+@item SW, SH
2401
+Width and height scale depending on the currently filtered plane. It is the
2402
+ratio between the corresponding luma plane number of pixels and the current
2403
+plane ones. E.g. for YUV4:2:0 the values are @code{1,1} for the luma plane, and
2404
+@code{0.5,0.5} for chroma planes.
2405
+
2406
+@item p(x, y)
2407
+Return the value of the pixel at location (@var{x},@var{y}) of the current
2408
+plane.
2409
+
2410
+@item lum(x, y)
2411
+Return the value of the pixel at location (@var{x},@var{y}) of the luminance
2412
+plane.
2413
+
2414
+@item cb(x, y)
2415
+Return the value of the pixel at location (@var{x},@var{y}) of the
2416
+blue-difference chroma plane.
2417
+
2418
+@item cr(x, y)
2419
+Return the value of the pixel at location (@var{x},@var{y}) of the
2420
+red-difference chroma plane.
2421
+@end table
2422
+
2423
+For functions, if @var{x} and @var{y} are outside the area, the value will be
2424
+automatically clipped to the closer edge.
2425
+
2426
+Some examples follow:
2427
+
2428
+@itemize
2429
+@item
2430
+Flip the image horizontally:
2431
+@example
2432
+geq=p(W-X\,Y)
2433
+@end example
2434
+
2435
+@item
2436
+Generate a fancy enigmatic moving light:
2437
+@example
2438
+nullsrc=s=256x256,geq=random(1)/hypot(X-cos(N*0.07)*W/2-W/2\,Y-sin(N*0.09)*H/2-H/2)^2*1000000*sin(N*0.02):128:128
2439
+@end example
2440
+@end itemize
2441
+
2367 2442
 @section gradfun
2368 2443
 
2369 2444
 Fix the banding artifacts that are sometimes introduced into nearly flat
... ...
@@ -4469,9 +4544,9 @@ color=c=red@@0.2:s=qcif:r=10
4469 4469
 
4470 4470
 If the input content is to be ignored, @code{nullsrc} can be used. The
4471 4471
 following command generates noise in the luminance plane by employing
4472
-the @code{mp=geq} filter:
4472
+the @code{geq} filter:
4473 4473
 @example
4474
-nullsrc=s=256x256, mp=geq=random(1)*255:128:128
4474
+nullsrc=s=256x256, geq=random(1)*255:128:128
4475 4475
 @end example
4476 4476
 
4477 4477
 @c man end VIDEO SOURCES
... ...
@@ -105,6 +105,7 @@ OBJS-$(CONFIG_FORMAT_FILTER)                 += vf_format.o
105 105
 OBJS-$(CONFIG_FRAMESTEP_FILTER)              += vf_framestep.o
106 106
 OBJS-$(CONFIG_FPS_FILTER)                    += vf_fps.o
107 107
 OBJS-$(CONFIG_FREI0R_FILTER)                 += vf_frei0r.o
108
+OBJS-$(CONFIG_GEQ_FILTER)                    += vf_geq.o
108 109
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
109 110
 OBJS-$(CONFIG_HFLIP_FILTER)                  += vf_hflip.o
110 111
 OBJS-$(CONFIG_HQDN3D_FILTER)                 += vf_hqdn3d.o
... ...
@@ -97,6 +97,7 @@ void avfilter_register_all(void)
97 97
     REGISTER_FILTER (FPS,         fps,         vf);
98 98
     REGISTER_FILTER (FRAMESTEP,   framestep,   vf);
99 99
     REGISTER_FILTER (FREI0R,      frei0r,      vf);
100
+    REGISTER_FILTER (GEQ,         geq,         vf);
100 101
     REGISTER_FILTER (GRADFUN,     gradfun,     vf);
101 102
     REGISTER_FILTER (HFLIP,       hflip,       vf);
102 103
     REGISTER_FILTER (HQDN3D,      hqdn3d,      vf);
... ...
@@ -29,8 +29,8 @@
29 29
 #include "libavutil/avutil.h"
30 30
 
31 31
 #define LIBAVFILTER_VERSION_MAJOR  3
32
-#define LIBAVFILTER_VERSION_MINOR  21
33
-#define LIBAVFILTER_VERSION_MICRO 108
32
+#define LIBAVFILTER_VERSION_MINOR  22
33
+#define LIBAVFILTER_VERSION_MICRO 100
34 34
 
35 35
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
36 36
                                                LIBAVFILTER_VERSION_MINOR, \
37 37
new file mode 100644
... ...
@@ -0,0 +1,237 @@
0
+/*
1
+ * Copyright (C) 2006 Michael Niedermayer <michaelni@gmx.at>
2
+ * Copyright (C) 2012 Clément Bœsch <ubitux@gmail.com>
3
+ *
4
+ * This file is part of FFmpeg.
5
+ *
6
+ * FFmpeg is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * FFmpeg is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public
17
+ * License along with FFmpeg; if not, write to the Free Software
18
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
+ */
20
+
21
+/**
22
+ * @file
23
+ * Generic equation change filter
24
+ * Originally written by Michael Niedermayer for the MPlayer project, and
25
+ * ported by Clément Bœsch for FFmpeg.
26
+ */
27
+
28
+#include "libavutil/avstring.h"
29
+#include "libavutil/eval.h"
30
+#include "libavutil/opt.h"
31
+#include "libavutil/pixdesc.h"
32
+#include "internal.h"
33
+
34
+typedef struct {
35
+    const AVClass *class;
36
+    AVExpr *e[3];               ///< expressions for each plane
37
+    char *expr_str[3];          ///< expression strings for each plane
38
+    int framenum;               ///< frame counter
39
+    AVFilterBufferRef *picref;  ///< current input buffer
40
+    int hsub, vsub;             ///< chroma subsampling
41
+} GEQContext;
42
+
43
+#define OFFSET(x) offsetof(GEQContext, x)
44
+#define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
45
+
46
+static const AVOption geq_options[] = {
47
+    { "lum_expr",   "set luminance expression",   OFFSET(expr_str),                   AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
48
+    { "cb_expr",    "set chroma blue expression", OFFSET(expr_str) +   sizeof(char*), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
49
+    { "cr_expr",    "set chroma red expression",  OFFSET(expr_str) + 2*sizeof(char*), AV_OPT_TYPE_STRING, {.str=NULL}, CHAR_MIN, CHAR_MAX, FLAGS },
50
+    {NULL},
51
+};
52
+
53
+AVFILTER_DEFINE_CLASS(geq);
54
+
55
+static inline double getpix(void *priv, double x, double y, int plane)
56
+{
57
+    int xi, yi;
58
+    GEQContext *geq = priv;
59
+    AVFilterBufferRef *picref = geq->picref;
60
+    const uint8_t *src = picref->data[plane];
61
+    const int linesize = picref->linesize[plane];
62
+    const int w = picref->video->w >> (plane ? geq->hsub : 0);
63
+    const int h = picref->video->h >> (plane ? geq->vsub : 0);
64
+
65
+    xi = x = av_clipf(x, 0, w - 2);
66
+    yi = y = av_clipf(y, 0, h - 2);
67
+
68
+    x -= xi;
69
+    y -= yi;
70
+
71
+    return (1-y)*((1-x)*src[xi +  yi    * linesize] + x*src[xi + 1 +  yi    * linesize])
72
+          +   y *((1-x)*src[xi + (yi+1) * linesize] + x*src[xi + 1 + (yi+1) * linesize]);
73
+}
74
+
75
+//TODO: cubic interpolate
76
+//TODO: keep the last few frames
77
+static double lum(void *priv, double x, double y) { return getpix(priv, x, y, 0); }
78
+static double  cb(void *priv, double x, double y) { return getpix(priv, x, y, 1); }
79
+static double  cr(void *priv, double x, double y) { return getpix(priv, x, y, 2); }
80
+
81
+static const char *const var_names[] = {   "X",   "Y",   "W",   "H",   "N",   "SW",   "SH",        NULL };
82
+enum                                   { VAR_X, VAR_Y, VAR_W, VAR_H, VAR_N, VAR_SW, VAR_SH, VAR_VARS_NB };
83
+
84
+static av_cold int geq_init(AVFilterContext *ctx, const char *args)
85
+{
86
+    GEQContext *geq = ctx->priv;
87
+    int plane, ret = 0;
88
+    static const char *shorthand[] = { "lum_expr", "cb_expr", "cr_expr", NULL };
89
+
90
+    geq->class = &geq_class;
91
+    av_opt_set_defaults(geq);
92
+
93
+    if ((ret = av_opt_set_from_string(geq, args, shorthand, "=", ":")) < 0)
94
+        return ret;
95
+
96
+    if (!geq->expr_str[0]) {
97
+        av_log(ctx, AV_LOG_ERROR, "Luminance expression is mandatory\n");
98
+        ret = AVERROR(EINVAL);
99
+        goto end;
100
+    }
101
+
102
+    if (!geq->expr_str[1] && !geq->expr_str[2]) {
103
+        /* No chroma at all: fallback on luma */
104
+        geq->expr_str[1] = av_strdup(geq->expr_str[0]);
105
+        geq->expr_str[2] = av_strdup(geq->expr_str[0]);
106
+    } else {
107
+        /* One chroma unspecified, fallback on the other */
108
+        if (!geq->expr_str[1]) geq->expr_str[1] = av_strdup(geq->expr_str[2]);
109
+        if (!geq->expr_str[2]) geq->expr_str[2] = av_strdup(geq->expr_str[1]);
110
+    }
111
+
112
+    if (!geq->expr_str[1] || !geq->expr_str[2]) {
113
+        ret = AVERROR(ENOMEM);
114
+        goto end;
115
+    }
116
+
117
+    for (plane = 0; plane < 3; plane++) {
118
+        static double (*p[])(void *, double, double) = { lum, cb, cr };
119
+        static const char *const func2_names[]    = { "lum", "cb", "cr", "p", NULL };
120
+        double (*func2[])(void *, double, double) = { lum, cb, cr, p[plane], NULL };
121
+
122
+        ret = av_expr_parse(&geq->e[plane], geq->expr_str[plane], var_names,
123
+                            NULL, NULL, func2_names, func2, 0, ctx);
124
+        if (ret < 0)
125
+            break;
126
+    }
127
+
128
+end:
129
+    return ret;
130
+}
131
+
132
+static int geq_query_formats(AVFilterContext *ctx)
133
+{
134
+    static const enum PixelFormat pix_fmts[] = {
135
+        AV_PIX_FMT_YUV444P,  AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV420P,
136
+        AV_PIX_FMT_YUV411P,  AV_PIX_FMT_YUV410P,  AV_PIX_FMT_YUV440P,
137
+        AV_PIX_FMT_YUVA420P,
138
+        AV_PIX_FMT_NONE
139
+    };
140
+    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
141
+    return 0;
142
+}
143
+
144
+static int geq_config_props(AVFilterLink *inlink)
145
+{
146
+    GEQContext *geq = inlink->dst->priv;
147
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format);
148
+
149
+    geq->hsub = desc->log2_chroma_w;
150
+    geq->vsub = desc->log2_chroma_h;
151
+    return 0;
152
+}
153
+
154
+static int geq_end_frame(AVFilterLink *inlink)
155
+{
156
+    int ret, plane;
157
+    GEQContext *geq = inlink->dst->priv;
158
+    AVFilterLink *outlink = inlink->dst->outputs[0];
159
+    AVFilterBufferRef *outpicref = outlink->out_buf;
160
+    double values[VAR_VARS_NB] = {
161
+        [VAR_N] = geq->framenum++,
162
+    };
163
+
164
+    geq->picref = inlink->cur_buf;
165
+
166
+    for (plane = 0; plane < 3; plane++) {
167
+        int x, y;
168
+        uint8_t *dst = outpicref->data[plane];
169
+        const int linesize = outpicref->linesize[plane];
170
+        const int w = inlink->w >> (plane ? geq->hsub : 0);
171
+        const int h = inlink->h >> (plane ? geq->vsub : 0);
172
+
173
+        values[VAR_W]  = w;
174
+        values[VAR_H]  = h;
175
+        values[VAR_SW] = w / (double)inlink->w;
176
+        values[VAR_SH] = h / (double)inlink->h;
177
+
178
+        for (y = 0; y < h; y++) {
179
+            values[VAR_Y] = y;
180
+            for (x = 0; x < w; x++) {
181
+                values[VAR_X] = x;
182
+                dst[x] = av_expr_eval(geq->e[plane], values, geq);
183
+            }
184
+            dst += linesize;
185
+        }
186
+    }
187
+
188
+    if ((ret = ff_draw_slice(outlink, 0, outlink->h, 1)) < 0 ||
189
+        (ret = ff_end_frame(outlink)) < 0)
190
+        return ret;
191
+    return 0;
192
+}
193
+
194
+static av_cold void geq_uninit(AVFilterContext *ctx)
195
+{
196
+    int i;
197
+    GEQContext *geq = ctx->priv;
198
+
199
+    for (i = 0; i < FF_ARRAY_ELEMS(geq->e); i++)
200
+        av_expr_free(geq->e[i]);
201
+    av_opt_free(geq);
202
+}
203
+
204
+static int null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir) { return 0; }
205
+
206
+static const AVFilterPad geq_inputs[] = {
207
+    {
208
+        .name         = "default",
209
+        .type         = AVMEDIA_TYPE_VIDEO,
210
+        .draw_slice   = null_draw_slice,
211
+        .config_props = geq_config_props,
212
+        .end_frame    = geq_end_frame,
213
+        .min_perms    = AV_PERM_READ,
214
+    },
215
+    { NULL }
216
+};
217
+
218
+static const AVFilterPad geq_outputs[] = {
219
+    {
220
+        .name = "default",
221
+        .type = AVMEDIA_TYPE_VIDEO,
222
+    },
223
+    { NULL }
224
+};
225
+
226
+AVFilter avfilter_vf_geq = {
227
+    .name          = "geq",
228
+    .description   = NULL_IF_CONFIG_SMALL("Apply generic equation to each pixel."),
229
+    .priv_size     = sizeof(GEQContext),
230
+    .init          = geq_init,
231
+    .uninit        = geq_uninit,
232
+    .query_formats = geq_query_formats,
233
+    .inputs        = geq_inputs,
234
+    .outputs       = geq_outputs,
235
+    .priv_class    = &geq_class,
236
+};