使用Unity Shader 实现透明度抠绿(AlphaMatting)

1、使用CbCr空间抠绿

YCbCr:是色彩空间的一种,通常会用于影片中的影像连续处理,或是数字摄影系统中。Y’为颜色的亮度(luma)成分、而
CB和CR则为蓝色和红色的浓度偏移量成份。Y’和Y是不同的,而Y就是所谓的亮度(luminance),表示光的浓度且为非线性,使用伽马修正(gamma correction)编码处理。
使用Unity Shader 实现透明度抠绿(AlphaMatting)计算代码:

float3 RGB_To_YCbCr(float3 rgb) {
	float Y = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b;
	float Cb = 0.564 * (rgb.b - Y);
	float Cr = 0.713 * (rgb.r - Y);
	return float3(Cb, Cr, Y);
}

但是这里我们不需要这么复杂,我们把它变成更简单的计算方法

float2 RGB_To_MyCbCr(float3 rgb) {
	float Cb = rgb.b - rgb.g;
	float Cr = rgb.r - rgb.g;
	return float2(Cb, Cr) * 0.5 + 0.5;
 }

这样每个颜色都可以通过这个函数获取它的CbCr坐标,因此相应的CbCr坐标:绿(0,0),黑白:(0.5,0.5),紫(1,1)。通过距离我们选中的颜色(绿色)的距离即可判断这个颜色是否应该被抠掉或是保留。

这样,我们可以根据:选择的绿色,最小裁剪距离和最大保留距离来计算一个颜色的不透明度(计算时,裁剪距离<=保留距离)
一个颜色在CbCr空间中的保留情况:
使用Unity Shader 实现透明度抠绿(AlphaMatting)

2、边缘虚化

为了让图片更好的融入背景,我们需要做边缘模糊
这里使用了一个3*3、Sigma=1的高斯卷积核计算当前像素和周围8个像素组成的九宫格算子。通过算子,计算出一个透明度值(比如当前颜色不透明,但周围颜色都是绿色,那么这个颜色很有可能是边缘,需要被虚化)。

使用Unity Shader 实现透明度抠绿(AlphaMatting)
(这里+0.00016%是为了保证整体总权重和为100%)
完成:
使用tex2D对input.texcoord0位置像素和周围8个(_Flaming_Pear_Range_Temp * X / width,± _Flaming_Pear_Range_Temp * Y / height)其中X和Y取值是(-1,0,1),目的是对九宫格UV取值,将最终混合后的a值存储在sumColorA中。

sumColorA = pow(sumColorA, _Flaming_Pear_Lucency_Degree * 3);//取值变为曲线,而非线性
c.a = sumColorA;

3、简单去绿

半透明的物体会与背景的绿色混合,小物体(如头发)将无法准确获得其原始颜色,因为它们没有像素那么大。
就像一杯水或一片绿幕中间的一根头发,我们之间的绿色来自于它们本身并没有反射太多绿色,这是无法通过色彩校正来校正的。
这一步主要是为了处理一些意外,让场景中不再有绿色通过扣绿之后的颜色,仍然可能出现没有去除掉的绿色。这一步通过检查rgb通道,进行一个简单的去绿。因为这步会改变原色,有时需要谨慎使用。
那么一个颜色去绿可以通过两种方式:1减小G通道值,2同时增加等量的RB通道值。
完成:
首先,去绿的目标颜色肯定是绿色(G>R && G>B)。
其次具体减小G或增加RB量多少,取决于这个颜色的亮度和G与相距其最小的R或B通道的差。

float getColorLuminance(float4 rgb) //获取颜色亮度
{
	return (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b);
 }
 亮度 = getColorLuminance();
暗度 = 1 - 亮度;
最大变换量 = (c.r > c.b)?(c.g - c.r):(c.g - c.b);
红蓝颜色增量 = 亮度 * 最大变换量;
绿色减量 = 暗度 * 最大变换量;

从图像上看,此时图像的透明部分已经变成透明的,没有出现绿色的现象。但是,在场景中肉眼可能仍然看到绿色,而我们的肉眼感知黄色比橙色更绿,所以当黄色靠近橙色时,黄色显得偏绿。所以我们还需要色彩校正。

4、颜色矫正处理

