四宫格就是四个坐标:0_0、w0_0、0_h0、w0_h0。 空白处默认是绿的,fill=black 基本是必写的一句。
上一节把三条参数讲清了,这一节落到最小的完整例子:四格。 四格能通,九格只是多写五条坐标。
一、四个坐标
四宫格是两行两列,四条坐标一句话说完:
格子 坐标 含义
第 1 格 0_0 左上,原点 第 2 格 w0_0 右上,x 挪一格宽 第 3 格 0_h0 左下,y 挪一格高 第 4 格 w0_h0 右下,x、y 各挪一格
四条坐标里的 w0、h0 都是「第 0 路」的尺寸。
四格等大,所以 w0 = w1 = w2 = w3,用哪个都一样。
写 W_0 不行——W 是 overlay 的变量,xstack 里没有这个符号。
两个滤镜的表达式变量表不一样,混着记最容易出错。
二、四色各占一格
四路纯色拼起来,量每格中心色:
格子 输入 中心 RGB 什么色
第 1 格 red (253, 0, 0) 红 第 2 格 green (0, 127, 0) 绿 第 3 格 blue (0, 0, 254) 蓝 第 4 格 white (255, 255, 255) 白
输出 160×90,四格的中心色正好按输入顺序排下来。 这就是 xstack 的默认行为:第几路输入就摆在第几格。
green 量出来是 (0, 127, 0),不是 (0, 255, 0)——
ffmpeg 里的 green 是网页标准色 0x008000,比纯绿暗一半。要纯绿得写 lime。
蓝为什么是 254 不是 255,红为什么是 253:还是 YUV 范围压缩那个零头, 7.4.6 讲 color 滤镜时算过一遍,换个滤镜名字结论不变。
三、fill 不给就是绿的
四格等大时看不出 fill 的作用,所以故意给两路不同尺寸:
fill 写什么 空白处 RGB 是不是黑色
不给 (0, 136, 0) 不是 black (0, 0, 0) 是 red (252, 0, 0) 不是 0x336699 (48, 100, 151) 不是 white (253, 253, 253) 不是
不给 fill 时空白处是 (0, 136, 0)——不是黑,是个绿。
原因很简单:画布一开始是 YUV 全 0,Y=0、U=0、V=0 转成 RGB 就带绿味。
这不是 bug,是「没初始化」。
所以拼格子时 fill=black 基本是必写的一句,少写就会露出绿色空当。
顺带一个尺寸规律:两路 80×45 与 40×22 拼横排,输出是 120×45。 宽取两者相加,高取两者最大——矮的那格下方就被 fill 补上了。
四、动手 Demo:四色四格、fill 五档
下面这段代码先拼一张四色四宫格,量四格的中心色; 再给两路不同尺寸,试 fill 的五种写法。
import av
import numpy as np
L4 = u"0_0|w0_0|0_h0|w0_h0"
COL = [u"red", u"green", u"blue", u"white"]
def quad(fill=None, sizes=None):
g = av.filter.Graph()
sizes = sizes or [u"80x45"] * 4
srcs = [g.add(u"color", u"c=%s:s=%s:r=25" % (COL[i], sizes[i]))
for i in range(4)]
args = u"inputs=4:layout=%s" % L4
if fill:
args += u":fill=%s" % fill
xs = g.add(u"xstack", args)
sk = g.add(u"buffersink")
for i, p in enumerate(srcs):
p.link_to(xs, 0, i)
xs.link_to(sk)
g.configure()
return sk.pull().to_ndarray(format=u"rgb24")
def mid(a, i, cw=80, ch=45):
j, k = i // 2, i % 2
blk = a[j * ch + ch // 2 - 5:j * ch + ch // 2 + 5,
k * cw + cw // 2 - 5:k * cw + cw // 2 + 5]
return tuple(int(v) for v in blk.reshape(-1, 3).mean(axis=0))
a = quad()
print(u"--- 四色各占一格(输出 %dx%d)" % (a.shape[1], a.shape[0]))
for i in range(4):
print(u" 第 %d 格 %-6s %s" % (i + 1, COL[i], mid(a, i)))
print(u"--- fill 五档(两路不同尺寸,看空白处)")
for f in (None, u"black", u"red", u"0x336699", u"white"):
g = av.filter.Graph()
s1 = g.add(u"color", u"c=red:s=80x45:r=25")
s2 = g.add(u"color", u"c=blue:s=40x22:r=25")
args = u"inputs=2:layout=0_0|w0_0"
if f:
args += u":fill=%s" % f
xs = g.add(u"xstack", args)
sk = g.add(u"buffersink")
s1.link_to(xs, 0, 0)
s2.link_to(xs, 0, 1)
xs.link_to(sk)
g.configure()
o = sk.pull().to_ndarray(format=u"rgb24")
print(u" fill=%-10s %s" % (f, tuple(int(v) for v in o[38, 100])))
实跑输出:
--------------------------------------------------------------------
7.10.2 使用 FFmpeg 实现【四宫格】
--------------------------------------------------------------------
**① 先看 layout 怎么写**
**四宫格就是两行两列,四个坐标:**
格子 坐标 含义
---------------------------------------------
第 1 格 0_0 左上,原点
第 2 格 w0_0 右上,x 挪一格宽
第 3 格 0_h0 左下,y 挪一格高
第 4 格 w0_h0 右下,x、y 各挪一格
**注意 `w0` 里的 0 指的是「第 0 路输入的宽」,不是「第 1 格」。**
**四格等大时 `w0 = w1 = w2 = w3`,随便用哪个都一样;不等大时就要挑对了。**
**② 四色各占一格,量出来**
格子 输入 中心 RGB 什么色
----------------------------------------------------
第 1 格 red (253, 0, 0) 红
第 2 格 green (0, 127, 0) 绿
第 3 格 blue (0, 0, 254) 蓝
第 4 格 white (255, 255, 255) 白
**输出 `160×90`,四格的中心色正好是红、绿、蓝、白,顺序一路对下来。**
**这就是 xstack 的默认行为:第几路输入就摆在第几格。**
**`green` 量出来是 `(0, 127, 0)`,不是 `(0, 255, 0)`——**
**ffmpeg 里的 `green` 是网页标准的 0x008000,比纯绿暗一半。要纯绿得写 `lime`。**
**③ fill 不给就是绿色,不是黑色**
**四格等大时看不出这一条,所以故意给两路不同尺寸:**
fill 写什么 空白处 RGB 是不是黑色
--------------------------------------------------
不给 (0, 136, 0) 不是
black (0, 0, 0) 是
red (252, 0, 0) 不是
0x336699 (48, 100, 151) 不是
white (253, 253, 253) 不是
**不给 fill 时空白处是 `(0, 136, 0)`——因为画布一开始是 YUV 全 0,**
**Y=0、U=0、V=0 转成 RGB 就是个带绿味的色。这不是 bug,是没初始化。**
**所以拼格子时 `fill=black` 基本是必写的。**
**少写这一句,露出来的空当是绿的,一眼就能看出不对。**
第一种量法与本节那张表完全一致,红绿蓝白一路对下来。 量的是每格中心 10×10,避开格与格交界处的混色。
第二种五档 fill 里,只有 black 是 (0, 0, 0),
不给的那一档是 (0, 136, 0)——绿得挺明显,拼图时一眼就能看出来。
注意 green 与 lime 的区别在真实项目里会咬人:
想做「绿幕抠像」的底,写 green 会抠不干净,因为它是 0x008000 的暗绿。
30 秒实验
- 改颜色:把
COL换成[red, yellow, cyan, magenta],看四格量出来是什么。 - 改尺寸:把四格改成
160x90,看输出是不是变成 320×180。 - 删 fill:把两路不同尺寸那段的
fill去掉,确认空白处变绿。
常见误区
- 以为空白处默认是黑色。默认是 YUV 全 0 的绿。
- 把
green当纯绿。它是 0x008000,要纯绿写lime。 - 用
W、H写 xstack 的坐标。那是 overlay 的变量,xstack 只认w0、h0。
自测三问(点开对答案)
1. 四宫格的四条坐标怎么写? —— `0_0|w0_0|0_h0|w0_h0`。 2. 不给 fill,空白处是什么色? —— `(0, 136, 0)`,YUV 全 0 转出来的绿。 3. 两路 80×45 与 40×22 横排,输出多大? —— 120×45,宽相加、高取最大。- ☐ (本节完,下一节 7.10.3 实现【四宫格】的任意顺序)