如何将文本嵌入图片,并完美解密

在视频中隐藏秘密:如何将文本嵌入图片,并完美解密


在当今的信息时代,安全传递信息变得尤为重要。如何在不被察觉的情况下传递一段文本,成为了谍战故事中的重要一环。而将文本隐藏在图片中,则是一种巧妙的方式。

本文将介绍如何将文本转换为图片,并在图片中嵌入文本信息,最终通过专业工具实现文本的加密与解密。


1. 从文本到图片的初步实现

这里我们先做将文本转成图片, 然后从图片上看不出来文本的汉字, 但是专业工具能解密出来. 也即我们实现一个 encode 函数 和一个 decode 函数。

这一次尝试, 我们只保证转出来是个图片.

1.1 转换文本为二进制字节流

在这一步中,假设我们的文本是"有内鬼终止交易"重复一万次, 首先我们将该字符串转为二进制bytes.:

# 导入必要的包
from PIL import Image
import numpy as np
import struct
import math

# 文本转二进制字节流
text = '有内鬼终止交易.'*1000
text_bytes = text.encode('utf-8')
print(text_bytes[:100]) # 打印前200位数据

在这里插入图片描述

1.2 将字节流转换为图片

我们可以利用二进制流不区分类型的特点,将其转换为任意导出为二进制的变量。为了避免深入研究图片协议的头部信息,我们使用numpy作为中转工具。每张图片都可以表示为一个 长度 × 宽度 × 3(RGB)的矩阵,矩阵元素的取值范围是0-255,分别对应红、绿、蓝三色的深度。由于处理二进制流不便,大多数情况下按8位处理,而8位二进制可以表示0-255, 因此,text_bytes的长度可以与图片中像素点数量 ×3 对应上。

只需要2步骤就可以完成二进制流转图片:

  • 生成一个能装的下文本二进制流的图片, 需要图片中像素点*3 大于文本字节数。
  • 将文本字节流和字节流长度信息放到图片numpy字节流中, 保存成图片。

这里需要放 字节流长度是因为图片容器字节数大于内容需要的字节数。因此,我们需要在text_bytes前添加一个表示其长度的二进制数据。这样在操作时 a) 读取图片时,先读取前4个字节获取内容长度。b) 根据长度读取对应大小的字节,获取原始文本的二进制流。 在很多涉及到字节流的地方, 都需要这样处理。

主要分为 1. 计算图片边长 2. 将字节流伪装成图片数据塞入图片。

# 计算最小图片正方形边长
text_bytes_contain_lens = struct.pack('>l',len(text_bytes)) + text_bytes
img_width = math.ceil((len(text_bytes_contain_lens)/3)**(1/2)) 
print(f'正方形图片的边长是 {img_width}')

# 生成并保存图片
img_path = "text.png"
picMap = np.random.randint(0,255,(img_width, img_width,3),dtype=np.uint8).tobytes()
picMap = text_bytes_contain_lens + picMap[len(text_bytes_contain_lens):]
picMapNpy = np.frombuffer(picMap,dtype=np.uint8).reshape((img_width,img_width,3))
Image.fromarray(picMapNpy).save(img_path)

在这里插入图片描述

生成的图片看起来虽然像是损坏的图片,但它确实有效。接下来,我们将演示如何从图片中提取出隐藏的文本。

1.3 从图片中解密文本

一旦我们将文本嵌入到图片中,接下来我们需要一个解密的过程,从图片中提取出隐藏的文本信息。这里主要的代码为之前代码反向的过程, 以下代码展示了如何解密并恢复原始文本:

# 从图片中恢复文本
img_path = "text.png"
pic = Image.open(img_path).convert("RGB")
picBytes = np.array(pic).tobytes()
lenData = struct.unpack('>l',picBytes[:4])[0]
byteData = picBytes[4:4+lenData]
text = byteData.decode('utf-8')
print(text[:100])

在这里插入图片描述

1.4 我们将之前的代码稍加封装即可得到两个函数