场景中的物体被光照亮,最终这些光汇聚到相机上看到当前的图像,因为我们在一个房间里,我们可能会被绿幕或绿棚漫反射的光通过。并反射多余的绿色。这一步是去除这些漫射绿色。
使用Unity Shader 实现透明度抠绿(AlphaMatting)
假设屏幕空间的光是均匀的,只考虑漫反射(绿幕或绿棚的镜面反射的程度很低),根据物体反射出绿是多少,就可以计算当前色块对应绿色吸收率(比如理论上,黑色和白色对绿色的吸收率不同,场景中均匀的漫反射绿光打上去,增加的G值就不同)。因为这一步的目的是降低绿光对场景的影响,可以不考虑将画面提亮(不对RB通道进行处理)。

完成:
设置一个[0,1]的场景中绿光漫反射强度greenIntensity,0为最强(保持当前图像状态,不对图像颜色进行任何处理),1为最弱。
所以理论上的代码是这样的:

fixed overMainColor = c.g * greenIntensity;
c.g = lerp(c.g, overColorGreen, c.g);

但为了效果更好,可以把Lerp的程度改为矫正率,这样更贴近实际我们选择的颜色

使用Unity Shader 实现透明度抠绿(AlphaMatting) 先算出颜色校正最多可以校正什么颜色,然后根据假设的绿光漫反射强度和校正率计算出最终颜色

fixed rectify = 使用简单去绿之前的颜色值进行上图取值;
fixed overMainColor = c.g * greenIntensity;
c.g = lerp(c.g, overColorGreen, rectify);

5、更多优化

以上只是一些基本的绿化处理。如果想实现更高级的处理,可以通过:
·尝试YCbCr空间进行计算
使用更大的边缘模糊算子
使用更多的绿键颜色样本
·使用蒙版专门化某些部分(如处理阴影或边框)
·保存并分析前几帧的数据,进行动态抠绿,这个方案需要使用Unity后处理那套机制了,而不是简单的Shader
·使这个Shader对蓝幕进行处理(跟绿幕同理,这里就不展开了)(再括号 不用想着红幕,没有客户有那种需求)
阴影和其他后期处理:
阴影处理我想到的有2种:
1就是在场景中打光的时候留意人的影子,别补光把影子补没了,这样扣绿扣不下去,会留在图像中。这种影子也更真实。
2使用假影子:因为Unity不支持半透明阴影,这里可以由我们自己写一个假阴影,这步其实就是扣绿流程的简化,并最终输出黑白色的面片放在场景中
其他的后期处理就靠想象了,比如加一点环境光,就像根据图像色调对渲染进行风格化一样。
硬件可以通过调整图像输入设备(摄像头)和显示分辨率(显卡必须支持)进行优化,使最终输出的图像效果更好

6、效果展示

(图片来源为截图,分辨率没有原视频高,加上绿剪技术还不够成熟,专业绿剪软件,没有人一定要做好)
CbCr图测试处理(这个CbCr图不标准):

使用Unity Shader 实现透明度抠绿(AlphaMatting)使用Unity Shader 实现透明度抠绿(AlphaMatting)
由于运动模糊处理四肢:
使用Unity Shader 实现透明度抠绿(AlphaMatting)使用Unity Shader 实现透明度抠绿(AlphaMatting)
头发处理:头发的分辨率和图像的分辨率有很大关系(相当大),可以看出头发还是有点绿的,这是因为校色无法处理这种混色,而且简单的绿化算法太简单了,不能很好的处理这种混合色,以后有时间再优化。 使用Unity Shader 实现透明度抠绿(AlphaMatting)使用Unity Shader 实现透明度抠绿(AlphaMatting)
透明物体:
使用Unity Shader 实现透明度抠绿(AlphaMatting)使用Unity Shader 实现透明度抠绿(AlphaMatting)

阴影处理:
使用Unity Shader 实现透明度抠绿(AlphaMatting)使用Unity Shader 实现透明度抠绿(AlphaMatting)
如果您有代码,稍后您有时间整理它会发布。
(这是我的第一篇技术分享文章,如有错误或不准确之处,请指正)
引用:https://www.cnblogs.com/mtcnn/p/9411978.html
图片来源:
https://www.bilibili.com/video/BV1bf4y1R7oa?spm_id_from=333.999.0.0
https://www.bilibili.com/video/BV1SK4y1j7oh/?spm_id_from=333.788.recommend_more_video.0
https://www.bilibili.com/video/BV1C44y177a7/?spm_id_from=333.788.recommend_more_video.9

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(1)
社会演员多的头像社会演员多普通用户
上一篇 2022年4月3日 下午7:25
下一篇 2022年4月3日 下午7:40

相关推荐