1. 基本知识
实现图片隐写的基本方法是利用图片的像素数据将信息嵌入其中,同时尽可能保持图片的视觉效果不变
基于 LSB(Least Significant Bit,最低有效位)隐写术:将数据嵌入到图片的像素值的最低有效位
基本原理如下:
- 编码过程:将要嵌入的信息(二进制数据)替换到图片像素值的最低有效位中
对 RGB 图片,每个通道可以分别嵌入数据 - 解码过程:从图片像素的最低有效位中提取信息,并将其还原为原始数据
但是他也有缺点:
- 容量有限:嵌入信息的量受限于图片的分辨率和像素通道数量
- 易受图像处理(如压缩、滤波)的影响
总结的表格如下:
类别 | 描述 |
---|---|
基本定义 | LSB 隐写术是一种通过修改数字文件(如图像、音频或视频)中每个数据单元的最低有效位来嵌入隐写信息的方法 |
适用范围 | 最常用于图像隐写(例如 BMP、PNG 格式),因为这些格式通常无压缩或无损压缩,且对人眼不易察觉嵌入信息的细微变化 |
原理 | 利用数字文件的冗余特性(如图像像素值、音频采样值),将隐写信息存储到文件数据的最低有效位中,改变的幅度足够小以不被人察觉 |
示例分析 | 原始二进制像素值:10101101 嵌入隐写信息后:10101100 此处修改了最低位 1→0,人眼无法察觉像素值的细微变化 |
优点 | 实现简单,效率高 对文件的质量影响较小 可在无损压缩图像中保持高隐写容量 |
缺点 | 容易受到压缩算法(如 JPEG)的破坏 嵌入容量有限(仅与像素数或数据单元数相关) 安全性较低,容易被简单分析或检测工具发现 |
嵌入过程 | 1. 选择载体文件(如图像) 2. 将需要隐藏的信息转换为二进制 3. 替换载体文件数据的最低有效位为隐写信息的二进制数据 |
提取过程 | 1. 读取载体文件的最低有效位数据 2. 还原二进制数据为隐写信息 |
适用场景 | 信息隐藏与传递 数字版权保护 水印嵌入 |
抗攻击性 | 对简单篡改或噪声较敏感 可结合加密技术提高安全性(例如对嵌入信息进行加密后再写入) |
扩展方法 | LSB-M(多位隐写):替换最低的多位数据以增加嵌入容量 结合压缩抗性技术,如差值扩展(DE) |
具体的文字Demo如下:
在图像隐写中,图像由像素组成,每个像素通常由红、绿、蓝(RGB)三种颜色通道的值表示,每个通道用 8 位二进制数表示
例如,一个像素的 RGB 值可能是 (123, 234, 56),其二进制表示为:
R: 01111011
G: 11101010
B: 00111000
LSB 隐写术通过修改每个通道的最低有效位(最后一位)来嵌入隐写信息。对于上述像素,嵌入数据后可能变为:
R: 01111010
G: 11101011
B: 00111001
2. Demo
-
编码部分:
图片数据转换为 NumPy 数组进行操作
嵌入的数据被插入到像素值的最低有效位
添加了 1111111111111110 作为结束标志,用于解码时识别信息终止 -
解码部分:
从图片中提取像素的最低有效位,重组为二进制字符串
根据二进制字符串恢复嵌入的信息
基本的测试Demo如下:
from PIL import Image
import numpy as np
def encode_image(input_image_path, output_image_path, message):
"""
将信息编码到图片中。
:param input_image_path: 输入图片路径
:param output_image_path: 输出图片路径
:param message: 要嵌入的文本信息
"""
# 打开图片并转换为 NumPy 数组
image = Image.open(input_image_path)
image_array = np.array(image)
# 将信息转换为二进制字符串
binary_message = ''.join(format(ord(char), '08b') for char in message)
binary_message += '1111111111111110' # 添加结束标志
# 将消息嵌入图片
flat_pixels = image_array.flatten()
if len(binary_message) > len(flat_pixels):
raise ValueError("信息太长,无法嵌入到图片中。")
for i in range(len(binary_message)):
flat_pixels[i] = (flat_pixels[i] & ~1) | int(binary_message[i])
# 将修改后的像素数组还原为图片
encoded_image = flat_pixels.reshape(image_array.shape)
encoded_image = Image.fromarray(encoded_image.astype(np.uint8))
encoded_image.save(output_image_path)
print(f"信息已嵌入到图片中,保存为 {output_image_path}。")
def decode_image(encoded_image_path):
"""
从图片中解码信息。
:param encoded_image_path: 含有嵌入信息的图片路径
:return: 解码出的文本信息
"""
# 打开图片并转换为 NumPy 数组
image = Image.open(encoded_image_path)
image_array = np.array(image)
# 提取二进制数据
flat_pixels = image_array.flatten()
binary_message = ''.join(str(flat_pixels[i] & 1) for i in range(flat_pixels.size))
# 将二进制数据转换为文本
chars = []
for i in range(0, len(binary_message), 8):
byte = binary_message[i:i+8]
if byte == '11111111': # 检测结束标志
break
chars.append(chr(int(byte, 2)))
return ''.join(chars)
# 测试
if __name__ == '__main__':
input_image = 'dog.jpg' # 替换为实际图片路径
output_image = 'output.png'
secret_message = "Hello, Steganography!"
# 编码
encode_image(input_image, output_image, secret_message)
# 解码
decoded_message = decode_image(output_image)
print("解码出的信息:", decoded_message)
截图如下: