Challenge
I have drawn a picture of a caterpillar and hid some text in it. I am
sure you can figure it out, as it is not too hard :)
Solution
下载 caterpillar.png(220x55, RGBA)
标准 stegano 分析(zsteg, bit plane 扫描, LSB/MSB 全通道)无结果。PNG
只有 IHDR/sRGB/bKGD/pHYs/IDAT/IEND 六个 chunk,无附加数据。
数据不在 RGB 裸值里,而是藏在其他色彩空间的 Hue
值中。
RGB 是加色混色——R, G, B 各 0-255。毛虫的主色是黄绿,以
RGB(135,194,41) 为例:
- R=135:不在可打印 ASCII 范围(32-126)外,chr(135) 是扩展字符
- G=194:同上,超出范围
- B=41:chr(41) =
),无意义
直接用 RGB 值当 ASCII 只能得到一堆乱码。
色彩空间可以互相转换
RGB 和 HSV
描述的是同一个颜色,只是坐标系不同——就像经纬度
(116.4°E, 39.9°N) 和北京市地址是同一地点。
1 2 3 4 5 6 7 8 9 10 11
| import colorsys
r, g, b = 135, 194, 41
h, s, v = colorsys.rgb_to_hsv(r/255, g/255, b/255)
r2, g2, b2 = colorsys.hsv_to_rgb(h, s, v)
|
转换公式是解析的,无精度损失。
解法
毛虫的 8 个主 body segment,每个用了一种特定的颜色。RGB→HSV
后,H(色相角度)落在可打印 ASCII 范围内:
1 2 3 4 5 6 7 8 9 10
| segment 1 (x= 0- 22) #e1f0c9 H=83° -> 'S' segment 2 (x= 23- 46) #e8f4d7 H=85° -> 'U' 腿阴影 (x= 47- 69) #6e7d4d H=79° -> 'O' 腿阴影 (x= 70- 92) #465512 H=73° -> 'I' segment 3 (x= 93-115) #a9bb27 H=67° -> 'C' segment 4 (x=116-140) #557315 H=79° -> 'O' segment 5 (x=141-165) #7dad30 H=83° -> 'S' segment 6 (x=166-187) #eaf4d0 H=77° -> 'M' 头部 (x=188-219) #e06478 H=350° -> 不可打印 (结束标记) ...
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| from PIL import Image import colorsys from collections import Counter
img = Image.open('caterpillar.png').convert('RGBA') w, h = img.size
x_hues = {} for x in range(w): hues = [] for y in range(h): r, g, b, a = img.getpixel((x, y)) if (r, g, b) == (255, 255, 255): continue h_val, _, _ = colorsys.rgb_to_hsv(r/255, g/255, b/255) hues.append(round(h_val * 360)) if hues: x_hues[x] = Counter(hues).most_common(1)[0][0]
result = [] run_h, run_start, run_len = None, None, 0 for x in range(w): h = x_hues.get(x) if h == run_h: run_len += 1 else: if run_len >= 10 and 32 <= run_h <= 126: result.append(chr(run_h)) run_h, run_start, run_len = h, x, 1
if run_len >= 10 and 32 <= run_h <= 126: result.append(chr(run_h))
decoded = ''.join(result) print(f'Decoded Hue sequence: {decoded}') print(f'Answer (last word): {decoded.split()[-1]}')
|
COLOR-SHEMES