基于Python与OpenCV的图像文本编码实现问询及检测代码说明
用Python + OpenCV实现图像文本编码(隐写)方案
嘿,既然你已经搞定了图像文本检测的代码,那咱们来聊聊怎么把文本直接编码进图像本身——也就是常说的图像隐写。这里我给你推荐用LSB(最低有效位)隐写方案,用OpenCV就能轻松实现,而且图像几乎看不出肉眼可见的变化。
核心原理
LSB隐写的思路特别简单:图像每个像素的RGB通道值都是8位二进制数,最低位的变化对图像亮度、色彩的影响极小,人眼完全察觉不到。我们可以把文本转换成二进制,逐个替换这些最低位,就把文本“藏”进图像里了。
实现步骤(附代码)
1. 文本嵌入(编码)函数
这个函数会把文本转成二进制,然后嵌入到图像的像素中:
import cv2 import numpy as np def encode_text_to_image(image_path, text, output_path): # 读取图像,建议用PNG等无损格式,JPEG压缩会破坏隐写内容 image = cv2.imread(image_path) if image is None: raise ValueError("无法读取目标图像文件") # 给文本加结束标记'\0',方便解码时识别文本边界 text += '\0' # 把每个字符转成8位二进制字符串,拼接成完整的二进制文本 binary_text = ''.join([format(ord(c), '08b') for c in text]) text_length = len(binary_text) # 检查图像容量是否足够容纳文本 total_available_bits = image.shape[0] * image.shape[1] * 3 if text_length > total_available_bits: raise ValueError("文本太长啦,当前图像的容量不够装下它") # 遍历像素,逐个嵌入二进制文本位 bit_index = 0 for i in range(image.shape[0]): for j in range(image.shape[1]): for channel in range(3): # 遍历RGB三个通道 if bit_index < text_length: # 替换当前通道的最低位为文本的二进制位 current_pixel = format(image[i][j][channel], '08b') new_pixel = current_pixel[:-1] + binary_text[bit_index] image[i][j][channel] = int(new_pixel, 2) bit_index += 1 else: # 文本嵌入完成,保存图像 cv2.imwrite(output_path, image) return
2. 文本提取(解码)函数
如果需要把藏在图像里的文本取出来,用这个函数:
def decode_text_from_image(image_path): image = cv2.imread(image_path) if image is None: raise ValueError("无法读取目标图像文件") binary_text = "" for i in range(image.shape[0]): for j in range(image.shape[1]): for channel in range(3): # 提取每个通道的最低位 binary_text += format(image[i][j][channel], '08b')[-1] # 每凑够8位就转成字符,检查是否是结束标记'\0' if len(binary_text) % 8 == 0: current_char = chr(int(binary_text[-8:], 2)) if current_char == '\0': # 找到结束标记,返回去掉标记的文本 return ''.join([chr(int(binary_text[k:k+8], 2)) for k in range(0, len(binary_text)-8, 8)]) # 如果没找到结束标记,返回所有能解码的文本 return ''.join([chr(int(binary_text[k:k+8], 2)) for k in range(0, len(binary_text), 8)])
3. 和你的文本检测代码结合(可选)
如果你想只在检测到的文本区域附近嵌入文本,可以结合你现有的轮廓检测代码,只遍历轮廓对应的矩形区域内的像素:
# 复用你提供的检测代码,提取文本区域的矩形坐标 def get_text_regions(image_path): image = cv2.imread(image_path) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) _, thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY_INV) kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3)) dilated = cv2.dilate(thresh, kernel, iterations=13) contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # 返回所有文本区域的(x, y, 宽度, 高度)坐标 text_regions = [] for contour in contours: x, y, w, h = cv2.boundingRect(contour) text_regions.append((x, y, w, h)) return text_regions # 修改嵌入函数,只在指定文本区域内嵌入 def encode_text_in_regions(image_path, text, regions, output_path): image = cv2.imread(image_path) text += '\0' binary_text = ''.join([format(ord(c), '08b') for c in text]) text_length = len(binary_text) bit_index = 0 for (x, y, w, h) in regions: if bit_index >= text_length: break # 遍历文本区域内的像素 for i in range(y, y+h): for j in range(x, x+w): for channel in range(3): if bit_index < text_length: current_pixel = format(image[i][j][channel], '08b') new_pixel = current_pixel[:-1] + binary_text[bit_index] image[i][j][channel] = int(new_pixel, 2) bit_index += 1 else: cv2.imwrite(output_path, image) return cv2.imwrite(output_path, image)
使用示例
# 基础用法:把文本嵌入整个图像 encode_text_to_image("sample.png", "这是我藏在图像里的小秘密!", "encoded_sample.png") # 解码提取文本 hidden_text = decode_text_from_image("encoded_sample.png") print("提取到的隐藏文本:", hidden_text) # 进阶用法:只在检测到的文本区域嵌入文本 text_regions = get_text_regions("sample.png") encode_text_in_regions("sample.png", "只在文本区域附近藏的内容", text_regions, "encoded_regions_sample.png")
注意事项
- 优先使用PNG等无损图像格式,JPEG是有损压缩,会破坏嵌入的文本信息;
- 嵌入的文本长度不能超过图像总像素数×3(每个像素3个通道,每个通道存1位),如果文本太长,可以先压缩文本或者换用更大的图像。
内容的提问来源于stack exchange,提问作者radix
相关产品推荐
相关产品推荐

