Skip to content

Commit 5ce0ffa

Browse files
committed
avfilter/vf_colorkey: Add colorkey video filter
1 parent 14c4b25 commit 5ce0ffa

File tree

6 files changed

+307
-0
lines changed

6 files changed

+307
-0
lines changed

Changelog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ version <next>:
2121
- Automatically rotate videos based on metadata in ffmpeg
2222
- improved Quickdraw compatibility
2323
- VP9 high bit-depth and extended colorspaces decoding support
24+
- colorkey video filter
2425

2526

2627
version 2.6:

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ Filters:
352352
avf_showcqt.c Muhammad Faiz
353353
vf_blend.c Paul B Mahol
354354
vf_colorbalance.c Paul B Mahol
355+
vf_colorkey.c Timo Rothenpieler
355356
vf_dejudder.c Nicholas Robbins
356357
vf_delogo.c Jean Delvare (CC <khali@linux-fr.org>)
357358
vf_drawbox.c/drawgrid Andrey Utkin

doc/filters.texi

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3026,6 +3026,45 @@ colorbalance=rs=.3
30263026
@end example
30273027
@end itemize
30283028

3029+
@section colorkey
3030+
RGB colorspace color keying.
3031+
3032+
The filter accepts the following options:
3033+
3034+
@table @option
3035+
@item color
3036+
The color which will be replaced with transparency.
3037+
3038+
@item similarity
3039+
Similarity percentage with the key color.
3040+
3041+
0.01 matches only the exact key color, while 1.0 matches everything.
3042+
3043+
@item blend
3044+
Blend percentage.
3045+
3046+
0.0 makes pixels either fully transparent, or not transparent at all.
3047+
3048+
Higher values result in semi-transparent pixels, with a higher transparency
3049+
the more similar the pixels color is to the key color.
3050+
@end table
3051+
3052+
@subsection Examples
3053+
3054+
@itemize
3055+
@item
3056+
Make every green pixel in the input image transparent:
3057+
@example
3058+
ffmpeg -i input.png -vf colorkey=green out.png
3059+
@end example
3060+
3061+
@item
3062+
Overlay a greenscreen-video on top of a static background image.
3063+
@example
3064+
ffmpeg -i background.png -i video.mp4 -filter_complex "[1:v]colorkey=0x3BBD1E:0.3:0.2[ckout];[0:v][ckout]overlay[out]" -map "[out]" output.flv
3065+
@end example
3066+
@end itemize
3067+
30293068
@section colorlevels
30303069

30313070
Adjust video input frames using levels.

libavfilter/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ OBJS-$(CONFIG_BOXBLUR_FILTER) += vf_boxblur.o
102102
OBJS-$(CONFIG_CODECVIEW_FILTER) += vf_codecview.o
103103
OBJS-$(CONFIG_COLORBALANCE_FILTER) += vf_colorbalance.o
104104
OBJS-$(CONFIG_COLORCHANNELMIXER_FILTER) += vf_colorchannelmixer.o
105+
OBJS-$(CONFIG_COLORKEY_FILTER) += vf_colorkey.o
105106
OBJS-$(CONFIG_COLORLEVELS_FILTER) += vf_colorlevels.o
106107
OBJS-$(CONFIG_COLORMATRIX_FILTER) += vf_colormatrix.o
107108
OBJS-$(CONFIG_COPY_FILTER) += vf_copy.o

libavfilter/allfilters.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ void avfilter_register_all(void)
118118
REGISTER_FILTER(CODECVIEW, codecview, vf);
119119
REGISTER_FILTER(COLORBALANCE, colorbalance, vf);
120120
REGISTER_FILTER(COLORCHANNELMIXER, colorchannelmixer, vf);
121+
REGISTER_FILTER(COLORKEY, colorkey, vf);
121122
REGISTER_FILTER(COLORLEVELS, colorlevels, vf);
122123
REGISTER_FILTER(COLORMATRIX, colormatrix, vf);
123124
REGISTER_FILTER(COPY, copy, vf);

