采用隐写术嵌入QR Code Payload:字节转存XOR操作异常求助
问题:QR Code水印嵌入Payload时的字节转存异常
我尝试用某论文所述方法,以水印形式给QR Code嵌入特殊Payload,流程逻辑看似没问题,但在把Payload保存为可与QR Code做XOR运算的字节数据时出了问题。
我的Python代码
import qrcode from PIL import Image, ImageChops qr = qrcode.QRCode( version=5, error_correction=qrcode.constants.ERROR_CORRECT_H, box_size=10, border=2, ) msg = "25.61795.?000001?.907363.02" sct = "secret message test" def save_qrcode(data, path): qr.add_data(data) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") img.save(path) save_qrcode(msg, "out.png") save_qrcode(sct, "out2.png") pure_qr_code = Image.open("out.png") encoded_data_as_img = Image.new(mode=pure_qr_code.mode, size=pure_qr_code.size) encoded_data_pre_xor = [ord(e) for e in sct] print(encoded_data_pre_xor) # Encoding encoded_data_as_img.putdata(encoded_data_pre_xor) encoded_data_as_img.save("out2.png") encoded_data_as_img = Image.open("out2.png") result = ImageChops.logical_xor(pure_qr_code, encoded_data_as_img) result.save("result.png") # Decoding result = Image.open("result.png") result2 = ImageChops.logical_xor(result, pure_qr_code) result2.save("result2.png") img_data_as_bytes = Image.open("result2.png").getdata() encoded_data_after_xor = [] i = 0 while img_data_as_bytes[i]: encoded_data_after_xor.append(img_data_as_bytes[i]) i += 1 print(encoded_data_after_xor)
代码运行输出
[115, 101, 99, 114, 101, 116, 32, 109, 101, 115, 115, 97, 103, 101, 32, 116, 101, 115, 116] [255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255]
当前现象与问题定位
对比result2.png和out2.png未发现差异,说明问题出在encoded_data_as_img.putdata(encoded_data_pre_xor)这一步的字节转存环节。
问题根源与修复方案
核心问题
- 数据长度不匹配:Payload的字节数远小于QR图像素总数,
putdata会自动用0填充剩余像素,导致后续XOR运算完全偏离预期。 - 图像格式转换干扰:将Payload图像保存为PNG再读取时,ASCII值会被转换为图像像素值(白色为
255、黑色为0),破坏原始字节数据。 - 解码逻辑错误:用
while img_data_as_bytes[i]读取数据时,遇到像素值0会提前终止,无法完整提取Payload。
修复后的代码
import qrcode from PIL import Image, ImageChops # 每次生成QR都创建新对象,避免数据累积 def create_qrcode(data): qr = qrcode.QRCode( version=5, error_correction=qrcode.constants.ERROR_CORRECT_H, box_size=10, border=2, ) qr.add_data(data) qr.make(fit=True) return qr.make_image(fill_color="black", back_color="white") msg = "25.61795.?000001?.907363.02" sct = "secret message test" # 生成原始QR图 pure_qr_code = create_qrcode(msg) pure_qr_code.save("out.png") # 扩展Payload到与QR图像素总数一致 pixel_count = pure_qr_code.size[0] * pure_qr_code.size[1] encoded_data_pre_xor = [] while len(encoded_data_pre_xor) < pixel_count: encoded_data_pre_xor.extend([ord(c) for c in sct]) encoded_data_pre_xor = encoded_data_pre_xor[:pixel_count] # 内存中直接创建Payload图像,跳过保存再读取的步骤 encoded_data_as_img = Image.new(mode=pure_qr_code.mode, size=pure_qr_code.size) encoded_data_as_img.putdata(encoded_data_pre_xor) # 编码:执行XOR运算 result = ImageChops.logical_xor(pure_qr_code, encoded_data_as_img) result.save("result.png") # 解码:反向XOR还原Payload result_img = Image.open("result.png") decoded_img = ImageChops.logical_xor(result_img, pure_qr_code) decoded_img.save("result2.png") # 提取并还原原始Payload img_data = decoded_img.getdata() encoded_data_after_xor = img_data[:len(sct)] decoded_sct = ''.join([chr(val) for val in encoded_data_after_xor]) print("原始Payload:", sct) print("解码后Payload:", decoded_sct)
关键修改点
- 每次生成QR都实例化新的
QRCode对象,避免旧数据残留 - 将Payload扩展到与QR图像素总数一致,避免自动填充
0 - 直接在内存中处理Payload图像,跳过PNG格式转换环节
- 解码时直接提取对应长度的数据,避免提前终止读取
内容的提问来源于stack exchange,提问作者Victor
相关产品推荐
相关产品推荐