def saveText2Img(text, img_path):
    text_bytes = text.encode('utf-8')
    text_bytes_contain_lens = struct.pack('>l',len(text_bytes)) + text_bytes # struct.pack('>l',...) 是获取该变量二进制流
    img_width = math.ceil((len(text_bytes_contain_lens)/3)**(1/2)) 

    picMap = np.random.randint(0,255,(img_width, img_width,3),dtype=np.uint8).tobytes()
    picMap = text_bytes_contain_lens + picMap[len(text_bytes_contain_lens):] # picMap前面的内容替换为text_bytes
    picMapNpy = np.frombuffer(picMap,dtype=np.uint8).reshape((img_width,img_width,3)) # 基于该字节流拿到rgb矩阵
    Image.fromarray(picMapNpy).save(img_path)

def loadTextFromImg(img_path):
    pic = Image.open(img_path).convert("RGB")
    picBytes = np.array(pic).tobytes()
    lenData = struct.unpack('>l',picBytes[:4])[0] # 读取前4个字节, 为数据字节流的长度.
    byteData = picBytes[4:4+lenData]
    text = byteData.decode('utf-8')
    return text
saveText2Img('有内鬼终止交易.'*10,'version1.png')
loadTextFromImg('version1.png')

2. 生成更“好看”的图片

我们发现生成的图片, 完全无意义, 明显就感觉是一个“坏图片”, 那我们能不能在看起来正常的图片里面加入我们的数据呢?

刚刚生成的图片很难看是因为我们将数据直接转成了图片, 本身图片不含内容。那更好的做法是我们将图片实际值和预期值的差作为信息,来存储数据。 比如我们使用纯色图片作为基础, 在纯色基础上稍加扰动来存储信息。这样从外部看还是一个普通的图片, 但是实际上已经存储了我们希望的文本。 这里为了简单, 我们将一个 byte 也就是 8个bit 拆成 8个byte. 具体可以参考下面的代码.

2.1 实现信息加密与解密

我们首先定义了几个辅助函数,用来实现字节与比特的相互转换,以及进行异或加密:

def byte_to_bits(byte_data):
    bit_bytes = []
    for byte in byte_data:
        bits = f'{byte:08b}'
        for bit in bits:
            bit_bytes.append(int(bit).to_bytes(1, byteorder='big'))
    return b''.join(bit_bytes)

def bits_to_byte(bit_bytes):
    if len(bit_bytes) % 8 != 0:
        raise ValueError("输入字节流的长度必须是8的倍数。")
    byte_list = []
    for i in range(0, len(bit_bytes), 8):
        bit_chunk = bit_bytes[i:i+8]
        bit_str = ''.join(str(int(b)) for b in bit_chunk)
        byte = int(bit_str, 2)
        byte_list.append(byte)
    return bytes(byte_list)
    
def xor_bytes(bytes1, bytes2):
    if len(bytes1) != len(bytes2):
        raise ValueError("两个字节数组的长度必须相同。")
    result = bytes(a ^ b for a, b in zip(bytes1, bytes2))
    return result

通过下面测试发现, 可以通过差异信息来存储信息。
在这里插入图片描述

2.2 将文本嵌入“好看”的图片中

接下来,我们基于上面的辅助函数,对之前的代码进行修改,实现一个看起来正常的图片中嵌入文本信息的过程:

def saveText2Img(text, img_path, baseColor=(70, 130, 180)):
    text_bytes = text.encode('utf-8')
    text_bytes_contain_lens = struct.pack('>l',len(text_bytes)) + text_bytes # struct.pack('>l',...) 是获取该变量二进制流
    img_width = math.ceil((len(text_bytes_contain_lens)/3*8)**(1/2)) 

    baseImg = Image.new("RGB", (img_width, img_width), baseColor)
    baseImgBytes = np.array(baseImg).tobytes()
    sparsetextBytes = byte_to_bits(text_bytes_contain_lens)
    baseImgWithInfoBytes = xor_bytes(baseImgBytes[:len(sparsetextBytes)],sparsetextBytes)+baseImgBytes[len(sparsetextBytes):]
    picMapNpy = np.frombuffer(baseImgWithInfoBytes,dtype=np.uint8).reshape((img_width,img_width,3)) # 基于该字节流拿到rgb矩阵
    Image.fromarray(picMapNpy).save(img_path)
