ffmpeg绿幕抠图原理解析

本文介绍了ffmpeg中的chromakey和colorkey滤镜在绿幕抠像中的应用。通过分析源码,可以看到这两个滤镜分别针对YUV和RGB颜色空间,计算像素点与指定色键的相似度,将相似度超过阈值的像素点标记为透明,实现背景替换效果。
摘要由CSDN通过智能技术生成

绿幕抠像技术早已不是什么高大上的新兴技术,但是应用场景还是非常多的,今天我们基于ffmpeg滤镜一起探讨一下绿幕抠图原理。

RGB颜色模型

在这里插入图片描述

如图用三维空间坐标表示rgb颜色,以rgb24为例rgb各自取值范围为0~255,argb、rgba等带透明度的格式也是在三维rgb基础上加上一个透明度维度,不详细讨论。坐标轴上每一个点代表一个颜色,两点之间的距离可以认为是两个颜色之间的相识度,为了方便表示经常以颜色间距离和灰度线的百分比来表示。

YUV颜色空间

yuv颜色空间是u分量和v分量,可以想象到是一个简单的二维空间,空间内部两点间距离就是两个颜色间的相似程度,一般以相识距离和中轴线的百分比来表示相似度。

ffmpeg源码

  1. ffmpeg滤镜chromakey针对yuv处理,将对应像素点标记透明。
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;
}

通过源码可以看到ffmpeg的处理是遍历像素点计算和给定色键的相识度并将范围内的像素点做透明标记。

2.ffmpeg滤镜colorkey针对rgb处理,将目标像素点设置为透明。

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;
}

通过源码可以看到和chromakey类似也是遍历像素点,逐一计算像素点和色键的相似度,将范围内的像素标记为透明。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值