加中文水印的第一步不是写命令,是挑字体。 挑错的信号很明确:四个字画出来像素数一模一样,那就是豆腐块。
前面所有文字水印都默认「字能显示出来」。这一节处理「显示不出来」的情况。 中文比英文多一道坎:字形不在字体里,画出来就是方块。
一、四个字体,同一句「水印测试」
字体 包围盒 像素 填充率 四个字一样吗
msyh 96×23 1073 0.49 各不相同 simhei 95×23 1003 0.46 各不相同 simsun 94×22 795 0.38 各不相同 arial 66×16 208 0.20 完全一样
最后一行 arial 的四个字像素数一模一样——这不是巧合,是缺字形的铁证。 它把每个汉字都画成同一个空心方框,也就是俗称的「豆腐块」。
填充率也是线索:真汉字笔画有疏有密,在 0.38 到 0.49 之间; 豆腐块只有一圈框边,填充率明显偏低。
二、三个中文字体怎么选
字体 观感 适合什么
微软雅黑 现代、笔画匀 水印、字幕首选 黑体 更粗、更醒目 小字号也能看清 宋体 横细竖粗 正式文档,不适合小字
同一句文字,黑体的像素是宋体的 1.26 倍。 所以缩到同样大小,黑体更抗压缩,宋体的细横最容易先糊掉。
视频里还有一层额外损失:编码会抹掉高频细节, 宋体那种一像素宽的横线,在低码率下经常整行消失。
三、命令行的写法
-vf "drawtext=text='水印测试':fontfile='C\:/Windows/Fonts/msyh.ttc':fontsize=24"
fontfile 不写会回落到一个编译期默认字体,那个字体通常没有汉字。
所以加中文水印,「指定字体」不是可选项,是必须做的事。
路径里的冒号还要转义,与 6.2.2 讲 movie 时是完全同一条规则。 同一个解析器,同一套坑,学会一次到处用。
四、动手 Demo:四个字体逐个量
用四个字体画同一句「水印测试」,量包围盒、非透明像素与填充率。 再逐字渲染,看四个字的像素数是不是完全一样。
import numpy as np
from PIL import Image, ImageDraw, ImageFont
TXT = u"水印测试"
def draw(text, fn, size=24):
f = ImageFont.truetype(u"C:/Windows/Fonts/" + fn, size)
bb = ImageDraw.Draw(Image.new(u"RGBA", (8, 8))).textbbox(
(0, 0), text, font=f)
im = Image.new(u"RGBA", (bb[2] - bb[0] + 8, bb[3] - bb[1] + 8),
(0, 0, 0, 0))
ImageDraw.Draw(im).text((4 - bb[0], 4 - bb[1]), text, font=f,
fill=(255, 255, 255, 255))
return im, bb
for fn in (u"msyh.ttc", u"simhei.ttf", u"simsun.ttc", u"arial.ttf"):
im, bb = draw(TXT, fn)
a = np.array(im)[:, :, 3] > 0
ib = im.getbbox()
area = (ib[2] - ib[0]) * (ib[3] - ib[1])
per = [int((np.array(draw(c, fn)[0])[:, :, 3] > 0).sum()) for c in TXT]
print(u"%-12s %dx%d px %4d 填充 %.2f %s"
% (fn, ib[2] - ib[0], ib[3] - ib[1], int(a.sum()),
a.sum() / float(area),
u"四个一样" if len(set(per)) == 1 else u"各不相同"))
实跑输出:
--------------------------------------------------------------------
6.6.1 -vf 的 drawtext 添加中文水印——先挑对字体
--------------------------------------------------------------------
**① 四个字体,同一句「水印测试」**
字体 包围盒 非透明像素 填充率 四个字一样吗
--------------------------------------------------------------------
msyh.ttc 96×23 1073 0.49 各不相同
simhei.ttf 95×23 1003 0.46 各不相同
simsun.ttc 94×22 795 0.38 各不相同
arial.ttf 66×16 208 0.20 完全一样
**最后一行 arial 的四个字像素数一模一样——这不是巧合,是缺字形的铁证。**
**它把每个汉字都画成同一个空心方框,也就是俗称的「豆腐块」。**
**填充率也是线索:真汉字笔画有疏有密,填充率在 0.4 到 0.5 之间;**
**豆腐块只有一个方框的边,填充率明显偏低。**
**② 三个中文字体哪个更合适**
字体 观感 适合什么
------------------------------------------------------------------
msyh(微软雅黑) 现代、笔画匀 水印、字幕首选
simhei(黑体) 更粗、更醒目 小字号也能看清
simsun(宋体) 笔画细、横细竖粗 正式文档,不适合小字
**同一句文字,黑体的像素是宋体的 1.26 倍——**
**所以缩到同样大小,黑体更抗压缩,宋体的细横最容易先糊掉。**
**③ 命令行怎么写**
**`-vf "drawtext=text='水印测试':fontfile='C\\:/Windows/Fonts/msyh.ttc':fontsize=24"`**
**`fontfile` 不写会回落到一个编译期默认字体,那个字体通常没有汉字。**
**所以加中文水印,「指定字体」不是可选项,是必须做的事。**
**路径里的冒号还要转义,与 6.2.2 讲 movie 时是同一条规则。**
im.getbbox() 量的是「真正的墨迹范围」,与 textbbox 差一两个像素。
填充率要算得准,必须用墨迹包围盒,用 textbbox 会把留白算进去。
填充率 = 字形像素 ÷ 墨迹包围盒面积。真汉字在 0.4 左右, 豆腐块只有框边,会掉到 0.2 以下,这个指标比看图片更客观。
arial.ttf 在 Windows 上通常不含汉字,但它不会报错,只是画方框。 所以「能渲染」和「渲染对了」是两件事,必须靠数值判断。
30 秒实验
- 换字号:把 24 换成 12,看豆腐块的像素数是不是也等比缩。
- 换 mac 字体:试
msyhbd.ttc(雅黑粗体),看像素涨多少。 - 真跑对比:有 drawtext 的机器上,故意写
fontfile指向 arial,看画面。
常见误区
- 不指定 fontfile。回落到默认字体,中文基本全是方块。
- 用 arial 之类西文字体写中文。能渲染,但渲染的是豆腐块。
- 用宋体做小字号水印。细横在低码率下会被整行抹掉。
自测三问(点开对答案)
1. 怎么判断渲染出来的是豆腐块? —— 逐字量像素数,四个字完全一样就是豆腐块;再看填充率是否低于 0.2。 2. 三个中文字体哪个最适合视频水印? —— 黑体或雅黑。宋体笔画太细,低码率下容易糊掉。 3. fontfile 为什么不能省? —— 不写会回落到编译期默认字体,那个字体通常没有汉字。- ☐ (本节完,下一节 6.6.2 -vf 的 drawtext 解决中文乱码问题)