You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python结合LSB技术在视频中隐藏并提取两张秘密图像?

实现方案:用LSB将两张图像嵌入视频帧并还原

前置依赖

先安装所需Python库:

pip install opencv-python numpy pillow

核心逻辑

LSB(最低有效位)隐写通过修改像素RGB通道的最低1位(或多位)存储秘密数据,肉眼几乎无法察觉。整体流程为:

  • 将两张秘密图像转为二进制数据流,附带尺寸信息(用于还原)
  • 遍历视频帧,将秘密数据依次写入帧像素的LSB位
  • 还原时反向提取LSB位,重组得到原始图像

步骤1:预处理秘密图像与视频帧

先将秘密图像转为带尺寸信息的二进制数据,同时读取拆分好的视频帧并转换为RGB格式(OpenCV默认BGR)。

import cv2
import numpy as np
from PIL import Image

def image_to_binary(img_path):
    # 读取图像并转为RGB格式
    img = Image.open(img_path).convert("RGB")
    width, height = img.size
    # 用4字节存储宽度、4字节存储高度(方便还原时解析)
    width_bytes = width.to_bytes(4, byteorder='big')
    height_bytes = height.to_bytes(4, byteorder='big')
    # 转换图像像素为二进制流
    pixel_data = img.tobytes()
    # 合并尺寸信息与像素数据
    return width_bytes + height_bytes + pixel_data

# 处理两张秘密图像
secret1_bin = image_to_binary("secret1.png")
secret2_bin = image_to_binary("secret2.png")
# 添加分隔符(4字节全1),方便还原时区分两张图像
separator = b'\xff\xff\xff\xff'
total_secret_bin = secret1_bin + separator + secret2_bin

# 读取拆分后的视频帧(假设帧存储在frames文件夹,命名为frame_0.jpg、frame_1.jpg...)
frames = []
frame_idx = 0
while True:
    frame_path = f"frames/frame_{frame_idx}.jpg"
    frame = cv2.imread(frame_path)
    if frame is None:
        break
    # BGR转RGB
    frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    frames.append(frame_rgb)
    frame_idx += 1

步骤2:LSB嵌入秘密数据

先检查视频帧总容量是否足够容纳秘密数据(每个像素3个通道可存3位数据),再逐帧逐像素替换LSB位。

def lsb_embed(frames, secret_bin):
    # 将二进制数据转为位列表
    secret_bits = [bit for byte in secret_bin for bit in f"{byte:08b}"]
    secret_bit_count = len(secret_bits)
    
    # 计算总可用存储位
    total_pixels = sum(frame.shape[0] * frame.shape[1] for frame in frames)
    total_available_bits = total_pixels * 3
    if secret_bit_count > total_available_bits:
        raise ValueError("视频帧总容量不足,无法嵌入全部秘密数据")
    
    bit_idx = 0
    # 逐帧逐像素嵌入数据
    for i in range(len(frames)):
        frame = frames[i]
        height, width, _ = frame.shape
        for y in range(height):
            for x in range(width):
                if bit_idx >= secret_bit_count:
                    break
                # 遍历RGB三个通道
                for c in range(3):
                    if bit_idx >= secret_bit_count:
                        break
                    # 替换当前通道的最低位
                    pixel_val = frame[y, x, c]
                    new_pixel_val = (pixel_val & 0xFE) | int(secret_bits[bit_idx])
                    frame[y, x, c] = new_pixel_val
                    bit_idx += 1
            if bit_idx >= secret_bit_count:
                break
        if bit_idx >= secret_bit_count:
            break
    # 转回BGR格式用于后续生成视频
    processed_frames = [cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) for frame in frames]
    return processed_frames

# 执行嵌入操作
processed_frames = lsb_embed(frames, total_secret_bin)

步骤3:重新生成含秘密的视频

用OpenCV将处理后的帧合成视频,保持原视频的帧率与分辨率。

def frames_to_video(frames, output_path, fps=30):
    height, width, _ = frames[0].shape
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    video_writer = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
    for frame in frames:
        video_writer.write(frame)
    video_writer.release()

# 生成输出视频(假设原视频帧率为30)
frames_to_video(processed_frames, "stego_video.mp4", fps=30)

步骤4:提取并还原秘密图像

从视频帧中提取LSB位,重组二进制数据后拆分出两张原始图像。

def lsb_extract(frames, target_bit_count):
    extracted_bits = []
    bit_idx = 0
    for frame in frames:
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        height, width, _ = frame_rgb.shape
        for y in range(height):
            for x in range(width):
                if bit_idx >= target_bit_count:
                    break
                for c in range(3):
                    if bit_idx >= target_bit_count:
                        break
                    # 提取当前通道的最低位
                    pixel_val = frame_rgb[y, x, c]
                    extracted_bits.append(str(pixel_val & 1))
                    bit_idx += 1
            if bit_idx >= target_bit_count:
                break
        if bit_idx >= target_bit_count:
            break
    # 将位列表转为二进制数据
    byte_chunks = [''.join(extracted_bits[i:i+8]) for i in range(0, len(extracted_bits), 8)]
    secret_bin = b''.join([int(chunk, 2).to_bytes(1, byteorder='big') for chunk in byte_chunks])
    return secret_bin

def binary_to_image(secret_bin, output_path):
    # 解析图像尺寸
    width = int.from_bytes(secret_bin[:4], byteorder='big')
    height = int.from_bytes(secret_bin[4:8], byteorder='big')
    # 提取像素数据并生成图像
    pixel_data = secret_bin[8:]
    img = Image.frombytes('RGB', (width, height), pixel_data)
    img.save(output_path)

# 读取含秘密的视频帧
stego_frames = []
cap = cv2.VideoCapture("stego_video.mp4")
while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    stego_frames.append(frame)
cap.release()

# 提取秘密数据(这里用嵌入时的总比特数)
extracted_secret_bin = lsb_extract(stego_frames, len(total_secret_bin)*8)

# 拆分两张秘密图像
separator = b'\xff\xff\xff\xff'
sep_idx = extracted_secret_bin.find(separator)
secret1_extracted_bin = extracted_secret_bin[:sep_idx]
secret2_extracted_bin = extracted_secret_bin[sep_idx+4:]

# 还原并保存图像
binary_to_image(secret1_extracted_bin, "recovered_secret1.png")
binary_to_image(secret2_extracted_bin, "recovered_secret2.png")

注意事项

  • 建议使用PNG格式的帧,JPG的压缩会破坏LSB数据,导致还原失败
  • 可选择修改多位LSB提升容量,但会增加被检测到的概率
  • 实际应用中,可将秘密数据的总长度嵌入到最前端,避免还原时手动指定长度

内容的提问来源于stack exchange,提问作者Heeya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 07:39:21