forked from FFmpeg/FFmpeg
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
avfilter/vf_colorkey: Add colorkey video filter
- Loading branch information
Showing
6 changed files
with
307 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
/* | ||
* Copyright (c) 2015 Timo Rothenpieler <timo@rothenpieler.org> | ||
* | ||
* This file is part of FFmpeg. | ||
* | ||
* FFmpeg is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 2.1 of the License, or (at your option) any later version. | ||
* | ||
* FFmpeg is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with FFmpeg; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
*/ | ||
|
||
#include "libavutil/opt.h" | ||
#include "avfilter.h" | ||
#include "formats.h" | ||
#include "internal.h" | ||
#include "video.h" | ||
|
||
typedef struct ColorkeyContext { | ||
const AVClass *class; | ||
|
||
/* color offsets rgba */ | ||
int co[4]; | ||
|
||
uint8_t colorkey_rgba[4]; | ||
float similarity; | ||
float blend; | ||
} ColorkeyContext; | ||
|
||
static int convert_format(int fmt) | ||
{ | ||
switch(fmt) { | ||
case AV_PIX_FMT_0RGB: | ||
return AV_PIX_FMT_ARGB; | ||
case AV_PIX_FMT_0BGR: | ||
return AV_PIX_FMT_ABGR; | ||
case AV_PIX_FMT_BGR0: | ||
return AV_PIX_FMT_BGRA; | ||
case AV_PIX_FMT_RGB0: | ||
return AV_PIX_FMT_RGBA; | ||
default: | ||
return fmt; | ||
} | ||
} | ||
|
||
static int offset_r(int fmt) | ||
{ | ||
switch(fmt) { | ||
case AV_PIX_FMT_ARGB: | ||
return 1; | ||
case AV_PIX_FMT_ABGR: | ||
return 3; | ||
case AV_PIX_FMT_BGRA: | ||
return 2; | ||
case AV_PIX_FMT_RGBA: | ||
default: | ||
return 0; | ||
} | ||
} | ||
|
||
static int offset_g(int fmt) | ||
{ | ||
switch(fmt) { | ||
case AV_PIX_FMT_ARGB: | ||
case AV_PIX_FMT_ABGR: | ||
return 2; | ||
case AV_PIX_FMT_BGRA: | ||
case AV_PIX_FMT_RGBA: | ||
default: | ||
return 1; | ||
} | ||
} | ||
|
||
static int offset_b(int fmt) | ||
{ | ||
switch(fmt) { | ||
case AV_PIX_FMT_ARGB: | ||
return 3; | ||
case AV_PIX_FMT_ABGR: | ||
return 1; | ||
case AV_PIX_FMT_BGRA: | ||
return 0; | ||
case AV_PIX_FMT_RGBA: | ||
default: | ||
return 2; | ||
} | ||
} | ||
|
||
static int offset_a(int fmt) | ||
{ | ||
switch(fmt) { | ||
case AV_PIX_FMT_ARGB: | ||
case AV_PIX_FMT_ABGR: | ||
return 0; | ||
case AV_PIX_FMT_BGRA: | ||
case AV_PIX_FMT_RGBA: | ||
default: | ||
return 3; | ||
} | ||
} | ||
|
||
static uint8_t do_colorkey_pixel(ColorkeyContext *ctx, uint8_t r, uint8_t g, uint8_t b) | ||
{ | ||
int dr = (int)r - ctx->colorkey_rgba[0]; | ||
int dg = (int)g - ctx->colorkey_rgba[1]; | ||
int db = (int)b - ctx->colorkey_rgba[2]; | ||
|
||
double diff = sqrt((dr * dr + dg * dg + db * db) / (255.0 * 255.0)); | ||
|
||
if (ctx->blend > 0.0001) { | ||
return av_clipd(FFMAX(diff - ctx->similarity, 0.0) / ctx->blend, 0.0, 1.0) * 255.0; | ||
} else { | ||
return ((diff - ctx->similarity) > 0.0) ? 255 : 0; | ||
} | ||
} | ||
|
||
static int do_colorkey_slice(AVFilterContext *avctx, void *arg, int jobnr, int nb_jobs) | ||
{ | ||
AVFrame *frame = arg; | ||
|
||
const int slice_start = (frame->height * jobnr) / nb_jobs; | ||
const int slice_end = (frame->height * (jobnr+1)) / nb_jobs; | ||
|
||
ColorkeyContext *ctx = avctx->priv; | ||
|
||
int o, x, y; | ||
|
||
for (y = slice_start; y < slice_end; ++y) { | ||
for (x = 0; x < frame->width; ++x) { | ||
o = frame->linesize[0] * y + x * 4; | ||
|
||
frame->data[0][o + ctx->co[3]] = | ||
do_colorkey_pixel(ctx, | ||
frame->data[0][o + ctx->co[0]], | ||
frame->data[0][o + ctx->co[1]], | ||
frame->data[0][o + ctx->co[2]]); | ||
} | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static int filter_frame(AVFilterLink *link, AVFrame *frame) | ||
{ | ||
AVFilterContext *avctx = link->dst; | ||
int res; | ||
|
||
if (res = av_frame_make_writable(frame)) | ||
return res; | ||
|
||
frame->format = convert_format(frame->format); | ||
|
||
if (res = avctx->internal->execute(avctx, do_colorkey_slice, frame, NULL, FFMIN(frame->height, avctx->graph->nb_threads))) | ||
return res; | ||
|
||
return ff_filter_frame(avctx->outputs[0], frame); | ||
} | ||
|
||
static int config_output(AVFilterLink *outlink) | ||
{ | ||
AVFilterContext *avctx = outlink->src; | ||
ColorkeyContext *ctx = avctx->priv; | ||
|
||
outlink->w = avctx->inputs[0]->w; | ||
outlink->h = avctx->inputs[0]->h; | ||
outlink->time_base = avctx->inputs[0]->time_base; | ||
|
||
outlink->format = convert_format(avctx->inputs[0]->format); | ||
|
||
ctx->co[0] = offset_r(outlink->format); | ||
ctx->co[1] = offset_g(outlink->format); | ||
ctx->co[2] = offset_b(outlink->format); | ||
ctx->co[3] = offset_a(outlink->format); | ||
|
||
return 0; | ||
} | ||
|
||
static int query_formats(AVFilterContext *avctx) | ||
{ | ||
static const enum AVPixelFormat pixel_fmts_in[] = { | ||
AV_PIX_FMT_0RGB, | ||
AV_PIX_FMT_ARGB, | ||
AV_PIX_FMT_RGB0, | ||
AV_PIX_FMT_RGBA, | ||
AV_PIX_FMT_0BGR, | ||
AV_PIX_FMT_ABGR, | ||
AV_PIX_FMT_BGR0, | ||
AV_PIX_FMT_BGRA, | ||
AV_PIX_FMT_NONE | ||
}; | ||
|
||
static const enum AVPixelFormat pixel_fmts_out[] = { | ||
AV_PIX_FMT_ARGB, | ||
AV_PIX_FMT_RGBA, | ||
AV_PIX_FMT_ABGR, | ||
AV_PIX_FMT_BGRA, | ||
AV_PIX_FMT_NONE | ||
}; | ||
|
||
AVFilterFormats *input_formats; | ||
AVFilterFormats *output_formats; | ||
|
||
input_formats = ff_make_format_list(pixel_fmts_in); | ||
if (!input_formats) | ||
return AVERROR(ENOMEM); | ||
|
||
output_formats = ff_make_format_list(pixel_fmts_out); | ||
if (!output_formats) | ||
return AVERROR(ENOMEM); | ||
|
||
ff_formats_ref(input_formats, &avctx->inputs[0]->out_formats); | ||
ff_formats_ref(output_formats, &avctx->outputs[0]->in_formats); | ||
|
||
return 0; | ||
} | ||
|
||
static const AVFilterPad colorkey_inputs[] = { | ||
{ | ||
.name = "default", | ||
.type = AVMEDIA_TYPE_VIDEO, | ||
.filter_frame = filter_frame, | ||
}, | ||
{ NULL } | ||
}; | ||
|
||
static const AVFilterPad colorkey_outputs[] = { | ||
{ | ||
.name = "default", | ||
.type = AVMEDIA_TYPE_VIDEO, | ||
.config_props = config_output, | ||
}, | ||
{ NULL } | ||
}; | ||
|
||
#define OFFSET(x) offsetof(ColorkeyContext, x) | ||
#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM | ||
|
||
static const AVOption colorkey_options[] = { | ||
{ "color", "set the colorkey key color", OFFSET(colorkey_rgba), AV_OPT_TYPE_COLOR, { .str = "black" }, CHAR_MIN, CHAR_MAX, FLAGS }, | ||
{ "similarity", "set the colorkey similarity value", OFFSET(similarity), AV_OPT_TYPE_FLOAT, { .dbl = 0.01 }, 0.01, 1.0, FLAGS }, | ||
{ "blend", "set the colorkey key blend value", OFFSET(blend), AV_OPT_TYPE_FLOAT, { .dbl = 0.0 }, 0.0, 1.0, FLAGS }, | ||
{ NULL } | ||
}; | ||
|
||
AVFILTER_DEFINE_CLASS(colorkey); | ||
|
||
AVFilter ff_vf_colorkey = { | ||
.name = "colorkey", | ||
.description = NULL_IF_CONFIG_SMALL("colorkey filter"), | ||
.priv_size = sizeof(ColorkeyContext), | ||
.priv_class = &colorkey_class, | ||
.query_formats = query_formats, | ||
.inputs = colorkey_inputs, | ||
.outputs = colorkey_outputs, | ||
.flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, | ||
}; |