IOS基于LSB图片水印方案
该方案在Xcode构建过程中对App Bundle内的PNG图片资源进行最低有效位LSB嵌入将水印信息隐藏于像素数据中不影响视觉效果且可追踪版权当然其他的作用自己进行体会。一、方案核心水印算法LSB最低有效位隐写术将水印二进制串嵌入图片每个像素RGB分量的最低位。人眼无法察觉满足“隐式增加”。处理时机在Copy Bundle Resources阶段之后对已拷贝到应用包中的图片进行原地修改不污染源文件。开关控制通过Xcode Build Settings中的自定义变量ENABLE_INVISIBLE_WATERMARK控制是否执行默认关闭。适用格式PNG无损压缩LSB稳定可扩展支持BMP。不处理JPEG有损压缩会破坏水印。二、实现代码2.1 Python脚本invisible_watermark.py将此脚本放入项目根目录的Scripts文件夹中。#!/usr/bin/env python3# -*- coding: utf-8 -*-importosimportsysimportstructfromPILimportImage# 配置 # 水印内容建议使用项目标识构建时间保证唯一性WATERMARK_TEXTCOPYRIGHT_YOURAPP_2026# 水印起始标记用于定位避免误读START_MARKER0xAA# 10101010END_MARKER0x55# 01010101# deftext_to_bits(text):将字符串转为二进制位列表含起始/结束标记# 添加起始标记8位bits[int(b)forbinformat(START_MARKER,08b)]# 添加文本长度32位大端text_bytestext.encode(utf-8)length_bits[]forbyteinstruct.pack(I,len(text_bytes)):length_bits.extend([int(b)forbinformat(byte,08b)])bits.extend(length_bits)# 添加文本内容forbyteintext_bytes:bits.extend([int(b)forbinformat(byte,08b)])# 添加结束标记8位bits.extend([int(b)forbinformat(END_MARKER,08b)])returnbitsdefembed_lsb(image_path,bits):将二进制位嵌入图片的RGB最低有效位返回是否成功imgImage.open(image_path).convert(RGB)pixelsimg.load()width,heightimg.size total_bitswidth*height*3# 每个像素3个通道iflen(bits)total_bits:print(f 错误: 图片容量不足需要{len(bits)}位实际{total_bits}位)returnFalseidx0foryinrange(height):forxinrange(width):r,g,bpixels[x,y]ifidxlen(bits):r(r0xFE)|bits[idx]idx1ifidxlen(bits):g(g0xFE)|bits[idx]idx1ifidxlen(bits):b(b0xFE)|bits[idx]idx1pixels[x,y](r,g,b)ifidxlen(bits):breakifidxlen(bits):breakimg.save(image_path,formatPNG,optimizeFalse)returnTruedefprocess_bundle(bundle_path):遍历App Bundle中的PNG图片嵌入水印processed0forroot,dirs,filesinos.walk(bundle_path):forfileinfiles:iffile.lower().endswith(.png):file_pathos.path.join(root,file)try:bitstext_to_bits(WATERMARK_TEXT)ifembed_lsb(file_path,bits):print(f ✓ 已处理:{os.path.relpath(file_path,bundle_path)})processed1else:print(f ✗ 容量不足跳过:{file_path})exceptExceptionase:print(f ✗ 处理失败{file_path}:{e})returnprocesseddefmain():iflen(sys.argv)2:print(用法: python3 invisible_watermark.py App Bundle路径)sys.exit(1)bundle_pathsys.argv[1]ifnotos.path.isdir(bundle_path):print(f错误: 路径不存在或不是目录:{bundle_path})sys.exit(1)print(f开始处理Bundle:{bundle_path})print(f水印内容:{WATERMARK_TEXT})countprocess_bundle(bundle_path)print(f完成共处理{count}个PNG图片)if__name____main__:main()2.2 Xcode Run Script 集成在Xcode项目Target的Build Phases中添加一个新的Run Script Phase放在Copy Bundle Resources之后确保资源已拷贝到App Bundle。脚本内容# 不可见水印开关仅在配置了ENABLE_INVISIBLE_WATERMARK且值为YES时执行if[${ENABLE_INVISIBLE_WATERMARK}!YES];thenecho 不可见水印已禁用 (ENABLE_INVISIBLE_WATERMARK ! YES)exit0fi# 检查Python3环境if!command-vpython3/dev/null;thenecho⚠️ 未找到python3跳过水印处理exit0fi# 检查Pillow库若未安装则自动安装可选python3-cimport PIL2/dev/nullif[$?-ne0];thenecho 安装Pillow库...pip3installPillow--user--quietfi# 获取App Bundle路径针对模拟器和真机统一处理APP_PATH${TARGET_BUILD_DIR}/${EXECUTABLE_FOLDER_PATH}if[!-d$APP_PATH];thenecho❌ 未找到App Bundle:$APP_PATHexit1fi# 脚本所在路径假设放在项目根目录/Scripts/下SCRIPT_PATH${SRCROOT}/Scripts/invisible_watermark.pyif[!-f$SCRIPT_PATH];thenecho❌ 未找到水印脚本:$SCRIPT_PATHexit1fiecho 开始嵌入不可见水印...python3$SCRIPT_PATH$APP_PATHecho✅ 水印嵌入完成2.3 开关配置方式在Xcode项目的Build Settings中添加User-Defined Setting选择Target → Build Settings点击→Add User-Defined Setting设置Key为ENABLE_INVISIBLE_WATERMARK设置Value为YES启用或NO禁用建议不同配置使用不同值DebugNO加快构建ReleaseYES正式包加水印通过Build Configuration下的ENABLE_INVISIBLE_WATERMARK分别设置即可。三、验证水印存在性可选用于追踪如果需要从图片中提取水印以验证版权可提供以下提取脚本单独使用不在构建时执行#!/usr/bin/env python3importsysfromPILimportImagedefextract_lsb(image_path):imgImage.open(image_path).convert(RGB)pixelsimg.load()width,heightimg.size bits[]foryinrange(height):forxinrange(width):r,g,bpixels[x,y]bits.append(r1)bits.append(g1)bits.append(b1)# 查找起始标记start_marker_bits[int(b)forbinformat(0xAA,08b)]foriinrange(len(bits)-8):ifbits[i:i8]start_marker_bits:# 读取长度length_bitsbits[i8:i832]length0forj,bitinenumerate(length_bits):length|(bit(31-j))# 读取文本text_bitsbits[i832:i832length*8]text_bytesbytearray()forjinrange(0,len(text_bits),8):byte0forkinrange(8):byte|(text_bits[jk](7-k))text_bytes.append(byte)# 验证结束标记end_posi832length*8end_marker_bits[int(b)forbinformat(0x55,08b)]ifbits[end_pos:end_pos8]end_marker_bits:print(f提取水印:{text_bytes.decode(utf-8)})returnprint(未检测到水印)if__name____main__:iflen(sys.argv)!2:print(用法: python3 extract_watermark.py image.png)else:extract_lsb(sys.argv[1])四、方案优势特性说明无感性LSB水印肉眼完全不可见不改变图片观感源文件安全只修改构建产物DerivedData中的App Bundle不触碰.xcassets原始图片开关可控通过Build Settings一键启用/禁用不同配置灵活切换自动化集成到Xcode构建流程无需手动操作可追溯支持从图片中提取水印用于版权验证轻量仅依赖Python3 PillowmacOS自带环境自动安装缺失库五、注意事项仅支持PNGJPEG等有损压缩会破坏LSB水印脚本会自动跳过非PNG文件。性能影响嵌入水印会增加构建时间取决于图片数量和大小建议仅在Release模式下启用。图片容量水印文本长度标记位约需(832文本长度*88)位。一张512x512的PNG可容纳约786432位约96KB文本完全满足需求。App Store合规该水印不影响App功能也未注入额外代码符合App Store审核标准。六、集成步骤将invisible_watermark.py放入项目Scripts/文件夹。在Xcode Build Phases中添加Run Script粘贴上述脚本内容。在Build Settings中添加User-Defined SettingENABLE_INVISIBLE_WATERMARKRelease设为YES。正常构建水印自动嵌入。此方案已用于多个生产项目稳定可靠。