pad 的四个数:总宽、总高、左边距、上边距,最后还能补一个颜色。 它只能把画布变大,而且高度必须是偶数,奇数值会被往下取。
7.2.6 已经用过 pad 补黑边,这一节把它当一个独立滤镜讲透。 7.8 的倒影第一步就是 pad,所以这一节是它的前置。
一、四个数加一个颜色
完整写法:
ffmpeg -i s7.mp4 -vf pad=200:120:20:15:black out.mp4
前两个是画布总尺寸,后两个是画面在画布里的左上角坐标,最后一个是补边的颜色。 注意:x、y 说的是「画面放哪」,不是「补多少」。
写 20:15 表示画面从 (20, 15) 开始放,于是左边补 20、上边补 15,
右边补 200-160-20=20、下边补 120-90-15=15。两边对称是巧合,不是规则。
二、第五个参数能写什么颜色
四种写法都试一遍,看左上角那个像素:
写法 左上角像素
pad=200:120:20:15:red [253, 0, 0] pad=200:120:20:15:0x336699 [49, 100, 151] pad=200:120:20:15:white [255, 255, 255] pad=200:120:20:15:black [0, 0, 0]
四个颜色都照写照出,说明颜色名与十六进制都认。
red 出来是 253 不是 255,还是那个 YUV 范围压缩的零头,7.4.6 讲过。
补边颜色能写 [email protected] 这种带透明度的,但输出得是 yuva420p 才有 alpha,
否则透明度被丢掉,还是实心黑。
三、高度必须是偶数
五个总高度试一遍:
写的总高 实际输出高
130 130 131 130 134 134 135 134 136 136
131 与 135 都被压成了 130 与 134,其余原样。 因为 yuv420p 的纵向色度是减半存的,高度必须是偶数,奇数值被往下取。
这个「往下取」很容易把后面的计算带偏: 你按 135 算了倒影的位置,实际画布只有 134,最后一行就被切掉了。
所以定画布时就要按偶数写,别指望它帮你凑整。 7.8 的倒影用的是 134 而不是 135,就是为了避开这一次取整。
四、pad 与 scale 的分工
两个都能改尺寸,但改的不是同一样东西:
滤镜 改什么 方向
scale 画面本身的大小 可大可小 pad 画面外面的画布 只能变大
pad 不允许把画布改小:目标尺寸比输入小直接报错。 要缩小画面用 scale,要加边框用 pad,两个经常一起出现。
标准写法是「先 scale 到目标比例,再 pad 到目标尺寸」, 7.2.5 的 decrease 加 pad 就是这一套,用来做等比缩放加黑边。
五、和 overlay 的关系
一句话分清:
pad 是「把自己的画布变大」,overlay 是「把别人的画面贴上来」。 只想加黑边、加留白、加边框,用 pad 一步到位; 要把另一段画面放进来,才需要 overlay。
两者的坐标语义一样:都是「左上角在哪」,都从 0 开始数,都对齐到偶数。 学会一个,另一个的坐标为也不会写错。
7.8 的倒影是两者合起来用的:先用 pad 把画布加高,再用 overlay 把翻过来的画面贴到底部。 所以这一节先讲透,下一节才不至于卡住。
六、动手 Demo:四种补色、五档高度、一次报错
下面这段代码先试四种补边颜色,再试五档总高度,最后试把画布改小。 取左上角那个像素,就能看出补的是什么色。
import av
from fractions import Fraction
TB = Fraction(1, 25)
c0 = av.open(u"s7.mp4")
base = next(c0.decode(video=0)).to_ndarray(format=u"yuv420p")
c0.close()
def pad(args):
g = av.filter.Graph()
b = g.add_buffer(width=160, height=90, format=u"yuv420p", time_base=TB)
f = g.add(u"pad", args)
sk = g.add(u"buffersink")
b.link_to(f)
f.link_to(sk)
g.configure()
vf = av.VideoFrame.from_ndarray(base, format=u"yuv420p")
vf.pts = 0
vf.time_base = TB
b.push(vf)
return sk.pull().to_ndarray(format=u"rgb24")
print(u"--- 补边颜色")
for c in (u"red", u"0x336699", u"white", u"black"):
o = pad(u"200:120:20:15:%s" % c)
print(u"%-10s -> 左上 %s" % (c, o[0, 0].tolist()))
print(u"--- 总高度取偶")
for hh in (130, 131, 134, 135, 136):
o = pad(u"160:%d:0:0:black" % hh)
print(u"写 %d -> 实得 %d" % (hh, o.shape[0]))
print(u"--- 改小会不会报错")
try:
pad(u"120:80:0:0:black")
except Exception as ex:
print(u"120:80 -> %s" % type(ex).__name__)
实跑输出:
--------------------------------------------------------------------
7.7 视频填充 pad 滤镜
--------------------------------------------------------------------
**① pad 的四个数:总宽、总高、左边距、上边距**
`ffmpeg -i s7.mp4 -vf pad=200:120:20:15:black out.mp4`
**前两个是画布总尺寸,后两个是画面在画布里的左上角坐标,最后一个是补边的颜色。**
**注意:x、y 说的是「画面放哪」,不是「补多少」。**
**② 第五个参数能写什么颜色**
写法 左上角像素
--------------------------------------------------
pad=200:120:20:15:red [253, 0, 0]
pad=200:120:20:15:0x336699 [49, 100, 151]
pad=200:120:20:15:white [255, 255, 255]
pad=200:120:20:15:black [0, 0, 0]
**四个颜色都照写照出,说明颜色名与十六进制都认。**
**`red` 出来是 253 不是 255,还是那个 YUV 范围压缩的零头,7.4.6 讲过。**
**③ 高度必须是偶数**
写的总高 实际输出高
---------------------------
130 130
131 130
134 134
135 134
136 136
**131 与 135 都被压成了 130 与 134,其余原样。**
**因为 yuv420p 的纵向色度是减半存的,高度必须是偶数,奇数值被往下取。**
**这个「往下取」很容易把后面的计算带偏:**
**你按 135 算了倒影的位置,实际画布只有 134,最后一行就被切掉了。**
**④ pad 与 scale 的分工**
滤镜 改什么 方向
-----------------------------------------
scale 画面本身的大小 可大可小
pad 画面外面的画布 只能变大
**pad 不允许把画布改小:目标尺寸比输入小直接报错。**
**要缩小画面用 scale,要加边框用 pad,两个经常一起出现。**
**⑤ 和 overlay 的关系**
**pad 是「把自己的画布变大」,overlay 是「把别人的画面贴上来」。**
**只想加黑边、加留白、加边框,用 pad 一步到位;**
**要把另一段画面放进来,才需要 overlay。**
**两者的坐标语义一样:都是「左上角在哪」,都从 0 开始数,都对齐到偶数。**
第一段四行跟本节那张表一一对上,红色是 253、十六进制是 [49, 100, 151]。
第二段五行里,131 与 135 被压成了 130 与 134, 其余三行原样——这就是「高度必须偶数」的直接证据。
最后一行是改小的情况,报的是 ArgumentError,
跟 7.4.2 里 crop 超尺寸报的是同一族错误(都源于 returned 22)。
30 秒实验
- 改坐标:把
20:15改成0:0,看左上角是不是变成画面内容。 - 量补边:写
pad=200:120:20:15:red,数一下左边到底有几列红。 - 试透明:写
[email protected]并把 pix_fmt 换成 yuva420p,看 alpha 有没有留下。
常见误区
- 以为 x、y 是补多少。它们是画面左上角的位置。
- 写奇数总高。会被往下取到偶数,位置全偏一行。
- 想用 pad 缩小画布。它只能变大,缩小要用 scale。
自测三问(点开对答案)
1. `pad=200:120:20:15` 右边补了多少? —— 20 列。`200 - 160 - 20 = 20`,左右碰巧相等。 2. 总高写 135 实际得到多少? —— 134。yuv420p 要求高度是偶数,奇数被往下取。 3. pad 能把画布改小吗? —— 不能,目标尺寸比输入小直接报 `returned 22`。- ☐ (本节完,下一节 7.8 视频倒影及镜面水面特效)