libavfilter/vf_colorkey.c

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
/*
2+
* Copyright (c) 2015 Timo Rothenpieler <timo@rothenpieler.org>
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+
#include "libavutil/opt.h"
22+
#include "avfilter.h"
23+
#include "formats.h"
24+
#include "internal.h"
25+
#include "video.h"
26+
27+
typedef struct ColorkeyContext {
28+
const AVClass *class;
29+
30+
/* color offsets rgba */
31+
int co[4];
32+
33+
uint8_t colorkey_rgba[4];
34+
float similarity;
35+
float blend;
36+
} ColorkeyContext;
37+
38+
static int convert_format(int fmt)
39+
{
40+
switch(fmt) {
41+
case AV_PIX_FMT_0RGB:
42+
return AV_PIX_FMT_ARGB;
43+
case AV_PIX_FMT_0BGR:
44+
return AV_PIX_FMT_ABGR;
45+
case AV_PIX_FMT_BGR0:
46+
return AV_PIX_FMT_BGRA;
47+
case AV_PIX_FMT_RGB0:
48+
return AV_PIX_FMT_RGBA;
49+
default:
50+
return fmt;
51+
}
52+
}
53+
54+
static int offset_r(int fmt)
55+
{
56+
switch(fmt) {
57+
case AV_PIX_FMT_ARGB:
58+
return 1;
59+
case AV_PIX_FMT_ABGR:
60+
return 3;
61+
case AV_PIX_FMT_BGRA:
62+
return 2;
63+
case AV_PIX_FMT_RGBA:
64+
default:
65+
return 0;
66+
}
67+
}
68+
69+
static int offset_g(int fmt)
70+
{
71+
switch(fmt) {
72+
case AV_PIX_FMT_ARGB:
73+
case AV_PIX_FMT_ABGR:
74+
return 2;
75+
case AV_PIX_FMT_BGRA:
76+
case AV_PIX_FMT_RGBA:
77+
default:
78+
return 1;
79+
}
80+
}
81+
82+
static int offset_b(int fmt)
83+
{
84+
switch(fmt) {
85+
case AV_PIX_FMT_ARGB:
86+
return 3;
87+
case AV_PIX_FMT_ABGR:
88+
return 1;
89+
case AV_PIX_FMT_BGRA:
90+
return 0;
91+
case AV_PIX_FMT_RGBA:
92+
default:
93+
return 2;
94+
}
95+
}
96+
97+
static int offset_a(int fmt)
98+
{
99+
switch(fmt) {
100+
case AV_PIX_FMT_ARGB:
101+
case AV_PIX_FMT_ABGR:
102+
return 0;
103+
case AV_PIX_FMT_BGRA:
104+
case AV_PIX_FMT_RGBA:
105+
default:
106+
return 3;
107+
}
108+
}
109+
110+
static uint8_t do_colorkey_pixel(ColorkeyContext *ctx, uint8_t r, uint8_t g, uint8_t b)
111+
{
112+
int dr = (int)r - ctx->colorkey_rgba[0];
113+
int dg = (int)g - ctx->colorkey_rgba[1];
114+
int db = (int)b - ctx->colorkey_rgba[2];
115+
116+
double diff = sqrt((dr * dr + dg * dg + db * db) / (255.0 * 255.0));
117+
118+
if (ctx->blend > 0.0001) {
119+
return av_clipd(FFMAX(diff - ctx->similarity, 0.0) / ctx->blend, 0.0, 1.0) * 255.0;
120+
} else {
121+
return ((diff - ctx->similarity) > 0.0) ? 255 : 0;
122+
}
123+
}
124+
125+
static int do_colorkey_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs)
126+
{
127+
AVFrame *frame = arg;
128+
129+
const int slice_start = (frame->height * jobnr) / nb_jobs;
130+
const int slice_end = (frame->height * (jobnr+1)) / nb_jobs;
131+
132+
ColorkeyContext *ctx = avctx->priv;
133+
134+
int o, x, y;
135+
136+
for (y = slice_start; y < slice_end; ++y) {
137+
for (x = 0; x < frame->width; ++x) {
138+
o = frame->linesize[0] * y + x * 4;
139+
140+
frame->data[0][o + ctx->co[3]] =
141+
do_colorkey_pixel(ctx,
142+
frame->data[0][o + ctx->co[0]],
143+
frame->data[0][o + ctx->co[1]],
144+
frame->data[0][o + ctx->co[2]]);
145+
}
146+
}
147+
148+
return 0;
149+
}
150+
151+
static int filter_frame(AVFilterLink *link, AVFrame *frame)
152+
{
153+
AVFilterContext *avctx = link->dst;
154+
int res;
155+
156+
if (res = av_frame_make_writable(frame))
157+
return res;
158+
159+
frame->format = convert_format(frame->format);
160+
161+
if (res = avctx->internal->execute(avctx, do_colorkey_slice, frame, NULL, FFMIN(frame->height, avctx->graph->nb_threads)))
162+
return res;
163+
164+
return ff_filter_frame(avctx->outputs[0], frame);
165+
}
166+
167+
static int config_output(AVFilterLink *outlink)
168+
{
169+
AVFilterContext *avctx = outlink->src;
170+
ColorkeyContext *ctx = avctx->priv;
171+
172+
outlink->w = avctx->inputs[0]->w;
173+
outlink->h = avctx->inputs[0]->h;
174+
outlink->time_base = avctx->inputs[0]->time_base;
175+
176+
outlink->format = convert_format(avctx->inputs[0]->format);
177+
178+
ctx->co[0] = offset_r(outlink->format);
179+
ctx->co[1] = offset_g(outlink->format);
180+
ctx->co[2] = offset_b(outlink->format);
181+
ctx->co[3] = offset_a(outlink->format);
182+
183+
return 0;
184+
}
185+
186+
static int query_formats(AVFilterContext *avctx)
187+
{
188+
static const enum AVPixelFormat pixel_fmts_in[] = {
189+
AV_PIX_FMT_0RGB,
190+
AV_PIX_FMT_ARGB,
191+
AV_PIX_FMT_RGB0,
192+
AV_PIX_FMT_RGBA,
193+
AV_PIX_FMT_0BGR,
194+
AV_PIX_FMT_ABGR,
195+
AV_PIX_FMT_BGR0,
196+
AV_PIX_FMT_BGRA,
197+
AV_PIX_FMT_NONE
198+
};
199+
200+
static const enum AVPixelFormat pixel_fmts_out[] = {
201+
AV_PIX_FMT_ARGB,
202+
AV_PIX_FMT_RGBA,
203+
AV_PIX_FMT_ABGR,
204+
AV_PIX_FMT_BGRA,
205+
AV_PIX_FMT_NONE
206+
};
207+
208+
AVFilterFormats *input_formats;
209+
AVFilterFormats *output_formats;
210+
211+
input_formats = ff_make_format_list(pixel_fmts_in);
212+
if (!input_formats)
213+
return AVERROR(ENOMEM);
214+
215+
output_formats = ff_make_format_list(pixel_fmts_out);
216+
if (!output_formats)
217+
return AVERROR(ENOMEM);
218+
219+
ff_formats_ref(input_formats, &avctx->inputs[0]->out_formats);
220+
ff_formats_ref(output_formats, &avctx->outputs[0]->in_formats);
221+
222+
return 0;
223+
}
224+
225+
static const AVFilterPad colorkey_inputs[] = {
226+
{
227+
.name = "default",
228+
.type = AVMEDIA_TYPE_VIDEO,
229+
.filter_frame = filter_frame,
230+
},
231+
{ NULL }
232+
};
233+
234+
static const AVFilterPad colorkey_outputs[] = {
235+
{
236+
.name = "default",
237+
.type = AVMEDIA_TYPE_VIDEO,
238+
.config_props = config_output,
239+
},
240+
{ NULL }
241+
};
242+
243+
#define OFFSET(x) offsetof(ColorkeyContext, x)
244+
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
245+
246+
static const AVOption colorkey_options[] = {
247+
{ "color", "set the colorkey key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS },
248+
{ "similarity", "set the colorkey similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.01, 1.0, FLAGS },
249+
{ "blend", "set the colorkey key blend value", OFFSET(blend), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, 0.0, 1.0, FLAGS },
250+
{ NULL }
251+
};
252+
253+
AVFILTER_DEFINE_CLASS(colorkey);
254+
255+
AVFilter ff_vf_colorkey = {
256+
.name = "colorkey",
257+
.description = NULL_IF_CONFIG_SMALL("colorkey filter"),
258+
.priv_size = sizeof(ColorkeyContext),
259+
.priv_class = &colorkey_class,
260+
.query_formats = query_formats,
261+
.inputs = colorkey_inputs,
262+
.outputs = colorkey_outputs,
263+
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS,
264+
};

0 commit comments

Comments
 (0)