saveText2Img('有内鬼终止交易.'*10,'version2.png')

在这里插入图片描述

2.3 从“好看”的图片中解密文本

同样,我们也需要对解密过程进行更新,使其能够正确提取出隐藏在“好看”图片中的文本信息:

def loadTextFromImg(img_path,baseColor=(70, 130, 180)):
    pic = Image.open(img_path).convert("RGB")
    img_width = pic.size[0]
    baseImg = Image.new("RGB", (img_width, img_width), baseColor)
    baseImgBytes = np.array(baseImg).tobytes()
    baseImgBytesFirst = baseImgBytes[:4*8]

    picBytes = np.array(pic).tobytes()
    picBytesFirst = picBytes[:4*8]
    lenBytes = bits_to_byte(xor_bytes(baseImgBytesFirst,picBytesFirst))
    lenData = struct.unpack('>l',lenBytes)[0] # 读取前4个字节, 为数据字节流的长度.
    
    byteData = picBytes[4*8:(4+lenData)*8]
    baseImgBytesData = baseImgBytes[4*8:(4+lenData)*8]
    byteData = bits_to_byte(xor_bytes(byteData,baseImgBytesData))
    text = byteData.decode('utf-8')
    return text
loadTextFromImg('version2.png')
2.4 使用真实的图片

使用真实图片的话和纯色图片类似, 只需要保证encode和decode使用同一个图片就行, 可能也还需要适当裁剪来保证尺寸, 具体细节就不在这里讨论了。


通过这两个过程,我们已经能够将文本成功地嵌入到图片中,并在必要时解密出来。虽然我们只展示了较为基础的加密和解密过程,但在实际应用中,这种方法可以扩展并与其他加密技术结合,使得信息传递更加安全。利用这一技巧,你可以将任何需要隐秘传输的文本信息嵌入到图片或视频中,从而提高信息安全性。

下面是公众号,欢迎扫描二维码,谢谢关注,谢谢支持!

公众号名称: 清易AI
公众号
欢迎来到“清易AI”!这里将为你揭开在家用电脑上轻松部署AI的神秘面纱。探索LLM、TTS、ASR等AI模型桌面部署,通过简洁而深入的技术文章,让你在AI的世界中轻松自如。成为家庭AI工程师,从容享受智能的乐趣!

  • 23
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于LSB(最低有效位)和DCT(离散余弦变换)算法实现文本图片嵌入的方法可以如下: 首先,将待嵌入文本转换为二进制格式,并确定要嵌入文本长度。然后,将文本分割为若干个子串,每个子串的长度与像素点数目一致。 接下来,对待嵌入图片进行DCT变换,将其转换为频域。对于每个DCT频域系数,将其最低有效位(LSB)替换为待嵌入文本对应位置的二进制位。这样就实现了文本嵌入。 当嵌入完成后,可以对修改后的DCT频域系数进行逆变换,将其转换回空域。此时,我们得到一个经过修改的图像。 为了提高隐藏文本的容量,在嵌入文本时,可以选择一些较小的系数进行替换。同时,为了保证嵌入文本对图像的影响尽可能小,可以选择较高频域的系数进行替换,因为这些系数对应的变化对于人眼来说并不明显。 为了减小对图像质量的影响,可以在嵌入前先对图像进行压缩。一种常用的压缩方法是JPEG,它基于DCT算法。 需要注意的是,嵌入文本后,图像中的一些像素会发生变化。虽然这些变化通常对人眼来说不易察觉,但通过分析图像的统计特性可以检测到文本的存在。 总而言之,基于LSB和DCT算法实现文本图片嵌入是一种简单而有效的方法。通过将文本信息嵌入到图像的频域系数中,可以实现隐藏信息而对图像质量的影响降到最低。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值