History

avfilter: add backgroundkey video filter

Paul B Mahol committed 4 years ago

Commit 5c2a29c1 parent 1dc8d826 files · +279 −2Browse files

Unified · Split

6 files
  1. Changelog +1 −0
  2. doc/filters.texi +19 −0
  3. libavfilter/Makefile +1 −0
  4. libavfilter/allfilters.c +1 −0
  5. libavfilter/version.h +2 −2
  6. libavfilter/vf_backgroundkey.c +255 −0
Changelog+1−0
@@ -18,6 +18,7 @@ version <next>: · 17 unmodified lines
18- Media 100i decoders18- Media 100i decoders
19- DTS to PTS reorder bsf19- DTS to PTS reorder bsf
20- ViewQuest VQC decoder20- ViewQuest VQC decoder
21- backgroundkey filter
21 22
22 23
23version 5.1:24version 5.1:
doc/filters.texi+19−0
@@ -7987,6 +7987,25 @@ The command accepts the same syntax of the corresponding option. · 7986 unmodified lines
7987If the specified expression is not valid, it is kept at its current7987If the specified expression is not valid, it is kept at its current
7988value.7988value.
7989 7989
7990@section backgroundkey
7991
7992Turns a static background into transparency.
7993
7994The filter accepts the following option:
7995
7996@table @option
7997@item threshold
7998Threshold for scene change detection.
7999@item similarity
8000Similarity percentage with the background.
8001@item blend
8002Set the blend amount for pixels that are not similar.
8003@end table
8004
8005@subsection Commands
8006
8007This filter supports the all above options as @ref{commands}.
8008
7990@section bbox8009@section bbox
7991 8010
7992Compute the bounding box for the non-black pixels in the input frame8011Compute the bounding box for the non-black pixels in the input frame
libavfilter/Makefile+1−0
@@ -191,6 +191,7 @@ OBJS-$(CONFIG_AVGBLUR_FILTER) += vf_avgblur.o · 190 unmodified lines
191OBJS-$(CONFIG_AVGBLUR_OPENCL_FILTER) += vf_avgblur_opencl.o opencl.o \191OBJS-$(CONFIG_AVGBLUR_OPENCL_FILTER) += vf_avgblur_opencl.o opencl.o \
192 opencl/avgblur.o boxblur.o192 opencl/avgblur.o boxblur.o
193OBJS-$(CONFIG_AVGBLUR_VULKAN_FILTER) += vf_avgblur_vulkan.o vulkan.o vulkan_filter.o193OBJS-$(CONFIG_AVGBLUR_VULKAN_FILTER) += vf_avgblur_vulkan.o vulkan.o vulkan_filter.o
194OBJS-$(CONFIG_BACKGROUNDKEY_FILTER) += vf_backgroundkey.o
194OBJS-$(CONFIG_BBOX_FILTER) += bbox.o vf_bbox.o195OBJS-$(CONFIG_BBOX_FILTER) += bbox.o vf_bbox.o
195OBJS-$(CONFIG_BENCH_FILTER) += f_bench.o196OBJS-$(CONFIG_BENCH_FILTER) += f_bench.o
196OBJS-$(CONFIG_BILATERAL_FILTER) += vf_bilateral.o197OBJS-$(CONFIG_BILATERAL_FILTER) += vf_bilateral.o
libavfilter/allfilters.c+1−0
@@ -177,6 +177,7 @@ extern const AVFilter ff_vf_atadenoise; · 176 unmodified lines
177extern const AVFilter ff_vf_avgblur;177extern const AVFilter ff_vf_avgblur;
178extern const AVFilter ff_vf_avgblur_opencl;178extern const AVFilter ff_vf_avgblur_opencl;
179extern const AVFilter ff_vf_avgblur_vulkan;179extern const AVFilter ff_vf_avgblur_vulkan;
180extern const AVFilter ff_vf_backgroundkey;
180extern const AVFilter ff_vf_bbox;181extern const AVFilter ff_vf_bbox;
181extern const AVFilter ff_vf_bench;182extern const AVFilter ff_vf_bench;
182extern const AVFilter ff_vf_bilateral;183extern const AVFilter ff_vf_bilateral;
libavfilter/version.h+2−2
@@ -31,8 +31,8 @@ · 30 unmodified lines
31 31
32#include "version_major.h"32#include "version_major.h"
33 33
34#define LIBAVFILTER_VERSION_MINOR 4934#define LIBAVFILTER_VERSION_MINOR 50
35#define LIBAVFILTER_VERSION_MICRO 10135#define LIBAVFILTER_VERSION_MICRO 100
36 36
37 37
38#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \38#define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
libavfilter/vf_backgroundkey.c+255−0
@@ -0,0 +1,255 @@
1/*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include "libavutil/opt.h"
20#include "libavutil/imgutils.h"
21#include "avfilter.h"
22#include "formats.h"
23#include "internal.h"
24#include "video.h"
25
26typedef struct BackgroundkeyContext {
27 const AVClass *class;
28
29 float threshold;
30 float similarity;
31 float blend;
32 int max;
33
34 int nb_threads;
35 int hsub_log2;
36 int vsub_log2;
37
38 int64_t max_sum;
39 int64_t *sums;
40
41 AVFrame *background;
42
43 int (*do_slice)(AVFilterContext *avctx, void *arg,
44 int jobnr, int nb_jobs);
45} BackgroundkeyContext;
46
47static int do_backgroundkey_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
48{
49 BackgroundkeyContext *s = avctx->priv;
50 AVFrame *frame = arg;
51 const int slice_start = (frame->height * jobnr) / nb_jobs;
52 const int slice_end = (frame->height * (jobnr + 1)) / nb_jobs;
53 const int min_diff = (255 + 255 + 255) * s->similarity;
54 const float blend = s->blend;
55 const int hsub = s->hsub_log2;
56 const int vsub = s->vsub_log2;
57 int64_t sum = 0;
58
59 for (int y = slice_start; y < slice_end; y++) {
60 const uint8_t *srcy = frame->data[0] + frame->linesize[0] * y;
61 const uint8_t *srcu = frame->data[1] + frame->linesize[1] * (y >> vsub);
62 const uint8_t *srcv = frame->data[2] + frame->linesize[2] * (y >> vsub);
63 const uint8_t *bsrcy = s->background->data[0] + s->background->linesize[0] * y;
64 const uint8_t *bsrcu = s->background->data[1] + s->background->linesize[1] * (y >> vsub);
65 const uint8_t *bsrcv = s->background->data[2] + s->background->linesize[2] * (y >> vsub);
66 uint8_t *dst = frame->data[3] + frame->linesize[3] * y;
67 for (int x = 0; x < frame->width; x++) {
68 const int xx = x >> hsub;
69 const int diff = FFABS(srcy[x] - bsrcy[x]) +
70 FFABS(srcu[xx] - bsrcu[xx]) +
71 FFABS(srcv[xx] - bsrcv[xx]);
72 int A;
73
74 sum += diff;
75 if (blend > 0.f) {
76 A = 255 - av_clipf((min_diff - diff) / blend, 0.f, 255.f);
77 } else {
78 A = (diff > min_diff) ? 255 : 0;
79 }
80
81 dst[x] = A;
82 }
83 }
84
85 s->sums[jobnr] = sum;
86
87 return 0;
88}
89
90static int do_backgroundkey16_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
91{
92 BackgroundkeyContext *s = avctx->priv;
93 AVFrame *frame = arg;
94 const int slice_start = (frame->height * jobnr) / nb_jobs;
95 const int slice_end = (frame->height * (jobnr + 1)) / nb_jobs;
96 const int hsub = s->hsub_log2;
97 const int vsub = s->vsub_log2;
98 const int max = s->max;
99 const int min_diff = s->similarity * (s->max + s->max + s->max);
100 const float blend = s->blend;
101 int64_t sum = 0;
102
103 for (int y = slice_start; y < slice_end; y++) {
104 const uint16_t *srcy = (const uint16_t *)(frame->data[0] + frame->linesize[0] * y);
105 const uint16_t *srcu = (const uint16_t *)(frame->data[1] + frame->linesize[1] * (y >> vsub));
106 const uint16_t *srcv = (const uint16_t *)(frame->data[2] + frame->linesize[2] * (y >> vsub));
107 const uint16_t *bsrcy = (const uint16_t *)(s->background->data[0] + s->background->linesize[0] * y);
108 const uint16_t *bsrcu = (const uint16_t *)(s->background->data[1] + s->background->linesize[1] * (y >> vsub));
109 const uint16_t *bsrcv = (const uint16_t *)(s->background->data[2] + s->background->linesize[2] * (y >> vsub));
110 uint16_t *dst = (uint16_t *)(frame->data[3] + frame->linesize[3] * y);
111 for (int x = 0; x < frame->width; x++) {
112 const int xx = x >> hsub;
113 const int diff = FFABS(srcy[x] - bsrcy[x] ) +
114 FFABS(srcu[xx] - bsrcu[xx]) +
115 FFABS(srcv[xx] - bsrcv[xx]);
116 int A;
117
118 sum += diff;
119 if (blend > 0.f) {
120 A = max - av_clipf((min_diff - diff) / blend, 0.f, max);
121 } else {
122 A = (diff > min_diff) ? max : 0;
123 }
124
125 dst[x] = A;
126 }
127 }
128
129 s->sums[jobnr] = sum;
130
131 return 0;
132}
133
134static int filter_frame(AVFilterLink *link, AVFrame *frame)
135{
136 AVFilterContext *avctx = link->dst;
137 BackgroundkeyContext *s = avctx->priv;
138 int64_t sum = 0;
139 int ret = 0;
140
141 if (!s->background) {
142 s->background = ff_get_video_buffer(link, frame->width, frame->height);
143 if (!s->background) {
144 ret = AVERROR(ENOMEM);
145 goto fail;
146 }
147 ret = av_frame_copy(s->background, frame);
148 if (ret < 0)
149 goto fail;
150 }
151
152 if (ret = ff_filter_execute(avctx, s->do_slice, frame, NULL,
153 FFMIN(frame->height, s->nb_threads)))
154 goto fail;
155
156 for (int n = 0; n < s->nb_threads; n++)
157 sum += s->sums[n];
158 if (s->max_sum * s->threshold < sum) {
159 ret = av_frame_copy(s->background, frame);
160 if (ret < 0)
161 goto fail;
162 }
163
164 return ff_filter_frame(avctx->outputs[0], frame);
165fail:
166 av_frame_free(&frame);
167 return ret;
168}
169
170static av_cold int config_output(AVFilterLink *outlink)
171{
172 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(outlink->format);
173 AVFilterContext *avctx = outlink->src;
174 AVFilterLink *inlink = avctx->inputs[0];
175 BackgroundkeyContext *s = avctx->priv;
176 int depth;
177
178 s->nb_threads = ff_filter_get_nb_threads(avctx);
179 depth = desc->comp[0].depth;
180 s->do_slice = depth <= 8 ? do_backgroundkey_slice : do_backgroundkey16_slice;
181 s->max = (1 << depth) - 1;
182 s->hsub_log2 = desc->log2_chroma_w;
183 s->vsub_log2 = desc->log2_chroma_h;
184 s->max_sum = (int64_t)(inlink->w) * inlink->h * s->max;
185 s->max_sum += 2LL * (inlink->w >> s->hsub_log2) * (inlink->h >> s->vsub_log2) * s->max;
186
187 s->sums = av_calloc(s->nb_threads, sizeof(*s->sums));
188 if (!s->sums)
189 return AVERROR(ENOMEM);
190
191 return 0;
192}
193
194static av_cold void uninit(AVFilterContext *ctx)
195{
196 BackgroundkeyContext *s = ctx->priv;
197
198 av_frame_free(&s->background);
199 av_freep(&s->sums);
200}
201
202static const AVFilterPad backgroundkey_inputs[] = {
203 {
204 .name = "default",
205 .type = AVMEDIA_TYPE_VIDEO,
206 .flags = AVFILTERPAD_FLAG_NEEDS_WRITABLE,
207 .filter_frame = filter_frame,
208 },
209};
210
211static const AVFilterPad backgroundkey_outputs[] = {
212 {
213 .name = "default",
214 .type = AVMEDIA_TYPE_VIDEO,
215 .config_props = config_output,
216 },
217};
218
219#define OFFSET(x) offsetof(BackgroundkeyContext, x)
220#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_RUNTIME_PARAM
221
222static const AVOption backgroundkey_options[] = {
223 { "threshold", "set the scene change threshold", OFFSET(threshold), AV_OPT_TYPE_FLOAT, { .dbl = 0.08}, 0.0, 1.0, FLAGS },
224 { "similarity", "set the similarity", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.1 }, 0.0, 1.0, FLAGS },
225 { "blend", "set the blend value", OFFSET(blend), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, 0.0, 1.0, FLAGS },
226 { NULL }
227};
228
229static const enum AVPixelFormat backgroundkey_fmts[] = {
230 AV_PIX_FMT_YUVA420P,
231 AV_PIX_FMT_YUVA422P,
232 AV_PIX_FMT_YUVA444P,
233 AV_PIX_FMT_YUVA420P9, AV_PIX_FMT_YUVA422P9, AV_PIX_FMT_YUVA444P9,
234 AV_PIX_FMT_YUVA420P10, AV_PIX_FMT_YUVA422P10, AV_PIX_FMT_YUVA444P10,
235 AV_PIX_FMT_YUVA422P12, AV_PIX_FMT_YUVA444P12,
236 AV_PIX_FMT_YUVA420P16, AV_PIX_FMT_YUVA422P16, AV_PIX_FMT_YUVA444P16,
237 AV_PIX_FMT_GBRAP,
238 AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GBRAP16,
239 AV_PIX_FMT_NONE
240};
241
242AVFILTER_DEFINE_CLASS(backgroundkey);
243
244const AVFilter ff_vf_backgroundkey = {
245 .name = "backgroundkey",
246 .description = NULL_IF_CONFIG_SMALL("Turns a static background into transparency."),
247 .priv_size = sizeof(BackgroundkeyContext),
248 .priv_class = &backgroundkey_class,
249 .uninit = uninit,
250 FILTER_INPUTS(backgroundkey_inputs),
251 FILTER_OUTPUTS(backgroundkey_outputs),
252 FILTER_PIXFMTS_ARRAY(backgroundkey_fmts),
253 .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
254 .process_command = ff_filter_process_command,
255};

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t