Python 实现图片隐写术(LSB + 四节点并行)
Python 实现图片隐写术(LSB + 四节点并行)本文配套实验:Pillow 模块 / 最低有效位(LSB) / lambda 表达式递归 / UTF-8 编码,并在华为云 FlexusX 4 节点上完成分布式并行隐写实战。完整源码与运行产物见文末仓库地址(Gitee)。一、什么是图片隐写术隐写术(Steganography)是把一段秘密信息“藏”进正常载体(图片、音频、视频)中,使第三方从外观上无法察觉信息存在的技术——它与加密不同:加密是“让内容看不懂”,隐写是“让内容看起来不存在”。本文用最经典的LSB(Least Significant Bit,最低有效位)算法:把秘密信息的每一个比特,写进图片像素 RGB 通道的最低那一位。由于最低位对颜色影响极小(例如(123,45,67)改成(122,45,67)肉眼根本分辨不出),载体图片看起来“毫无变化”,但信息已经藏进去了。二、本次实验的知识点知识点在实验中的作用1. Pillow 模块生成/读写图片,做像素级getdata()/putdata()操作2. 最低有效位 (LSB)把秘密比特藏入像素 RGB 通道最低位:v 0xFE | bit3. lambda 表达式递归用“自应用递归 lambda(Y 组合子风格)”完成bytes ↔ bits与逐比特嵌入/提取4. UTF-8 编码中文/英文/emoji 先encode("utf-8")成字节流,再按比特嵌入三、LSB 隐写原理(图文)一张W×H的 RGB 图片,共有W×H×3个 0~255 的颜色分量。每个分量用 8 bit 表示,最低位(第 0 位)对颜色贡献只有1,改掉它肉眼几乎无感。嵌入一个比特b到颜色分量v:v' = (v 0xFE) | bv 0xFE:把最低位清零(保留高 7 位)| b:写入秘密比特b(0 或 1)提取时只需v' 1即可拿回该比特。容量估算:本实验用200×200封面,容量 =200×200×3 = 120000 bit ≈ 14.6 KB文本,足以藏下一篇短文。为了知道“从哪一位开始、读到哪一位结束”,我们在数据前加一个32 位消息长度头:先写消息比特总数,提取端先读 32 位得到长度,再精确读后续比特。四、UTF-8:多字节字符的正确打开方式秘密文本往往是中文或 emoji,例如华为云FlexusX四节点并行隐写术✅2026DataHelper。这些字符在内存里不是按“字”而是按字节存储的:ASCII 字符(如F、2、D)→ 1 字节常用汉字(如华、云)→ 3 字节(UTF-8)emoji(如✅)→ 4 字节所以绝不能“一个字当一个字节”处理,否则中文会乱码。正确做法:data=fragment.encode("utf-8")# str - bytes(UTF-8 多字节)# ...嵌入比特...recovered=to_bytes(msg_bits,0).decode("utf-8")# bytes - str各节点真实编码数据(来自本次运行summary.json):节点片段字符数UTF-8 字节数消息比特0001华为云Flexus9151200004X四节点并行隐写8221760002术✅2026Da812960003taHelper8864可以看到:同样 8 个字符,X四节点并行隐写是 22 字节(含 6 个汉字 ×3 + 2 个 ASCII),而taHelper只有 8 字节(全 ASCII)——这正是 UTF-8 变长编码的体现。五、lambda 表达式递归:不用def也能递归很多人以为 Python 的lambda不能递归(因为没有名字)。其实用自应用(Y 组合子风格)即可:把函数自身f作为参数传给自己,再在自己内部调用f(f)。1) bytes → bits(逐字节拆 8 比特)to_bits=(lambdaf:f(f))(lambdaf:lambdabs,i:[]ifi=len(bs)else[(bs[i]k)1forkin(7,6,5,4,3,2,1,0)]+f(f)(bs,i+1))msg_bits=to_bits(list(data),0)# 字节流 - 0/1 比特列表终止条件:i = len(bs)返回空列表递归步:取第i字节,按高位到低位7→0取出 8 个比特,拼到后续结果前2) LSB 嵌入(逐比特改写最低位,原地修改)embed=(lambdaf:f(f))(lambdaf:lambdaarr,bits,p,b:(arr.__setitem__(p,(arr[p]0xFE)|bits[b]),f(f)(arr,bits,p+1,b+1))[-1]ifblen(bits)elseNone)embed(flat,all_bits,0,0)# flat: 一维像素分量列表用arr.__setitem__(p, (arr[p] 0xFE) | bits[b])原地改最低位元组(set, recurse)[-1]的写法只是为了把“副作用”塞进 lambda 的单一表达式里3) 提取 + bits → bytesextract=(lambdaf:f(f))(lambdaf:lambdaarr,cnt,p:[]ifp=cntelse[arr[p]1]+f(f