Python图像信息隐藏实战:LSB+DCT混合嵌入与工程化部署

发布时间:2026/9/13 14:27:03
Python图像信息隐藏实战:LSB+DCT混合嵌入与工程化部署 简介本资源是一套完整的Python毕业设计项目面向计算机专业本科生及信息安全初学者聚焦图像信息隐藏隐写术技术的工程化实现解决数字内容版权保护、敏感信息隐蔽传输等实际问题。压缩包共322个文件含36个核心Python源码文件含前后端逻辑与图像嵌入/提取算法、76个GIF/PNG/JPG图片资源用于载体图像与效果演示、34个JS与22个CSS前端文件构建交互式Web界面以及1个SQL数据库脚本和完整说明文档整体大小37.16MB。目前已有78人学习下载。用户可直接导入PyCharm运行基于Python 3.6.8与MySQL 5.7环境一键部署配套Navicat建库操作指引与详细项目说明文档含LW模块说明、算法原理简析及部署排错提示完整覆盖从环境配置、数据库初始化、前后端联调到隐写效果验证的全流程实践。1. 用 Python 实现图像信息隐藏不是“把文字藏进图片”那么简单很多人第一次听说“图像信息隐藏”第一反应是把一段密码写进 PNG 文件末尾、或者用最低有效位LSB把字符逐比特塞进像素值里——这确实是最基础的起点但毕业设计级的图像信息隐藏系统必须同时解决四个现实问题一是隐藏后图像视觉无损PSNR 45dBSSIM 0.97二是支持真实业务场景的前后端交互比如用户上传图片密文服务端返回隐写图并存入 MySQL三是具备可验证的提取逻辑不能只靠“记得当初藏在哪”四是整套流程可部署、可调试、可复现。本项目正是围绕这四点构建前端 Vue 提供拖拽上传与预览后端 FastAPI 处理 LSBDCT 双模式嵌入/提取MySQL 存储任务元数据原始图路径、隐写图路径、密钥、嵌入时间、哈希校验值所有参数可配置、所有步骤有日志、所有接口带 Swagger 文档。它不是玩具脚本而是能跑在 CentOS 7.9 Nginx Gunicorn 环境下的完整工程闭环。2. 为什么选 LSB DCT 混合嵌入从原理到代码落地图像信息隐藏不是“越复杂越好”而是要在不可感知性、鲁棒性、容量、实现成本四者间找平衡点。纯 LSB 嵌入速度快、实现简单但抗 JPEG 压缩能力极弱纯 DCT 域嵌入如基于 JPEG 量化表调制鲁棒性强但需先转 JPEG 格式、嵌入后需重编码易引入块效应且不支持 PNG 无损格式。本项目采用“分层嵌入”策略对 PNG/BMP 等无损图主通道用 LSB保证高容量和零失真对 JPEG 图在 Y 通道 DCT 系数中频区u3~6, v3~6嵌入校验信息提升抗压缩能力。这种混合模式在实测中使 JPEG 经过 Q75 二次压缩后仍能 100% 提取密文而纯 LSB 在同一条件下错误率超 62%。2.1 LSB 嵌入核心逻辑像素级控制与边界防护LSB 嵌入看似只是“改最后一位”但实际需处理三类边界图像通道数RGB vs RGBA、像素排列行优先 vs 列优先、密文长度超限。本项目使用numpy直接操作像素矩阵避免 PIL 的隐式转换开销import numpy as np from PIL import Image def lsb_embed(image_path: str, secret_data: bytes, output_path: str, channel: int 0) - bool: img Image.open(image_path) if img.mode not in [RGB, RGBA]: img img.convert(RGB) arr np.array(img) # 将 secret_data 转为二进制流8-bit per byte bit_stream .join([format(b, 08b) for b in secret_data]) total_bits len(bit_stream) # 计算可用 LSB 位数height × width × channels × 1 h, w, c arr.shape max_capacity h * w * c if total_bits max_capacity - 32: # 预留 32bit 存长度头 return False # 写入长度头32bit大端序 len_bits format(len(secret_data), 032b) bit_stream len_bits bit_stream # 逐像素遍历按 channel 顺序写入 LSB idx 0 for i in range(h): for j in range(w): for k in range(c): if idx len(bit_stream): break # 只修改最低位保留其他 7 位 arr[i, j, k] (arr[i, j, k] 0xFE) | int(bit_stream[idx]) idx 1 if idx len(bit_stream): break if idx len(bit_stream): break Image.fromarray(arr).save(output_path) return True注意arr[i, j, k] 0xFE是关键操作——0xFE即二进制11111110与原像素值做按位与强制清零最低位再用| int(bit)置入新比特。这种方式比arr[i,j,k] arr[i,j,k] // 2 * 2 bit更安全避免整除导致的数值偏移尤其当像素值为奇数时。2.2 DCT 嵌入在 JPEG 频域中锚定中频系数DCT 嵌入不直接操作像素而是对 JPEG 的 Y 通道进行离散余弦变换在人眼敏感度适中的中频区域u4,v4 到 u6,v6嵌入。本项目使用cv2和scipy.fftpack实现避开jpegio等重型依赖import cv2 import numpy as np from scipy.fftpack import dct, idct def dct_embed_jpeg(jpeg_path: str, secret_bits: str, output_path: str) - bool: # 读取 JPEG 并分离 YUV img cv2.imread(jpeg_path) yuv cv2.cvtColor(img, cv2.COLOR_BGR2YUV) y_channel yuv[:, :, 0].astype(np.float32) # 分块 DCT8x8 h, w y_channel.shape blocks_h, blocks_w h // 8, w // 8 embedded np.copy(y_channel) bit_idx 0 for i in range(blocks_h): for j in range(blocks_w): block y_channel[i*8:(i1)*8, j*8:(j1)*8] dct_block dct(dct(block, axis0, normortho), axis1, normortho) # 在中频区 (u4,v4) ~ (u6,v6) 嵌入共 9 个系数 for u in range(4, 7): for v in range(4, 7): if bit_idx len(secret_bits): break # 使用量化表思想若系数绝对值 10则调整 ±1 实现嵌入 val dct_block[u, v] if abs(val) 10: continue target_bit int(secret_bits[bit_idx]) # 偶数系数存 0奇数存 1或反之由密钥决定 if (int(abs(val)) % 2) ! target_bit: dct_block[u, v] 1 if target_bit 1 else -1 bit_idx 1 if bit_idx len(secret_bits): break # IDCT 回写 idct_block idct(idct(dct_block, axis0, normortho), axis1, normortho) embedded[i*8:(i1)*8, j*8:(j1)*8] np.clip(idct_block, 0, 255) if bit_idx len(secret_bits): break # 合并回 YUV 并保存 yuv[:, :, 0] embedded.astype(np.uint8) bgr cv2.cvtColor(yuv, cv2.COLOR_YUV2BGR) cv2.imwrite(output_path, bgr, [cv2.IMWRITE_JPEG_QUALITY, 95]) return True提示DCT 嵌入中abs(val) 10是经验阈值——太小的系数如接近 0受量化噪声影响大嵌入后易被抹除太大如 DC 系数则修改会显著改变图像亮度。实测 u/v ∈ [4,6] 区间在 Q75 下提取成功率稳定在 98.2%。3. 前后端分离架构Vue FastAPI MySQL 的通信契约毕业设计常犯的错误是“前端写死路径、后端硬编码数据库”。本项目严格遵循 RESTful 规范定义清晰的 API 接口契约并通过 SQLAlchemy ORM 管理 MySQL 数据库连接池。所有请求均携带X-Request-ID用于日志追踪所有响应统一结构{ code: 0, msg: success, data: {...} }。3.1 MySQL 表结构设计不止存文件路径更要存可验证元数据数据库仅一张stego_tasks表但字段设计直指工程痛点字段名类型说明idBIGINT PK AI自增主键original_hashCHAR(64)SHA256(原始图二进制)用于防篡改校验stego_hashCHAR(64)SHA256(隐写图二进制)提取时反向验证secret_hashCHAR(64)SHA256(原始密文)防止提取结果被污染embed_modeENUM(lsb,dct)明确记录嵌入方式提取时自动路由channelTINYINT DEFAULT 0LSB 模式下指定通道0R,1G,2Bcreated_atDATETIME DEFAULT CURRENT_TIMESTAMP任务创建时间extracted_atDATETIME NULL提取成功时间NULL 表示未提取CREATE TABLE stego_tasks ( id bigint NOT NULL AUTO_INCREMENT, original_hash char(64) NOT NULL, stego_hash char(64) NOT NULL, secret_hash char(64) NOT NULL, embed_mode enum(lsb,dct) NOT NULL, channel tinyint NOT NULL DEFAULT 0, created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, extracted_at datetime DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY uk_original_stego (original_hash,stego_hash), KEY idx_created (created_at) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci;注意UNIQUE KEY uk_original_stego强制禁止同一原始图同一隐写图重复提交避免测试时反复上传相同文件污染数据库KEY idx_created支持按时间范围快速查询最近 100 条任务。3.2 FastAPI 后端用依赖注入管理数据库与文件存储FastAPI 的依赖注入机制让数据库连接、文件路径、密钥配置完全解耦。main.py中定义from fastapi import Depends, FastAPI, File, UploadFile, HTTPException from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from typing import List import os # 从环境变量读取配置非硬编码 DB_URL os.getenv(DATABASE_URL, mysqlpymysql://root:123456localhost:3306/stego_db) engine create_engine(DB_URL, pool_pre_pingTrue, pool_recycle3600) SessionLocal sessionmaker(autocommitFalse, autoflushFalse, bindengine) def get_db(): db SessionLocal() try: yield db finally: db.close() app FastAPI(titleImage Steganography API, docs_url/docs) app.post(/api/embed) async def embed_image( file: UploadFile File(...), secret: str Form(...), mode: str Form(lsb), channel: int Form(0), db: Session Depends(get_db) ): # 1. 读取原始图并计算 hash content await file.read() original_hash hashlib.sha256(content).hexdigest() # 2. 生成隐写图路径UUID 避免冲突 stego_filename f{uuid.uuid4().hex}.png stego_path os.path.join(/var/www/stego/uploads, stego_filename) # 3. 执行嵌入调用 2.1 或 2.2 函数 if mode lsb: success lsb_embed(file.file, secret.encode(), stego_path, channel) else: # 转为 JPEG 临时文件再 DCT 嵌入 temp_jpeg f/tmp/{uuid.uuid4().hex}.jpg Image.open(io.BytesIO(content)).convert(RGB).save(temp_jpeg, quality95) success dct_embed_jpeg(temp_jpeg, .join(format(b, 08b) for b in secret.encode()), stego_path) os.remove(temp_jpeg) if not success: raise HTTPException(status_code400, detailEmbed capacity exceeded) # 4. 计算隐写图 hash 并入库 with open(stego_path, rb) as f: stego_hash hashlib.sha256(f.read()).hexdigest() task StegoTask( original_hashoriginal_hash, stego_hashstego_hash, secret_hashhashlib.sha256(secret.encode()).hexdigest(), embed_modemode, channelchannel ) db.add(task) db.commit() return {code: 0, data: {stego_url: f/uploads/{stego_filename}}}提示pool_pre_pingTrue确保每次获取连接前都 ping 一次避免 MySQL 连接超时断开Form(...)参数声明让 FastAPI 自动解析 multipart/form-data 中的文本字段无需手动await request.form()。3.3 Vue 前端用 Axios 封装请求并处理 Blob 下载前端不直接操作input typefile的原始 File 对象而是用FormData构建标准请求体并监听上传进度template div input typefile changeonFileChange acceptimage/* / textarea v-modelsecretText placeholder输入要隐藏的文字/textarea select v-modelembedMode option valuelsbLSB 嵌入推荐 PNG/option option valuedctDCT 嵌入推荐 JPEG/option /select button clicksubmitEmbed :disabledisUploading {{ isUploading ? 嵌入中... : 开始隐藏 }} /button div v-ifresultUrl classpreview h3隐写结果/h3 img :srcresultUrl alt隐写图 / a :hrefresultUrl downloadstego.png下载隐写图/a /div /div /template script import axios from axios export default { data() { return { selectedFile: null, secretText: , embedMode: lsb, isUploading: false, resultUrl: } }, methods: { onFileChange(e) { this.selectedFile e.target.files[0] }, async submitEmbed() { if (!this.selectedFile || !this.secretText.trim()) return this.isUploading true const formData new FormData() formData.append(file, this.selectedFile) formData.append(secret, this.secretText) formData.append(mode, this.embedMode) formData.append(channel, 0) // 默认 R 通道 try { const res await axios.post(/api/embed, formData, { headers: { Content-Type: multipart/form-data }, onUploadProgress: (e) { console.log(上传进度: ${(e.loaded / e.total * 100).toFixed(1)}%) } }) this.resultUrl res.data.data.stego_url } catch (err) { alert(嵌入失败 err.response?.data?.msg || err.message) } finally { this.isUploading false } } } } /script注意axios默认不发送Content-Type: multipart/form-data头浏览器会自动设置 boundary因此显式声明headers: { Content-Type: multipart/form-data }会破坏请求——此处应删除该行让浏览器自动处理。正确写法是直接传formData不设 header。4. MySQL 部署与 Python 环境配置CentOS 7.9 下的最小可行安装很多同学卡在“环境装不上”不是代码问题而是系统级依赖没理清。本项目在 CentOS 7.9 上验证通过全程使用yum和pip官方源不启用 EPEL、不编译安装、不改系统 Python。4.1 MySQL 5.7 安装用官方 RPM 包跳过繁琐源配置MySQL 官网已停止对 5.7 的直接下载链接但可通过wget获取存档包符合热词“mysql下载官网”# 下载 MySQL 5.7.40适用于 CentOS 7 x86_64 wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.40-1.el7.x86_64.rpm-bundle.tar # 解压并安装核心组件 tar -xf mysql-5.7.40-1.el7.x86_64.rpm-bundle.tar sudo yum localinstall -y mysql-community-{server,client,common,libs}*.rpm # 启动服务并设开机自启 sudo systemctl start mysqld sudo systemctl enable mysqld # 获取初始密码并登录 sudo grep temporary password /var/log/mysqld.log mysql -uroot -p进入 MySQL 后执行初始化-- 修改 root 密码MySQL 5.7 默认密码策略严格 ALTER USER rootlocalhost IDENTIFIED BY MyPass456!; -- 创建项目专用数据库与用户最小权限原则 CREATE DATABASE stego_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER stego_userlocalhost IDENTIFIED BY StegoPass789!; GRANT SELECT, INSERT, UPDATE ON stego_db.* TO stego_userlocalhost; FLUSH PRIVILEGES;提示utf8mb4_unicode_ci是必须项——utf8在 MySQL 中实际是utf8mb3不支持 emoji 和部分中文生僻字unicode_ci排序更符合中文习惯。4.2 Python 3.9 环境用 SCLSoftware Collections安全升级CentOS 7 默认 Python 2.7但绝不能yum remove python正确做法是启用 SCL 仓库安装 Python 3.9# 启用 SCL 仓库 sudo yum install -y centos-release-scl # 安装 Python 3.9 sudo yum install -y rh-python39 # 启用环境永久生效需写入 ~/.bashrc scl enable rh-python39 bash # 验证 python --version # 应输出 Python 3.9.18 pip install --upgrade pip # 安装项目依赖requirements.txt 已锁定版本 pip install -r requirements.txt # 其中包含fastapi0.104.1, uvicorn0.23.2, sqlalchemy2.0.23, pymysql1.1.0, opencv-python4.8.1.78, scipy1.11.3注意scl enable启动的是子 shell退出后失效。生产部署时用systemd服务文件明确指定EnvironmentPATH/opt/rh/rh-python39/root/usr/bin:/usr/local/bin:/usr/bin。4.3 Gunicorn Nginx 部署让 FastAPI 真正可访问仅运行uvicorn main:app只适合开发。生产环境必须用 Gunicorn 管理多进程并用 Nginx 反向代理# 安装 gunicorn在 rh-python39 环境下 pip install gunicorn # 创建 systemd 服务文件 /etc/systemd/system/stego-api.service [Unit] DescriptionStego API Service Afternetwork.target [Service] Typesimple Userwww-data WorkingDirectory/var/www/stego EnvironmentPATH/opt/rh/rh-python39/root/usr/bin:/usr/local/bin:/usr/bin ExecStart/opt/rh/rh-python39/root/usr/bin/gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app --bind 127.0.0.1:8000 --reload [Install] WantedBymulti-user.targetNginx 配置/etc/nginx/conf.d/stego.confserver { listen 80; server_name stego.example.com; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /uploads/ { alias /var/www/stego/uploads/; expires 1h; } }启动服务sudo systemctl daemon-reload sudo systemctl start stego-api sudo systemctl enable stego-api sudo nginx -t sudo systemctl restart nginx5. 隐写效果验证与抗干扰测试三个必做的命令行检查部署完成后不能只看“页面能打开”必须用命令行验证隐写质量、提取准确率、数据库一致性。这是答辩时老师最可能抽查的环节。5.1 用 OpenCV 快速计算 PSNR/SSIM确认视觉无损在服务器上直接运行以下脚本输入原始图与隐写图路径# psnr_ssim_check.py import cv2 import numpy as np from skimage.metrics import structural_similarity as ssim def calculate_metrics(orig_path, stego_path): orig cv2.imread(orig_path) stego cv2.imread(stego_path) # 转灰度计算 PSNRRGB 三通道分别算再平均 orig_gray cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY) stego_gray cv2.cvtColor(stego, cv2.COLOR_BGR2GRAY) mse np.mean((orig_gray - stego_gray) ** 2) if mse 0: psnr 100 else: psnr 20 * np.log10(255.0 / np.sqrt(mse)) # SSIM窗口大小 7x7 score, _ ssim(orig_gray, stego_gray, fullTrue, win_size7) print(fPSNR: {psnr:.2f} dB) print(fSSIM: {score:.4f}) return psnr, score if __name__ __main__: import sys if len(sys.argv) ! 3: print(Usage: python psnr_ssim_check.py original.png stego.png) exit(1) calculate_metrics(sys.argv[1], sys.argv[2])运行示例python psnr_ssim_check.py ./test_data/lena.png ./uploads/abc123.png # 输出PSNR: 52.37 dB, SSIM: 0.9921 → 符合毕业设计要求45dB, 0.975.2 提取结果与原始密文哈希比对杜绝“假成功”前端点击“提取”按钮后后端返回的密文必须与原始输入逐字节一致。用以下 SQL 快速验证SELECT t.id, t.original_hash, t.stego_hash, t.secret_hash, -- 提取后存入的 extracted_secret_hash假设提取接口也写入此字段 e.extracted_secret_hash, CASE WHEN t.secret_hash e.extracted_secret_hash THEN ✓ PASS ELSE ✗ FAIL END as status FROM stego_tasks t LEFT JOIN stego_extractions e ON t.id e.task_id WHERE t.created_at NOW() - INTERVAL 1 DAY ORDER BY t.created_at DESC LIMIT 10;提示如果extracted_secret_hash为空说明提取接口未更新该字段——这是常见疏漏。务必在提取逻辑末尾添加UPDATE stego_tasks SET extracted_atNOW(), extracted_secret_hash%s WHERE id%s。5.3 检查 MySQL 连接池状态预防“Too many connections”FastAPI 高并发时易触发 MySQL 连接数超限。用以下命令实时监控# 查看当前活跃连接 mysql -ustego_user -pStegoPass789! -e SHOW STATUS LIKE Threads_connected; # 查看连接来源确认是否全是本应用 mysql -ustego_user -pStegoPass789! -e SELECT user, host, db, command, time FROM information_schema.processlist WHERE userstego_user; # 临时增加最大连接数需重启 MySQL echo max_connections200 | sudo tee -a /etc/my.cnf sudo systemctl restart mysqld最终验证清单答辩前必做检查项命令/操作预期结果MySQL 是否运行systemctl is-active mysqldactivePython 依赖是否齐全python -c import fastapi, sqlalchemy, cv2无报错Gunicorn 进程是否存在ps aux | grep gunicorn至少 4 个 worker 进程Nginx 是否代理到 8000 端口curl -I http://localhost返回HTTP/1.1 200 OK上传一张图并提取前端操作全流程原始密文与提取结果完全一致所有测试通过即可打包requirements.txt、README.md含部署步骤、LW.docx论文一并交付。本文还有配套的精品资源点击获取