作者jserv (松鼠)
看板Gossiping
标题Re: [问卦] 用C语言排出张惠妹的脸很强吗?
时间Mon Mar 16 17:37:38 2026
※ 引述《bike3905 (RKTxLeo)》之铭言:
: https://i.imgur.com/ntEbO56.jpeg
: 有人说对岸用无人机排出张惠妹的脸
: 只要给我足够的时间
: 我能用C语言排出张惠妹的脸
: 这样很强吗?
我准备指派学生的作业时,想到上面的挑战。以 128x128 画素 RGB 来说,张惠妹
的脸部可用 776 位元组展现,简单的编码如下:
static const char *H =
"4ZmQ;9Z5\\1k^QTkmJD@=gbeB[@alfNAZ?>3NaIB]d_I7h9;=Q8aM^M=@3JEeg^9UOi1"
"eUBkjfK[`40\\IKQg?\\FR;CaFBaCe:ioATMIFmSUk^g05X4XRY3Xh4aoMFF8W`P7MRM"
"Jnk9Y\\MBVZ1NK2=YUHF@O`9NmC93M31_a5h@g\\mmd7AoeAAgm:?Z37>ZIGQ>F0YdMe"
"g?d]WWF@G`T=IKlV;KGbi8\\<5hmFS11A[]_Xf?B598nhd2anSoUVkDD`0Gm@fO]`B2L"
"K<cn6Sm\\TL_U<8<WN>[ZWGXU1\\7WZf;PQZhneTe7BV?<_7_1RKXoBI;VQRc?GakMY9"
"X7?FBcNm5LB>C7Bj<l>aaNoB@0k463f;IfPi5E@]DAeM00J=\\C901VlF035:>aCNTbT"
"WO9DAVW@DPZc`0[BU??[5J7R>5h7AHkc7h8:XW\\=NF2iYKJM[mZld=WSU:YdMiSjQn"
"\\k11:Kn>1WgW7_nKZWdmj23T3jZXBm]Wb[2f1R:i4VaD@LU:A1_9j`dA4eQ>WTXD>`m"
"85B@48_RjCHk1gcn5meS:EU3R\\m1i[i20=L30GDa>^Q7420@hljIdUNfoT6g;_3;FDS"
"C]cOXc=D97>^3Ko3C2IX1U21a9A];SU=QJK@XR]h^;:lHikZT8C]e1PL=NCVJhnA\\25"
"CUM_NJb59AQOC?H^;K2]Zn6RE:bBT:N75jm9nDoHllZ5U>_0L:T`ieTC1f=Gf0fVe<^d"
"eWZB2biMeY;kN7m70VWG?ANB92^K5JJ]AQD;6k9DO=L^76cmY^1ae8ojF3:F2GdGO2T6"
"=NEFX\\UHOcgKlLVU:`7nM?2MBDLQ<_:fb51S<C=W:KP9ZVI;f@g::Zbn=OL;IW8;L\\"
"GIYF;QO\\Pd<ZD_7W=MSG^W91GVEl?4`UQ2P;5=kV75SdL[;FY;WPjWo8>Nk>^4oKE\\"
"_12jbO_FmPW3oG7=cBWk?blijB48`@3Q:_ef1g^@0PhUS721ejdBIhJ1EL2i5D[k[oIH"
"G]@aL`;]HE\\6b:Yi9aK@4Aom1E4c765V40";
有效位元串流和完整的 C 语言影像解码器程式码可见:
https://gist.github.com/jserv/221efc83f7b60b996dcf9c8b435980d5
编译和测试方式: gcc -O2 amei.c -lm && ./a.out > amei.png
预期可见到天后的脸庞,影像资料取自 [1],故意降低品质,避免侵权。
本方法改写自 [2],解码器分为二层:
* 上层是有损 (lossy) 影像解码:
利用四元树 (quadtree) 分割、预测 (prediction)、DCT 残差 (residual)
* 下层是无损 (lossless) 熵编码 (entropy coding):
利用二元算术编码 (binary arithmetic coding) + 指数哥伦布编码
(exponential-Golomb coding)
资料流向:
位元串流 (bitstream) → 算术解码 → 指数哥伦布解码 → 系数 (coefficient)
重建 → IDCT → 预测补偿 → 去方块滤波 (deblocking filter) → 色彩空间
(color space) 转换 → PNG 输出
算术编码是整个压缩系统的基石,负责将位元串流无损还原为个别的位元和整数。算术
编码将 [0, 1) 区间 (interval) 依据符号的机率 (probability) 递回分割。对二元
算术编码而言,区间分成两段,分别对应 0 和 1,大小正比於各自的预测机率。解码
时,追踪目前的分数 (fraction) 落入哪一段,即可还原对应的位元值。
压缩资料不直接以 RGB 储存,而用 YCgCo 色彩空间:
- Y:亮度 (luma),携带绝大部分影像细节
- Cg:绿-品红色度 (green-magenta chrominance)
- Co:橙-蓝色度 (orange-blue chrominance)
YCgCo 将亮度与色度解相关 (decorrelate),人眼对亮度远比色度敏感,因此编码器可
对色度通道使用较大的量化步长而不明显损失视觉品质。解码器不做色度次取样
(chroma subsampling),而是直接提高色度的量化系数。三个通道以平面格式 (planar
format) 分别储存在记忆体中,最终从 YCgCo 转回交错格式 (interleaved format) 的
RGB 输出。
函式 W() 是解码的主体,以递回四元树方式拆分影像:
static void W(int z, int l, int g)
{
if (g > 5 || (g > 2 && t(g - 3))) {
int c = 1 << --g;
foreach (a, 4)
W(z + a % 2 * c, l + a / 2 * c, g);
return;
}
/* ... 叶节点解码 ... */
}
```
- 区块 (block) 大於 32x32 (即 g > 5): 强制分割
- 区块为 4x4 (即 g == 2`): 不再分割
- 中间层级 (8x8, 16x16, 32x32): 解码一个位元决定是否分割
分割时将区块一分为四,按左上、右上、左下、右下顺序递回处理。产生类似 Z-order
曲线的走访顺序,使空间相邻的区块在编码串流中也相邻,有利於压缩效率。张惠妹脸部
的影像采用四元树分割机制,细节丰富的区域 (如五官) 被切得更细,平坦区域 (如背景)
保持较大区块。
到达四元树的叶节点 (leaf node) 後,解码器对 Y,Cg,Co 三个通道分别处理。首先解码
预测模式 q,该值在 0-33 之间,三个通道共用。
if (!q) {
int v = 0;
foreach (a, c)
v += (l ? o[-f + a] : 0) + (z ? o[a * f - 1] : 0);
*i += z && l ? v / 2 : v;
}
撷取目前区块正上方和正左方已解码画素的平均值,写入 DCT 的 DC 系数。技巧是,
不必逐画素填入预测值,只需修改 DC 系数,因为 DC 系数在 IDCT 後恰好对应整个区块
的常数偏移 (constant offset)。
33 个方向性预测模式对应 HEVC 的 33 个影格内预测 (intra prediction) 方向,每个
模式以 1/8 画素为步进定义一个斜率 (slope),围绕对角线 (模式 17) 对称分布。
int C = q < 17, w = C ? 9 - q : q - 25;
- 模式 1-16 (C=1): 主要从左方画素向右延伸
- 模式 17-33 (C=0): 主要从上方画素向下延伸
- 模式 9: 水平复制左方画素
- 模式 25: 垂直复制上方画素
- 模式 17: 沿对角线方向
对於非整数位置 (sub-pixel position),解码器以 1/8 画素精度在两个相邻参考画素
间进行线性内插 (linear interpolation),产生抗锯齿 (anti-aliasing) 效果:
o[C ? j * f + a : a * f + j] +=
(*i * (8 - J) + i[1] * J + 4) >> 3;
权重 J 介於 0-7,(8-J) 和 J 分别为两个邻近画素的内插权重。
预测只是近似值。要还原实际影像,还需要加上残差 (residual)。残差以离散余弦转换
(Discrete Cosine Transform, DCT) 系数的形式储存。对每个区块的每个色彩通道
(color channel) 而言:
foreach (a, d) i[a] = 0; /* 清零工作缓冲区 */
for (int a = 0; a < d; a++) {
if (t(61 + g * 2 + I)) break; /* 结束旗标 */
a += n(5 + I * 10); /* 零游程长度 */
int k = 1 - 2 * t(3); /* 正负号 */
if (a < d)
i[a] = k * (n(25 + (I + (a < d / 8) * 2) * 10) + 1) * (B ? P : O);
}
流程类似 H.263 的 (LAST, RUN, LEVEL) 三元组 (triplet):
1. 解码一个位元判断是否已到最後一个非零系数
2. 解码零游程 (run of zeros)——连续跳过多少个零系数
3. 解码正负号 (sign bit)
4. 解码量化等级 (quantized level),乘以量化步长(亮度用 O,色度用 P)
还原系数,此为均匀中踏量化 (mid-tread uniform quantization),无死区 (dead
zone)
系数按列主序 (row-major order) 排列,不同於 JPEG 的锯齿形扫描 (zigzag scan)。
非零系数倾向集中於左上角 (低频区域),自然影像的 DCT 系数具有高度稀疏性
(sparsity)。
函式 R() 实作一维 IDCT,并藉由可分离性 (separability) 拆为二次呼叫,以完成
二维 IDCT:
R(i + d, 1, i, 1, c, c, 10); /* 各列做一维 IDCT */
R(o, f, i + d, c, 1, c, 10 + g); /* 各行做一维 IDCT,直接写回影像平面 */
DCT 的本质是将区块分解为一组正弦波基底图样 (basis pattern) 的加权和 (weighted
sum)。左上角的低频 (low-frequency) 基底对应模糊的渐层,右下角的高频
(high-frequency) 基底对应细节边缘。量化 (quantization) 就是在此处引入失真
压缩:除以量化步长并四舍五入,小系数归零,减少需要编码的资料量。
IDCT 是整个解码器中唯一使用浮点运算之处 (cos() 和 lrint()),其余全用定点数
(fixed-point) 或整数算术。余弦值可预先计算为表格查询 (lookup table),如此
便能完全消除浮点运算。
[1]
https://www.mirrormedia.mg/story/20251104edi024
[2] A Tour of the Tiny and Obfuscated Image Decoder
http://eastfarthing.com/blog/
---
你不知不觉看到这里了吗?
我要广告最近开发的专案:
https://github.com/sysprog21/zhtw-mcp
zhtw-mcp 是针对繁体中文场景设计的 MCP 伺服器,目标在於让 AI 在产生繁体中文内容
时,能够符合台湾的语言习惯、资讯科技术语与正式书写规范。本专案不是单纯的繁简
字形转换,而是让 AI 代理人在理解语境後,选择符合台湾语境的词汇与表达方式。在
许多现有工具中,所谓的繁简转换通常只处理字形层级,例如将「计算机」转换为「计算
机」或「电脑」,却忽略词汇与语境的差异,如「程序/程式」、「线程/执行绪」、
「内存/记忆体」、「网络/网路」等。对於技术文件、教材与研究资料而言,这些差异
不只是风格问题,而会影响专业语言的一致性与可读性。zhtw-mcp 的设计理念正是回应
这个问题。系统并非以固定词典逐字替换,而用 AI 代理人的语境理解能力,将整段文本
视为语意单位,再决定最适合的台湾用语与书写方式。
欢迎在 GitHub 打星、测试,和提交意见及程式码。
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 140.116.245.217 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Gossiping/M.1773653862.A.BF3.html
1F:→ snow3804: 说中文好吗 111.71.212.86 03/16 17:38
2F:嘘 yuluiloveyou: 太长了谁他妈看得完 27.52.66.232 03/16 17:41
3F:推 ttucse: 黄教授有在逛八卦板噢? 61.229.65.127 03/16 17:42
4F:推 GTR12534: 有 jserv 先推 60.250.32.102 03/16 17:42
5F:→ jserv: @yuluiloveyou, 这主要给学生看,顺便分享140.116.245.217 03/16 17:43
6F:推 ttucse: 你吃饭了吗?成大附近你都吃什麽? 61.229.65.127 03/16 17:44
7F:推 yoyodiy: 大神 1.165.175.33 03/16 17:45
8F:→ jserv: @ttucse, 我喜欢大学路十八巷,但店家变少140.116.245.217 03/16 17:46
9F:推 whyhsu: id 42.79.142.249 03/16 17:48
10F:推 Mood10207: 太专业了,这里不适合 59.124.89.51 03/16 17:48
11F:→ jserv: @Mood10207, 跪求有缘人一起写程式140.116.245.217 03/16 17:49
12F:推 kingstongyu: 现在不都把解码器做成IC?! 1.168.149.78 03/16 17:49
13F:→ jserv: @kingstongyu, 这也是我授课主题之一140.116.245.217 03/16 17:50
14F:→ kingstongyu: 透过程式写解码器生成图片的速度太慢 1.168.149.78 03/16 17:51
15F:→ yydogyy: 请说中文,谢谢 106.64.136.199 03/16 17:51
16F:→ kingstongyu: 这在竹科或联发科有在做,一般都依 1.168.149.78 03/16 17:52
17F:→ kingstongyu: IEEE协定把类比数位的转换标准协定 1.168.149.78 03/16 17:53
18F:推 ttucse: 教授,如果自学作业系统,读Andrew Tanenb 61.229.65.127 03/16 17:54
19F:→ ttucse: aum写的,minix那本教科书,你觉得如何呢 61.229.65.127 03/16 17:55
20F:→ ttucse: ? 61.229.65.127 03/16 17:55
21F:推 victortang: 老师请问餐做好叫你取餐是不是中断? 118.233.3.24 03/16 17:56
22F:推 kingstongyu: 生成影像或音乐,现在都塞进南桥北桥 1.168.149.78 03/16 17:56
23F:→ kingstongyu: 里了,再更过分一点说不定INTEL会把南 1.168.149.78 03/16 17:57
24F:→ jserv: @ttucse, 时间许可就看 AST 教授大作140.116.245.217 03/16 17:57
25F:→ kingstongyu: 桥北桥都做在X86架构处理器里加焊死 1.168.149.78 03/16 17:58
26F:推 kabukiryu: ..... 42.79.37.131 03/16 17:58
27F:→ kingstongyu: 在主机板上卖 1.168.149.78 03/16 17:58
28F:→ jserv: @kingstongyu, 以前有 Medfield,但是...140.116.245.217 03/16 18:09
29F:推 kingstongyu: J大,贵校现在没研究AI吗?!全台大学 1.168.149.78 03/16 18:12
30F:→ kingstongyu: 连电机系不都朝AI发展发PAPER吗?! 1.168.149.78 03/16 18:13
31F:→ jserv: @kingstongyu, 电机资讯简称 AI140.116.245.217 03/16 18:19
32F:推 seanflower: 图呢 39.14.209.98 03/16 20:01
33F:→ jserv: @seanflower, 执行程式就看得到140.116.245.217 03/16 20:25
34F:推 PeikangShin: 你确定文组的看得懂?223.141.148.249 03/16 20:27
35F:推 ericthree: 有神快拜 1.171.31.124 03/16 21:06
36F:→ jserv: @PeikangShin, 养龙虾後,个个都是天才140.116.245.217 03/16 21:09
37F:推 HowLeeHi: 钓到宅社夫了 1.160.91.11 03/16 21:54
38F:推 crazysix: 这一篇文章值 332 Ptt币 42.77.66.139 03/16 22:12
39F:→ jserv: @crazysix, 我偷懒从作业描述贴上140.116.245.217 03/16 22:51
40F:→ jserv: @HowLeeHi, 我是资深乡民140.116.245.217 03/16 22:51
41F:推 lunenera: 写那麽长有跑出来的结果吗 42.79.27.105 03/17 01:08
42F:推 red0210: 神111.251.218.133 03/17 02:38
43F:推 rdplyr: 推大神 74.102.77.105 03/17 03:26