20行核心(shader)代码实现实时绿幕抠图
背景
因为视频相关标准及浏览器的实现问题,很难在主流浏览器中顺利播放背景透明的视频。
有两种方法可以为最通用的视频格式(MP4,H264)移除背景,实现透明效果:
-
原视频配上绿幕,使用本文介绍方法移除背景绿幕
- 优点:制作使用简单
- 缺点:抠图可能不完美,导致偏色
-
将视频中的alpha通道与画面并排放置,在客户端混合
- 优点:精确还原
- 缺点:分辨率增加,视频变大;可能适应场景小,原视频制作需要精确的alpha通道
效果演示
静态图片抠图 DEMO
配合 @webav/av-cliper 进行视频抠图 DEMO
方法二参考:WebGL Chromakey 实时绿幕抠图
绿幕抠图原理
- 定义一个范围(range),和一个目标颜色值
- 使用 WebGL(片元着色器 ) 逐个比对原像素与目标颜色的距离
- 根据颜色距离判断
- 超过range上限(颜色差距很大)则保留原像素
- 低于range下限(颜色很相似)则移除像素
- 处于range之中,原像素 – 目标像素 * 相似度系数
实现
需要先了解一下 YUV 颜色编码
Shader代码
precision mediump float;
uniform sampler2D u_texture;
uniform vec4 keyRGBA;
uniform vec2 range;
varying vec2 v_texCoord;
vec2 RGBToCC(vec4 rgba) {
float Y = 0.299 * rgba.r + 0.587 * rgba.g + 0.114 * rgba.b;
return vec2((rgba.b - Y) * 0.565, (rgba.r - Y) * 0.713);
}
void main() {
vec4 srcColor = texture2D(u_texture, v_texCoord);
vec2 srcCC = RGBToCC(srcColor);
vec2 keyCC = RGBToCC(keyRGBA);
float mask = sqrt(pow(keyCC.x - srcCC.x, 2.0) + pow(keyCC.y - srcCC.y, 2.0));
mask = smoothstep(range.x, range.y, mask);
if (mask == 0.0) { discard; }
else if (mask == 1.0) { gl_FragColor = srcColor; }
else {
gl_FragColor = max(srcColor - (1.0 - mask) * keyRGBA, 0.0);
}
}
上面算法使用 CPU(纯js代码)也能实现,但性能会差很多
除了上面分析的核心代码之外还有一些为了让Shader运行起来的辅助代码,属于 WebGL 的基础知识,查看完整代码
如何使用
import { createChromakey } from '@webav/av-cliper'
或复制完整代码到项目中- 参考以下示例
import { createChromakey } from '../src/chromakey'
const cvs = document.querySelector('#canvas') as HTMLCanvasElement
const ctx = cvs.getContext('2d', {
alpha: true
})!
;(async () => {
const img = new Image()
img.src = './public/img/green-dog.jpeg'
await new Promise(resolve => {
img.onload = resolve
})
const chromakey = createChromakey({
keyColor: [65, 249, 0]
})
ctx.drawImage(await chromakey(img), 0, 0, cvs.width, cvs.height)
})()
传入一张 720P 的图片给 chromakey
首次执行(包括初始化)大概耗时 20ms,后续每次执行基本在 1ms 之内;
所以性能方面实现视频实时抠图没有压力,将 Video 标签传给 chromakey 快速刷新即可。
async function render() {
ctx.drawImage(await chromakey(videoElement), 0, 0, cvs.width, cvs.height)
requestAnimationFrame(render) // 注意:后台页面 requestAnimationFrame 停止执行
}
render()
今天的文章WebGL Chromakey 实时绿幕抠图分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/59664.html