用Python玩点花的:手把手教你用PIL库把秘密藏进图片(附完整代码)
用Python玩点花的:手把手教你用PIL库把秘密藏进图片(附完整代码)
你是否想过在朋友圈分享的照片里藏点小秘密?或者给朋友发送一张看似普通的图片,实则暗藏玄机?今天我们就用Python的PIL库,来实现这个有趣的功能——将文本、图片甚至压缩包隐藏在普通图片中,而人眼几乎无法察觉差异。
1. 准备工作:理解LSB隐写原理
LSB(Least Significant Bit,最低有效位)隐写是一种简单却巧妙的信息隐藏技术。它的核心思想是利用人类视觉的局限性——我们对颜色细微变化的敏感度有限。
每个像素的RGB值由三个8位二进制数表示(0-255)。修改这些二进制数的最低位(即第8位),对颜色的实际影响微乎其微。例如:
| 原始颜色值 | 二进制表示 | 修改最低位后 |
|---|---|---|
| R:201 | 11001001 | 11001000 (200) |
| G:123 | 01111011 | 01111010 (122) |
| B:56 | 00111000 | 00111001 (57) |
表:LSB修改示例 - 人眼几乎无法察觉这种细微的颜色变化
通过这种方式,每个像素的RGB三个通道可以携带3位隐藏信息。对于一张800×600的图片,理论上可以隐藏:
800 × 600 × 3 ÷ 8 ≈ 180KB
的隐藏数据(不考虑图片格式压缩等因素)。
2. 环境搭建与PIL库安装
首先确保你已安装Python(3.6+版本推荐),然后通过pip安装Pillow库(PIL的现代分支):
pip install pillow
验证安装是否成功:
from PIL import Image
print(Image.__version__) # 应该输出类似'9.5.0'的版本号
提示:如果在安装过程中遇到权限问题,可以尝试添加
--user参数,或者使用虚拟环境。
3. 基础实现:文本隐写
让我们从最简单的文本隐藏开始。创建一个 lsb_steganography.py 文件,添加以下代码:
from PIL import Image
import binascii
def text_to_bits(text):
"""将文本转换为二进制字符串"""
byte_str = text.encode('utf-8')
hex_str = binascii.hexlify(byte_str).decode('utf-8')
return bin(int(hex_str, 16))[2:].zfill(8 * ((len(hex_str) + 1) // 2))
def encode_lsb(image_path, text, output_path):
img = Image.open(image_path)
width, height = img.size
binary_text = text_to_bits(text) + '1111111111111110' # 添加结束标记
if len(binary_text) > width * height * 3:
raise ValueError("文本过长,无法隐藏在给定图片中")
data_index = 0
for y in range(height):
for x in range(width):
pixel = list(img.getpixel((x, y)))
for color_channel in range(3): # R,G,B三个通道
if data_index < len(binary_text):
# 清除最低位后加上我们的数据位
pixel[color_channel] = pixel[color_channel] & ~1 | int(binary_text[data_index])
data_index += 1
img.putpixel((x, y), tuple(pixel))
if data_index >= len(binary_text):
img.save(output_path)
return True
img.save(output_path)
return True
使用示例:
# 隐藏文本
encode_lsb('original.jpg', '这是我的秘密信息', 'secret_image.png')
# 从命令行运行
# python lsb_steganography.py encode original.jpg "秘密信息" secret.png
4. 进阶功能:隐藏图片和文件
现在让我们扩展功能,使其能够隐藏其他图片和任意文件。关键是将文件读取为二进制数据:
def file_to_bits(file_path):
with open(file_path, 'rb') as f:
byte_data = f.read()
return ''.join(format(byte, '08b') for byte in byte_data)
def encode_file(image_path, file_path, output_path):
img = Image.open(image_path)
file_bits = file_to_bits(file_path)
# 添加文件头标识和长度信息
header = f"{len(file_bits):032b}"
full_data = header + file_bits
if len(full_data) > img.width * img.height * 3:
raise ValueError("文件太大,无法隐藏在给定图片中")
# 剩余实现与文本隐藏类似...
提取功能的实现:
def decode_lsb(image_path, output_path=None):
img = Image.open(image_path)
width, height = img.size
binary_data = []
for y in range(height):
for x in range(width):
pixel = img.getpixel((x, y))
for channel in pixel[:3]: # 只处理RGB,忽略alpha通道
binary_data.append(str(channel & 1))
binary_str = ''.join(binary_data)
# 尝试检测文件类型
if binary_str[:32]: # 检查是否有我们添加的长度头
data_length = int(binary_str[:32], 2)
file_data = binary_str[32:32+data_length]
# 将二进制数据转换回字节
byte_data = bytes(int(file_data[i:i+8], 2) for i in range(0, len(file_data), 8))
if output_path:
with open(output_path, 'wb') as f:
f.write(byte_data)
return f"文件已保存到 {output_path}"
else:
try:
return byte_data.decode('utf-8')
except UnicodeDecodeError:
return "提取的数据不是文本,请指定输出文件路径保存为文件"
# 如果没有长度头,尝试作为纯文本解码
try:
# 查找可能的结束标记
end_marker_index = binary_str.find('1111111111111110')
if end_marker_index != -1:
text_bits = binary_str[:end_marker_index]
text = ''.join(chr(int(text_bits[i:i+8], 2)) for i in range(0, len(text_bits), 8))
return text
except:
pass
return "未能识别隐藏的数据类型"
5. 优化与增强功能
基础功能实现后,我们可以添加一些实用功能来提升用户体验:
1. 添加进度条
使用 tqdm 库显示处理进度:
from tqdm import tqdm
def encode_lsb_with_progress(image_path, data, output_path):
# ...初始化代码...
with tqdm(total=len(binary_data), desc="隐藏数据") as pbar:
for y in range(height):
for x in range(width):
# ...处理像素...
pbar.update(3) # 每个像素处理3个通道
# ...保存图片...
2. 错误处理与验证
添加输入验证和错误处理:
def validate_image_capacity(image_path, data_size):
img = Image.open(image_path)
max_bits = img.width * img.height * 3
required_bits = data_size * 8 + 32 # 加上32位的长度头
if required_bits > max_bits:
available_kb = max_bits // (8 * 1024)
raise ValueError(
f"图片容量不足。需要: {required_bits//(8*1024)+1}KB, "
f"可用: {available_kb}KB\n"
f"建议使用更大尺寸或更高分辨率的图片"
)
3. 命令行界面
使用 argparse 创建友好的命令行界面:
import argparse
def main():
parser = argparse.ArgumentParser(description="LSB隐写工具")
subparsers = parser.add_subparsers(dest='command', required=True)
# 编码命令
encode_parser = subparsers.add_parser('encode', help='隐藏数据到图片')
encode_parser.add_argument('image', help='原始图片路径')
encode_parser.add_argument('data', help='要隐藏的文本或文件路径')
encode_parser.add_argument('output', help='输出图片路径')
# 解码命令
decode_parser = subparsers.add_parser('decode', help='从图片提取数据')
decode_parser.add_argument('image', help='包含隐藏数据的图片路径')
decode_parser.add_argument('--output', help='输出文件路径(可选)')
args = parser.parse_args()
if args.command == 'encode':
try:
# 尝试作为文件处理
with open(args.data, 'rb'):
encode_file(args.image, args.data, args.output)
print(f"文件成功隐藏在 {args.output} 中")
except (IOError, OSError):
# 作为文本处理
encode_lsb(args.image, args.data, args.output)
print(f"文本成功隐藏在 {args.output} 中")
elif args.command == 'decode':
result = decode_lsb(args.image, args.output)
print("提取结果:", result)
if __name__ == '__main__':
main()
6. 实际应用与创意玩法
掌握了这项技术后,你可以尝试以下有趣的应用场景:
- 秘密通信 :与朋友约定使用特定图片传递秘密信息
- 数字水印 :在图片中嵌入版权信息或签名
- 趣味游戏 :设计寻宝游戏,将线索隐藏在图片中
- 备份重要信息 :将加密的密码或密钥隐藏在不起眼的图片中
创意扩展思路 :
- 加密增强安全性 :在隐藏前对数据进行加密
- 分布式存储 :将大文件分割隐藏在多张图片中
- 容错机制 :添加纠错码,使隐藏的数据更能抵抗图片修改
- 自动化工具 :开发浏览器插件或手机应用,方便日常使用
注意:请遵守法律法规,仅将此类技术用于合法用途。未经允许在他人图片中隐藏信息可能涉及法律问题。
更多推荐
所有评论(0)