1.不用png,用AVFrame
2.合流
3.图片抠图透明 (1.)mp4扣yuv图,(2)用1.把一张yuv标记为透明然后av_hwframe_transfer_data到GPU (3)用抠图算法函数对yuv进行处理 (4)
qsv的h264_qsv只支持nv12和qsv,但qsv本身并不限制像素格式,比如在qsv里可以用vpp_qsv=format输出各种像素格式。因此以上第二种方法可能可行。
av_hwframe_transfer_data转换不了可能是硬件帧函数配置的像素格式出的问题。
cpp
ffmpeg -y -i TJTV.png -vf colorkey=0x0000FF:0.5:0.0 -c:v png t.png
0x0000FF:六位每两位分别表示红绿蓝,因此它是对rgb像素进行透明标记。
0.5表示蓝色的范围容差值,范围为0.01-1,数值越大,容错越大。
0.0是alpha通道的阀值,为0表示完全透明,实测这个值作用不明显。
对于TJTV.png,可以看到它的编码和封装方式都是png,它的像素格式为rgba。
Stream #0:0: Video: png, rgba(pc), 280x279 [SAR 2835:2835 DAR
280:279], 25 fps, 25 tbr, 25 tbn
这个命令只能对rgb的像素格式进行透明标记,但是对yuv得不能,会导致花屏。只有argb、rgba等带透明度格式的像素格式才能进行抠图透明标记。
透明的原理是对rgb数据逐个像素处理,修改rgb值,达到对特定的像素进行透明标记。
透明的原理是对rgb或yuv增加第四个Alpha通道,这个通道的值由一个byte表示,不同的值表示透明程度不同,Alpha的值与其他yuv、rgb值作用来改变灰阶度来达到透明效果。
chromakey与colorkey的区别,官网给了这四个例子
cpp
ffmpeg -i input.png -vf colorkey=green out.png
cpp
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
cpp
ffmpeg -i input.png -vf chromakey=green out.png
cpp
ffmpeg -f lavfi -i color=c=black:s=1280x720 -i video.mp4 -shortest -filter_complex "[1:v]chromakey=0x70de77:0.1:0.2[ckout];[0:v][ckout]overlay[out]" -map "[out]" output.mkv
clolorkey是对yuv数据进行处理,但它的三个参数值依然和colorkey同样的用法。对yuv进行透明标记后,如果再次编码,那么标记就会失效。同时如果对rtb图像进行标记后,如果转为yuv,则标记同样失效并且花屏。
以下是去除绿色背景。
cpp
ffmpeg -h filter=colorkey
Filter colorkey
Turns a certain color into transparency. Operates on RGB colors.
slice threading supported
Inputs:
#0: default (video)
Outputs:
#0: default (video)
colorkey AVOptions:
color <color> ..FV.....T. set the colorkey key color (default "black")
similarity <float> ..FV.....T. set the colorkey similarity value (from 1e-05 to 1) (default 0.01)
blend <float> ..FV.....T. set the colorkey key blend value (from 0 to 1) (default 0)
cpp
ffmpeg -h filter=chromakey
Filter chromakey
Turns a certain color into transparency. Operates on YUV colors.
slice threading supported
Inputs:
#0: default (video)
Outputs:
#0: default (video)
chromakey AVOptions:
color <color> ..FV.....T. set the chromakey key color (default "black")
similarity <float> ..FV.....T. set the chromakey similarity value (from 1e-05 to 1) (default 0.01)
blend <float> ..FV.....T. set the chromakey key blend value (from 0 to 1) (default 0)
yuv <boolean> ..FV.....T. color parameter is in yuv instead of rgb (default false)
第四个参数默认为false,也就是前面传递的是grb信号值,如果为true的话,颜色传递的信号已经是YUV而不是RGB。像"green"或"red"这样的字面颜色不再有意义了。 这可以用于将精确的YUV值作为十六进制数传递。
附录:
1.ffmpeg滤镜chromakey针对yuv处理,将对应像素点标记透明。
cpp
typedef struct ColorkeyContext {
const AVClass *class;
/* color offsets rgba */
int co[4];
uint8_t colorkey_rgba[4];
float similarity;
float blend;
} ColorkeyContext;
static uint8_t do_chromakey_pixel(ChromakeyContext *ctx, uint8_t u[9], uint8_t v[9])
{
double diff = 0.0;
int du, dv, i;
for (i = 0; i < 9; ++i) {
du = (int)u[i] - ctx->chromakey_uv[0];
dv = (int)v[i] - ctx->chromakey_uv[1];
diff += sqrt((du * du + dv * dv) / (255.0 * 255.0 * 2));
}
diff /= 9.0;
if (ctx->blend > 0.0001) {
return av_clipd((diff - ctx->similarity) / ctx->blend, 0.0, 1.0) * 255.0;
} else {
return (diff > ctx->similarity) ? 255 : 0;
}
}
static int do_chromakey_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;
ChromakeyContext *ctx = avctx->priv;
int x, y, xo, yo;
uint8_t u[9], v[9];
memset(u, ctx->chromakey_uv[0], sizeof(u));
memset(v, ctx->chromakey_uv[1], sizeof(v));
for (y = slice_start; y < slice_end; ++y)
{
for (x = 0; x < frame->width; ++x)
{
for (yo = 0; yo < 3; ++yo)
{
for (xo = 0; xo < 3; ++xo)
{
get_pixel_uv(frame, ctx->hsub_log2, ctx->vsub_log2, x + xo - 1, y + yo - 1, &u[yo * 3 + xo], &v[yo * 3 + xo]);
}
}
frame->data[3][frame->linesize[3] * y + x] = do_chromakey_pixel(ctx, u, v);
}
}
return 0;
}
2.ffmpeg滤镜colorkey针对rgb处理,将目标像素点设置为透明。
cpp
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 * 3.0));
if (ctx->blend > 0.0001) {
return av_clipd((diff - ctx->similarity) / ctx->blend, 0.0, 1.0) * 255.0;
} else {
return (diff > ctx->similarity) ? 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;
// 修改rgba a数据
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;
}
参考:
http://ffmpeg.org/ffmpeg-filters.html#colorkey
ffmpeg绿幕抠图原理解析
ffmpeg -i 20190323162628578.png -i juren-30s.mp4 -filter_complex "[0:v]colorkey=0x00FF00:0.1:0[out];[1:v][out]overlay[re]" -map [re] -c:v h264_qsv re.mp4
cpp
转成yuv就会花屏
ffmpeg -i 20190323162628578.png -vf "colorkey=0x00FF00:0.5:0" -pix_fmt yuv420p t.yuv
ffplay -video_size 146x60 -pixel_format yuv420p -i t.yuv
cpp
这样OK
ffmpeg -i 20190323162628578.png -vf "colorkey=0x00FF00:0.5:0" t.png
ffplay t.png
cpp
这样OK
ffmpeg -i 20190323162628578.png -vf "colorkey=0x00FF00:0.5:0" -pix_fmt yuva420p t.yuv
ffplay -video_size 146x60 -pixel_format yuva420p -i t.yuv
cpp
palettegen滤镜也可以设置透明度
ffmpeg -i "./了不起\3D%02d.png" -filter_complex "[0:v]scale=700:700:force_original_aspect_ratio=decrease,format=rgba,fps=25,split[v0][v1];[v0]palettegen=reserve_transparent=on:transparency_color=00000000[v];[v1][v]paletteuse" -y 了不起.gif