把一张图变成 @%#*+=-:. 这种字符密集排列的”字符画”——背后只有一个简单的核心思路:像素亮度 → 字符密度。但要做出好看的字符画,缩放、字符表选择、宽高比补偿三件事都要做对。
算法骨架
原图(任意尺寸)
↓ 缩放到目标字符数(注意宽高比补偿)
缩放图(字符数 × 字符数 像素)
↓ 灰度化(RGB → Y)
灰度图(每像素 0-255)
↓ 字符表映射
字符画(每像素一个字符)
灰度化公式
| 公式 | 适用 |
|---|---|
Y = 0.299R + 0.587G + 0.114B | ITU-R BT.601,与人眼对绿色敏感一致,常用 |
Y = 0.2126R + 0.7152G + 0.0722B | ITU-R BT.709,HDTV 标准 |
Y = (R + G + B) / 3 | 朴素平均,速度快但偏色明显 |
Y = max(R, G, B) | 取最亮通道,保亮度但失真 |
字符画用 BT.601 即可,差异在字符画尺度上不明显。
字符密度阶
10 阶常用表(浅 → 深):
" .:-=+*#%@"
Paul Bourke 70 阶(精度更高):
$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,"^`.
自定义建议:
| 场景 | 推荐字符表 |
|---|---|
| 极简 / Logo | " █"(二值) |
| 文字注释中的小图 | " .:#"(4 阶) |
| 一般用途 | " .:-=+*#%@"(10 阶) |
| 高保真大图 | Bourke 70 阶 |
| 黑底白字(终端) | 表反向:"@%#*+=-:. " |
宽高比补偿
等宽字符的显示宽高 ≈ 1 : 2
→ 目标高度 = 像素高度 / 2
→ 或者:采样时跳行(每两行采一行)
没补偿时:人脸被纵向拉长——这是字符画最常见的”看起来怪”的原因。
颜色字符画
| 输出格式 | 着色方法 |
|---|---|
| 终端 | ANSI 24-bit \033[38;2;R;G;Bm字符\033[0m |
| HTML | <span style="color:#xxx">字符</span> |
| SVG | <text fill="#xxx">字符</text> |
| 图片 | Canvas 逐字符 fillStyle 渲染 |
坑:
- 老 Windows cmd 只支持 16 色,要做调色板量化
- HTML 输出每字符
<span>包裹的话,节点数巨大,长字符画会卡浏览器——用 Canvas 渲染再导出图片更稳 - 复制到 IM / 邮件 / Word 等富文本会丢失等宽字体属性,字符画必须粘到代码块或纯文本环境才保持比例
进阶:边缘检测增强
简单灰度映射的字符画,形状不锐利——人物轮廓边界处往往糊一团。
优化路径:
- 先做一次 Sobel 边缘检测,得到边缘图
- 边缘像素位置用 方向字符(
/ \ | _ -)替代密度字符 - 非边缘区域照常用密度字符填充
这就是 jp2a、aalib、libcaca 等老牌字符画工具的差异化所在。
实操参数推荐
| 输入 | 字符宽 | 字符表 | 备注 |
|---|---|---|---|
| 简单 Logo | 60-80 | 10 阶 | 大致够 |
| 头像 / 人物 | 100-150 | 70 阶 | 细节多,越多越好 |
| 风景照 | 150-200 | 70 阶 | 同上 |
| 复杂场景 / 漫画 | 200-300 | 70 阶 + 边缘检测 | 否则糊成团 |
| 黑白二维码 / 简笔画 | 30-50 | 二值 | 锐利度优先 |
实用场景
字符画不只是好玩——
- CLI 工具启动 logo(
figlet/cowsay风格) - 代码注释里的流程图:箭头方框比真实图片更适合 code review
- README banner:GitHub 项目首屏吸引力,零外部资源
- 邮件 / IM 签名:纯文本通用
- 教学演示:数据结构、算法过程
- 怀旧风格化:终端 dashboard、复古网页
试着把一张照片转成字符画看看,调一调参数你会理解每一个参数的作用。