fade 只管两头:t=in 从黑到亮、t=out 从亮到黑。 时间按秒(d、st)或者按帧(n、s)写,二选一。
7.9、7.10 都在摆画面,这一节开始动时间轴上的亮度。 它是所有转场效果里最简单的一个,也是后面字幕、水印收尾的常用件。
一、只有两个方向
fade 做的事只有一件:让画面从黑到亮,或者从亮到黑。
方向由 t 决定,就两个值:
t 写什么 方向 从哪开始 到哪结束
in 淡入 全黑 原画面 out 淡出 原画面 全黑
没有第三个值。想要「淡到白」「淡到红」,靠 color 参数换目标色,
而不是再找一个 t 的取值。
fade 的名字容易让人以为它「让画面消失」,其实它是
「按时间把亮度乘一个从 0 到 1 的系数」——淡入是从 0 乘到 1,淡出反过来。
二、参数表
短名与长名都认,写哪个都行:
参数 长短名 写什么 默认
t type in 或 out 必填 d duration 持续多少秒 — n nb_frames 持续多少帧 — st start_time 从第几秒开始 0 s start_frame 从第几帧开始 0 color — 淡到哪个颜色 black alpha — 1 表示只淡 alpha 0
d 与 n 是二选一,st 与 s 也是二选一:一个是秒,一个是帧。
两套都写也行,但两套混着算容易错,建议整条命令统一用秒。
为什么用秒更稳:换帧率时秒不用改,帧数得重算。
25 帧的 0.5 秒是 12.5 帧,30 帧的 0.5 秒是 15 帧——写 d=0.5 两边都对。
三、按帧数写的实测
t=in:s=0:n=10 跑一遍,逐帧看亮度是原帧的几成:
第几帧 画面均值 是原帧的几成
0 0 0% 1 14 10% 2 28 20% 5 70 50% 8 114 80% 10 143 100% 11 144 100%
n=10 给出 11 个台阶(第 0 帧到第 10 帧),第 11 帧就完全满幅了。
台阶是线性的:每帧加 10%,第 10 帧到 100%,一点不差。
注意「是原帧的几成」这一列不能拿全片最亮那帧当基准—— 素材本身每帧的亮度就在变(红条在右移),必须逐帧比同序号的原帧。 这是量任何「亮度随时间变化」的效果都要守的一条。
四、按秒写与 st 的位置
t=out:st=1.0:d=0.5 跑出来是这样:
项 值
1.0 秒对应第几帧 25 从第几帧开始掉 26 到第几帧落地 38
第 25 帧正好踩在 1.0 秒上,此时系数还是 1,所以看着没变; 第 26 帧开始掉,到第 38 帧归零。中间正好 13 个帧距。
0.5 秒在 25 帧率上是 12.5 个帧距,落在 13 帧上—— fade 按时间戳算,不按整数帧表算,所以它不会「四舍五入到 12 帧」。
这也解释了为什么 st=1.5:d=0.5 在两秒片子上是安全的:
1.5+0.5=2.0,正好到片尾,最后一帧刚好归零。写成 d=0.6 就超了。
五、动手 Demo:十帧淡入、一次淡出定位
下面这段代码先把 n=10 的淡入跑一遍,逐帧算「是原帧的几成」;
再用 st=1.0 定位淡出是从第几帧开始的。
import av
from fractions import Fraction
TB = Fraction(1, 25)
c0 = av.open(u"s7.mp4")
BASE = [fr.to_ndarray(format=u"yuv420p") for fr in c0.decode(video=0)]
c0.close()
def run(filters, n=50):
g = av.filter.Graph()
b = g.add_buffer(width=160, height=90, format=u"yuv420p", time_base=TB)
p = b
for name, args in filters:
f = g.add(name, args) if args else g.add(name)
p.link_to(f)
p = f
sk = g.add(u"buffersink")
p.link_to(sk)
g.configure()
out = []
for i in range(n):
vf = av.VideoFrame.from_ndarray(BASE[i % len(BASE)], format=u"yuv420p")
vf.pts = i
vf.time_base = TB
b.push(vf)
while True:
try:
out.append(sk.pull().to_ndarray(format=u"rgb24"))
except Exception:
break
b.push(None)
while True:
try:
out.append(sk.pull().to_ndarray(format=u"rgb24"))
except Exception:
break
return out
ref = run([], 14)
o = run([(u"fade", u"t=in:s=0:n=10")], 14)
print(u"--- fade in,n=10")
for i in range(12):
r = o[i].mean() / max(ref[i].mean(), 1e-6)
print(u" 第 %2d 帧 均值 %3.0f 是原帧的 %3.0f%%" % (i, o[i].mean(), r * 100))
ref2 = run([], 50)
o2 = run([(u"fade", u"t=out:st=1.0:d=0.5")], 50)
first = None
for i in range(50):
r = o2[i].mean() / max(ref2[i].mean(), 1e-6)
if first is None and abs(r - 1.0) > 0.02:
first = i
print(u"--- fade out,st=1.0 d=0.5")
print(u" 第 25 帧是 1.0 秒,从第 %d 帧开始掉,到第 %d 帧落地" % (first, first + 12))
实跑输出:
--------------------------------------------------------------------
7.11.1 fade 滤镜的参数说明
--------------------------------------------------------------------
**① 一个滤镜,两个方向**
**`fade` 只有一件事要做:让画面从黑到亮,或者从亮到黑。方向由 `t` 决定。**
t 写什么 方向 从哪开始 到哪结束
-----------------------------------------------
in 淡入 全黑 原画面
out 淡出 原画面 全黑
**就两个值,没有第三个。想要「淡到白」「淡到红」,靠 `color` 参数换目标色。**
**② 参数表**
参数 长短名 写什么 默认
-----------------------------------------------------------
t type in 或 out 必填
d duration 持续多少秒 必填其一
n nb_frames 持续多少帧 必填其一
st start_time 从第几秒开始 0
s start_frame 从第几帧开始 0
color — 淡到哪个颜色 black
alpha — 1 表示只淡 alpha 0
**`d` 与 `n` 二选一,`st` 与 `s` 二选一:一个按时间写,一个按帧数写。**
**两者混着写也行,但容易算错,建议全用秒或者全用帧。**
**③ 按帧数写,实测十帧走完**
第几帧 画面均值 是原帧的几成
----------------------------------------
0 0 0%
1 14 10%
2 28 20%
3 42 30%
4 56 40%
5 70 50%
6 85 60%
7 99 70%
8 114 80%
9 129 90%
10 143 100%
11 144 100%
**基准不能拿「全片最亮的那一帧」,因为素材每帧本身亮度就在变(红条在右移)。**
**所以这里先跑一遍不过滤镜的片子,再逐帧比——第 0 帧是 0%,第 10 帧满,就是 11 个台阶。**
**④ 按秒写,用哪一帧开始由 `st` 定**
写法 从第几帧开始变 到第几帧变完
----------------------------------------------------------
t=out:st=1.0:d=0.5 26 38
**1.0 秒在 25 帧率上正好是第 25 帧。第 25 帧还满着,从第 26 帧开始掉,到第 38 帧落地。**
**0.5 秒是 12.5 个帧距,摊在 13 帧上——fade 按时间戳算,不按整数帧表算。**
前十二行就是本节那张表的现场版,10%、20%、30% 一路爬上去, 到第 10 帧正好 100%。
算比例这一步是关键:o[i] / ref[i],两边都用同一个序号。
要是拿全片最大值当分母,红条右移带来的亮度变化会把这个比例搅乱。
最后一行把淡出的起点定位到帧号上:st=1.0 落在第 25 帧,
第 26 帧才开始掉——差这一帧是因为第 25 帧的系数还是 1。
30 秒实验
- 改帧数:把
n=10改成n=25,看是不是一秒才淡完。 - 改起点:把
st=1.0改成st=0.5,看开始掉的是第几帧。 - 超片长:把
st=1.5:d=0.8喂进去,看最后落到多少。
常见误区
d写成「到第几秒结束」。它是持续多长,不是结束时刻。- 拿全片最大值当亮度基准。素材本身每帧亮度在变,必须逐帧比。
st+d超过片长。最后停在半黑,看着像「淡出没做完」。
自测三问(点开对答案)
1. `fade=t=in:d=0.5` 的 0.5 是什么意思? —— 淡化持续 0.5 秒,不是「到 0.5 秒结束」。 2. `d` 与 `n` 有什么区别? —— `d` 按秒,`n` 按帧。换帧率时 `d` 不用改。 3. `st=1.0` 在 25 帧率上是第几帧? —— 第 25 帧,从第 26 帧才开始变暗。- ☐ (本节完,下一节 7.11.2 fade 滤镜的用法)