... | ... |
@@ -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 |
+}; |