直接用下面overlay带有透明通道的视频时会出现,浮层视频边缘锐化,或者亮度变暗等等问题
ffmpeg -i in.mp4 -i x.mov -filter_complex "[0][1]overlay" -y output.mp4
改进方式:
添加参数alpha=1
ffmpeg -i in.mp4 -i x.mov -filter_complex "[0][1]overlay=alpha=1" -y output.mp4
https://ffmpeg.org/ffmpeg-filters.html#overlay-1
解释:
alpha : Set format of alpha of the overlaid video, it can be straight or premultiplied. Default is straight.
- straight
- premultiplied
参考https://segmentfault.com/a/1190000002990030 解释了为什么要用premultiplied;
Alpha Blending
要搞清楚这个问题,先得理解Alpha通道的工作原理,如果你已经了解可以直接跳过。
最常见的像素表示格式是RGBA8888即 ( r , g , b , a ) (r, g, b, a) (r,g,b,a),每个通道8位,0-255。例如红色60%透明度就是 ( 255 , 0 , 0 , 153 ) (255, 0, 0, 153) (255,0,0,153),为了表示方便alpha通道一般记成正规化后的0-1的浮点数,也就是 ( 255 , 0 , 0 , 0.6 ) (255, 0, 0, 0.6) (255,0,0,0.6)。而 Premultiplied Alpha 则是把RGB通道乘以透明度也就是 (r a, g a, b * a, a),50%透明红色就变成了 ( 153 , 0 , 0 , 0.6 ) (153, 0, 0, 0.6) (153,0,0,0.6)。
透明通道在渲染的时候通过 Alpha Blending 产生作用,如果一个透明度为
α
s
\alpha_s
αs 的颜色
C
s
C_s
Cs 渲染到颜色
C
d
C_d
Cd上,混合后的颜色通过以下公式计算,
C
o
=
α
s
∗
C
s
+
(
1
−
α
s
)
∗
C
d
C_o = \alpha_s * C_s + (1- \alpha_s)* C_d
Co=αs∗Cs+(1−αs)∗Cd
以60% 透明的红色渲染到白色背景为例:
C
o
=
(
255
,
0
,
0
)
⋅
0.6
+
(
255
,
255
,
255
)
⋅
(
1
−
0.6
)
=
(
255
,
102
,
102
)
C_o =(255,0,0)⋅0.6+(255,255,255)⋅(1−0.6)=(255,102,102)
Co=(255,0,0)⋅0.6+(255,255,255)⋅(1−0.6)=(255,102,102)
也就是说,从视觉上,
(
255
,
0
,
0
,
0.6
)
(255, 0, 0, 0.6)
(255,0,0,0.6)渲染到白色背景上 和
(
255
,
102
,
102
)
(255, 102, 102)
(255,102,102) 是同一个颜色。如果颜色以 Premultiplied Alpha 形式存储,也就是
C
s
C_s
Cs 已经乘以透明度了,所以混合公式变成了:
为什么要 Premultiplied Alpha 呢?
Premultiplied Alpha 后的像素格式变得不直观,因为在画图的时候都是先从调色板中选出一个RGB颜色,再单独设置透明度,如果RGB乘以透明度就搞不清楚原色是什么了。从前面的 Alpha Blending 公式可以看出,Premultiplied Alpha 之后,混合的时候可以少一次乘法,这可以提高一些效率,但这并不是最主要的原因。最主要的原因是:
没有 Premultiplied Alpha 的纹理无法进行 Texture Filtering(除非使用最近邻插值)。
以最常见的 filtering 方式线性插值为例,一个宽2px高1px的图片,左边的像素是红色,右边是绿色10%透明度,如果把这个图片缩放到1x1的大小,那么缩放后1像素的颜色就是左右两个像素线性插值的结果,也就是把两个像素各个通道加起来除以2。如果使用没有 Premultiplied Alpha 的颜色进行插值,那么结果就是:
(
(
255
,
0
,
0
,
1
)
+
(
0
,
255
,
0
,
0.1
)
)
⋅
0.5
=
(
127
,
127
,
0
,
0.55
)
((255,0,0,1)+(0,255,0,0.1))⋅0.5=(127,127,0,0.55)
((255,0,0,1)+(0,255,0,0.1))⋅0.5=(127,127,0,0.55)
如果绿色 Premultiplied Alpha,也就是 (0, 255 * 0.1, 0, 0.1),和红色混合后:
(
(
255
,
0
,
0
,
1
)
+
(
0
,
25
,
0
,
0.1
)
)
⋅
0.5
=
(
127
,
25
,
0
,
0.55
)
((255,0,0,1)+(0,25,0,0.1))⋅0.5=(127,25,0,0.55)
((255,0,0,1)+(0,25,0,0.1))⋅0.5=(127,25,0,0.55)
从上面的图里第三个颜色是没有 Premultiplied Alpha 的混合结果,对比第四个 Premultiplied Alpha 后颜色的结果,显然第四个颜色更符合直觉,第三个颜色太绿了,因为绿色通道没有乘以透明度,所以在线性插值的时候占了过大的权重。
所以 Premultiplied Alpha 最重要的意义是使得带透明度图片纹理可以正常的进行线性插值。这样旋转、缩放或者非整数的纹理坐标才能正常显示,否则就会像上面的例子一样,在透明像素边缘附近产生奇怪的颜色。