Signed-off-by: Paul B Mahol <onemda@gmail.com>
Paul B Mahol authored on 2017/04/24 02:59:32... | ... |
@@ -9278,6 +9278,26 @@ Interpolate values using a tetrahedron. |
9278 | 9278 |
@end table |
9279 | 9279 |
@end table |
9280 | 9280 |
|
9281 |
+@section lumakey |
|
9282 |
+ |
|
9283 |
+Turn certain luma values into transparency. |
|
9284 |
+ |
|
9285 |
+The filter accepts the following options: |
|
9286 |
+ |
|
9287 |
+@table @option |
|
9288 |
+@item threshold |
|
9289 |
+Set the luma which will be used as base for transparency. |
|
9290 |
+Default value is @code{0}. |
|
9291 |
+ |
|
9292 |
+@item tolerance |
|
9293 |
+Set the range of luma values to be keyed out. |
|
9294 |
+Default value is @code{0}. |
|
9295 |
+ |
|
9296 |
+@item softness |
|
9297 |
+Set the range of softness. Default value is @code{0}. |
|
9298 |
+Use this to control gradual transition from zero to full transparency. |
|
9299 |
+@end table |
|
9300 |
+ |
|
9281 | 9301 |
@section lut, lutrgb, lutyuv |
9282 | 9302 |
|
9283 | 9303 |
Compute a look-up table for binding each pixel component input value |
... | ... |
@@ -212,6 +212,7 @@ OBJS-$(CONFIG_INTERLEAVE_FILTER) += f_interleave.o |
212 | 212 |
OBJS-$(CONFIG_KERNDEINT_FILTER) += vf_kerndeint.o |
213 | 213 |
OBJS-$(CONFIG_LENSCORRECTION_FILTER) += vf_lenscorrection.o |
214 | 214 |
OBJS-$(CONFIG_LOOP_FILTER) += f_loop.o |
215 |
+OBJS-$(CONFIG_LUMAKEY_FILTER) += vf_lumakey.o |
|
215 | 216 |
OBJS-$(CONFIG_LUT_FILTER) += vf_lut.o |
216 | 217 |
OBJS-$(CONFIG_LUT2_FILTER) += vf_lut2.o framesync.o |
217 | 218 |
OBJS-$(CONFIG_LUT3D_FILTER) += vf_lut3d.o |
... | ... |
@@ -223,6 +223,7 @@ static void register_all(void) |
223 | 223 |
REGISTER_FILTER(KERNDEINT, kerndeint, vf); |
224 | 224 |
REGISTER_FILTER(LENSCORRECTION, lenscorrection, vf); |
225 | 225 |
REGISTER_FILTER(LOOP, loop, vf); |
226 |
+ REGISTER_FILTER(LUMAKEY, lumakey, vf); |
|
226 | 227 |
REGISTER_FILTER(LUT, lut, vf); |
227 | 228 |
REGISTER_FILTER(LUT2, lut2, vf); |
228 | 229 |
REGISTER_FILTER(LUT3D, lut3d, vf); |
... | ... |
@@ -30,7 +30,7 @@ |
30 | 30 |
#include "libavutil/version.h" |
31 | 31 |
|
32 | 32 |
#define LIBAVFILTER_VERSION_MAJOR 6 |
33 |
-#define LIBAVFILTER_VERSION_MINOR 86 |
|
33 |
+#define LIBAVFILTER_VERSION_MINOR 87 |
|
34 | 34 |
#define LIBAVFILTER_VERSION_MICRO 100 |
35 | 35 |
|
36 | 36 |
#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ |
37 | 37 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,201 @@ |
0 |
+/* |
|
1 |
+ * Copyright (c) 2017 Paul B Mahol |
|
2 |
+ * |
|
3 |
+ * This file is part of FFmpeg. |
|
4 |
+ * |
|
5 |
+ * FFmpeg is free software; you can redistribute it and/or |
|
6 |
+ * modify it under the terms of the GNU Lesser General Public |
|
7 |
+ * License as published by the Free Software Foundation; either |
|
8 |
+ * version 2.1 of the License, or (at your option) any later version. |
|
9 |
+ * |
|
10 |
+ * FFmpeg is distributed in the hope that it will be useful, |
|
11 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
13 |
+ * Lesser General Public License for more details. |
|
14 |
+ * |
|
15 |
+ * You should have received a copy of the GNU Lesser General Public |
|
16 |
+ * License along with FFmpeg; if not, write to the Free Software |
|
17 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
18 |
+ */ |
|
19 |
+ |
|
20 |
+#include "libavutil/opt.h" |
|
21 |
+#include "libavutil/imgutils.h" |
|
22 |
+#include "avfilter.h" |
|
23 |
+#include "formats.h" |
|
24 |
+#include "internal.h" |
|
25 |
+#include "video.h" |
|
26 |
+ |
|
27 |
+typedef struct LumakeyContext { |
|
28 |
+ const AVClass *class; |
|
29 |
+ |
|
30 |
+ int threshold; |
|
31 |
+ int tolerance; |
|
32 |
+ int softness; |
|
33 |
+ |
|
34 |
+ int white; |
|
35 |
+ int black; |
|
36 |
+ int max; |
|
37 |
+ |
|
38 |
+ int (*do_lumakey_slice)(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs); |
|
39 |
+} LumakeyContext; |
|
40 |
+ |
|
41 |
+static int do_lumakey_slice8(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) |
|
42 |
+{ |
|
43 |
+ LumakeyContext *s = ctx->priv; |
|
44 |
+ AVFrame *frame = arg; |
|
45 |
+ const int slice_start = (frame->height * jobnr) / nb_jobs; |
|
46 |
+ const int slice_end = (frame->height * (jobnr + 1)) / nb_jobs; |
|
47 |
+ uint8_t *alpha = frame->data[3] + slice_start * frame->linesize[3]; |
|
48 |
+ const uint8_t *luma = frame->data[0] + slice_start * frame->linesize[0]; |
|
49 |
+ const int so = s->softness; |
|
50 |
+ const int w = s->white; |
|
51 |
+ const int b = s->black; |
|
52 |
+ int x, y; |
|
53 |
+ |
|
54 |
+ for (y = slice_start; y < slice_end; y++) { |
|
55 |
+ for (x = 0; x < frame->width; x++) { |
|
56 |
+ if (luma[x] >= b && luma[x] <= w) { |
|
57 |
+ alpha[x] = 0; |
|
58 |
+ } else if (luma[x] > b - so && luma[x] < w + so) { |
|
59 |
+ if (luma[x] < b) { |
|
60 |
+ alpha[x] = 255 - (luma[x] - b + so) * 255 / so; |
|
61 |
+ } else { |
|
62 |
+ alpha[x] = (luma[x] - w) * 255 / so; |
|
63 |
+ } |
|
64 |
+ } |
|
65 |
+ } |
|
66 |
+ luma += frame->linesize[0]; |
|
67 |
+ alpha += frame->linesize[3]; |
|
68 |
+ } |
|
69 |
+ |
|
70 |
+ return 0; |
|
71 |
+} |
|
72 |
+ |
|
73 |
+static int do_lumakey_slice16(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs) |
|
74 |
+{ |
|
75 |
+ LumakeyContext *s = ctx->priv; |
|
76 |
+ AVFrame *frame = arg; |
|
77 |
+ const int slice_start = (frame->height * jobnr) / nb_jobs; |
|
78 |
+ const int slice_end = (frame->height * (jobnr + 1)) / nb_jobs; |
|
79 |
+ uint16_t *alpha = (uint16_t *)(frame->data[3] + slice_start * frame->linesize[3]); |
|
80 |
+ const uint16_t *luma = (const uint16_t *)(frame->data[0] + slice_start * frame->linesize[0]); |
|
81 |
+ const int so = s->softness; |
|
82 |
+ const int w = s->white; |
|
83 |
+ const int b = s->black; |
|
84 |
+ const int m = s->max; |
|
85 |
+ int x, y; |
|
86 |
+ |
|
87 |
+ for (y = slice_start; y < slice_end; y++) { |
|
88 |
+ for (x = 0; x < frame->width; x++) { |
|
89 |
+ if (luma[x] >= b && luma[x] <= w) { |
|
90 |
+ alpha[x] = 0; |
|
91 |
+ } else if (luma[x] > b - so && luma[x] < w + so) { |
|
92 |
+ if (luma[x] < b) { |
|
93 |
+ alpha[x] = m - (luma[x] - b + so) * m / so; |
|
94 |
+ } else { |
|
95 |
+ alpha[x] = (luma[x] - w) * m / so; |
|
96 |
+ } |
|
97 |
+ } |
|
98 |
+ } |
|
99 |
+ luma += frame->linesize[0] / 2; |
|
100 |
+ alpha += frame->linesize[3] / 2; |
|
101 |
+ } |
|
102 |
+ |
|
103 |
+ return 0; |
|
104 |
+} |
|
105 |
+ |
|
106 |
+static int config_input(AVFilterLink *inlink) |
|
107 |
+{ |
|
108 |
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(inlink->format); |
|
109 |
+ AVFilterContext *ctx = inlink->dst; |
|
110 |
+ LumakeyContext *s = ctx->priv; |
|
111 |
+ int depth; |
|
112 |
+ |
|
113 |
+ depth = desc->comp[0].depth; |
|
114 |
+ if (depth == 8) { |
|
115 |
+ s->white = av_clip_uint8(s->threshold + s->tolerance); |
|
116 |
+ s->black = av_clip_uint8(s->threshold - s->tolerance); |
|
117 |
+ s->do_lumakey_slice = do_lumakey_slice8; |
|
118 |
+ } else { |
|
119 |
+ s->max = (1 << depth) - 1; |
|
120 |
+ s->white = av_clip(s->threshold + s->tolerance, 0, s->max); |
|
121 |
+ s->black = av_clip(s->threshold - s->tolerance, 0, s->max); |
|
122 |
+ s->do_lumakey_slice = do_lumakey_slice16; |
|
123 |
+ } |
|
124 |
+ |
|
125 |
+ return 0; |
|
126 |
+} |
|
127 |
+ |
|
128 |
+static int filter_frame(AVFilterLink *link, AVFrame *frame) |
|
129 |
+{ |
|
130 |
+ AVFilterContext *ctx = link->dst; |
|
131 |
+ LumakeyContext *s = ctx->priv; |
|
132 |
+ int ret; |
|
133 |
+ |
|
134 |
+ if (ret = av_frame_make_writable(frame)) |
|
135 |
+ return ret; |
|
136 |
+ |
|
137 |
+ if (ret = ctx->internal->execute(ctx, s->do_lumakey_slice, frame, NULL, FFMIN(frame->height, ff_filter_get_nb_threads(ctx)))) |
|
138 |
+ return ret; |
|
139 |
+ |
|
140 |
+ return ff_filter_frame(ctx->outputs[0], frame); |
|
141 |
+} |
|
142 |
+ |
|
143 |
+static av_cold int query_formats(AVFilterContext *ctx) |
|
144 |
+{ |
|
145 |
+ static const enum AVPixelFormat pixel_fmts[] = { |
|
146 |
+ AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA422P, AV_PIX_FMT_YUVA420P, |
|
147 |
+ AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA420P9, |
|
148 |
+ AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA420P10, |
|
149 |
+ AV_PIX_FMT_YUVA444P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA420P16, |
|
150 |
+ AV_PIX_FMT_NONE |
|
151 |
+ }; |
|
152 |
+ AVFilterFormats *formats; |
|
153 |
+ |
|
154 |
+ formats = ff_make_format_list(pixel_fmts); |
|
155 |
+ if (!formats) |
|
156 |
+ return AVERROR(ENOMEM); |
|
157 |
+ |
|
158 |
+ return ff_set_common_formats(ctx, formats); |
|
159 |
+} |
|
160 |
+ |
|
161 |
+static const AVFilterPad lumakey_inputs[] = { |
|
162 |
+ { |
|
163 |
+ .name = "default", |
|
164 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
165 |
+ .filter_frame = filter_frame, |
|
166 |
+ .config_props = config_input, |
|
167 |
+ }, |
|
168 |
+ { NULL } |
|
169 |
+}; |
|
170 |
+ |
|
171 |
+static const AVFilterPad lumakey_outputs[] = { |
|
172 |
+ { |
|
173 |
+ .name = "default", |
|
174 |
+ .type = AVMEDIA_TYPE_VIDEO, |
|
175 |
+ }, |
|
176 |
+ { NULL } |
|
177 |
+}; |
|
178 |
+ |
|
179 |
+#define OFFSET(x) offsetof(LumakeyContext, x) |
|
180 |
+#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM |
|
181 |
+ |
|
182 |
+static const AVOption lumakey_options[] = { |
|
183 |
+ { "threshold", "set the threshold value", OFFSET(threshold), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, |
|
184 |
+ { "tolerance", "set the tolerance value", OFFSET(tolerance), AV_OPT_TYPE_INT, {.i64=1}, 0, UINT16_MAX, FLAGS }, |
|
185 |
+ { "softness", "set the softness value", OFFSET(softness), AV_OPT_TYPE_INT, {.i64=0}, 0, UINT16_MAX, FLAGS }, |
|
186 |
+ { NULL } |
|
187 |
+}; |
|
188 |
+ |
|
189 |
+AVFILTER_DEFINE_CLASS(lumakey); |
|
190 |
+ |
|
191 |
+AVFilter ff_vf_lumakey = { |
|
192 |
+ .name = "lumakey", |
|
193 |
+ .description = NULL_IF_CONFIG_SMALL("Turns a certain luma into transparency."), |
|
194 |
+ .priv_size = sizeof(LumakeyContext), |
|
195 |
+ .priv_class = &lumakey_class, |
|
196 |
+ .query_formats = query_formats, |
|
197 |
+ .inputs = lumakey_inputs, |
|
198 |
+ .outputs = lumakey_outputs, |
|
199 |
+ .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, |
|
200 |
+}; |