nsF5 图像隐写工具 (Steganography)
针对 8bit 灰度/彩色图像 的隐写研究工具,实现了基于伴随式矩阵编码(二元汉明码) 的 nsF5 隐写算法,并附带盲隐写分析、图像哈希键控与码族/嵌入效率可视化。
项目位于 F:\Steganography,核心为纯 Python(依赖 numpy/Pillow,GUI 使用标准库 tkinter);
另提供 C++ 加速库(cpp/fsfeatures.dll 特征提取、cpp/nsf5embed.dll 嵌入热路径 + 确定性置乱
nsf5_permute,MinGW 编译,跨语言校验与 Python 一致性一致;置乱在 DLL 缺失时自动回退到
Python 同算法,嵌入/解码两端序列恒定可逆)。
功能总览
| 模块 | 说明 |
|---|---|
| 嵌入 / 解码 | 将 ASCII 字符串嵌入图像 LSB,解码还原;支持口令键控 |
| 伴随式矩阵编码 | nsF5 + F5 / LSB 矩阵编码,二元汉明码 [n=2^p-1, k, 3],块内至多改 1 系数 |
| 湿纸编码 | nsF5 核心:预标记"减幅归零=湿"位置,在干位解 GF(2) 线性方程,无收缩 |
| 图像哈希键控 | 载入时计算 SHA-256;隐藏路径由"内容哈希+口令"唯一决定,解码端自同步并感知篡改 |
| 盲隐写分析 | 卡方检验(Westfeld) + RS 分析(Fridrich),输出 0–1 隐写倾向概率与判读 |
| ML 隐写分类器(双版本) | 143d 稳健版(默认) + 53d 可解释版,详见下文"双版本部署策略" |
| 绘图 | 绘制码族(嵌入率 α vs 载荷)理论曲线 与 实测嵌入效率对比 |
| GUI | 载入图 → 嵌入/解码 → 分析 → 绘图 一体化界面 |
模型双版本(2026-09-06)
项目保留两套训练好的 LGB 模型,默认加载 143d 稳健版,可切换到 53d 可解释版:
from src.ml_predict import MLPredictor
# 默认 143d (稳健, 部署推荐)
pred = MLPredictor() # models/stego_classifier.joblib
# 切换 53d 可解释版 (AUC 更高, 论文/教学推荐)
pred = MLPredictor(model_path='models/stego_classifier_v2_jpeg_lgb_51d.joblib',
clip_outliers=False)
# 预测
r = pred.predict(image)
# r = {'probability': 0.83, 'verdict': '含密(stego)', 'threshold': 0.168}
| 版本 | 文件 | AUC (8-split mean) | OOD 鲁棒 | 推荐场景 |
|---|---|---|---|---|
| 143d 默认 | stego_classifier.joblib |
0.9085 | 1/8 fp | 通用部署 / 真实图 |
| 53d 可解释 | stego_classifier_v2_jpeg_lgb_51d.joblib |
0.9227 | 3/8 fp | 论文 / 答辩 / 教学 |
详见下文的 双版本部署策略 一节。
安装与运行
方式 A:从源码运行
cd F:\Steganography
pip install numpy pillow
python src\gui.py
若系统默认
python未带 tkinter,可用带 tkinter 的解释器(如C:\Python314\python.exe):C:\Python314\python.exe src\gui.py
方式 B:安装打包的模块(wheel)
每个版本会以源码包发布,可构建并安装:
# 构建 wheel + sdist(需已安装 build)
python -m build
# 安装 wheel(核心模块:ns5_core / steganalysis / gui 等)
pip install dist\nsf5stego-1.1.0-py3-none-any.whl
注意:wheel 仅含纯 Python 核心;
cpp/下的 Windows DLL(特征提取/嵌入加速)随仓库源码发布, 运行 GUI/离线使用仍需项目源码目录内的cpp/。
运行测试
python src\test_core.py # 核心算法自测(嵌入/解码 + 汉明矩阵 + 湿纸 + 口令)
python src\test_steg.py # 盲隐写分析自测(区分 干净/含密 图)
python src\run_e2e.py # 端到端验证(嵌入→保存→解码→分析→绘图)
python src\test_gui.py # GUI 冒烟测试(构建窗口/载入/预览)
GUI 使用流程
- 点击 载入原始图 / 含密图 选择 8bit 图像。
- 选择 算法(
nsF5或matrix)、参数 p(块比特数,越大效率越高)、可选口令。 - 在文本框中输入待嵌入的 ASCII 字符串。
- 点击 1 嵌入并保存 → 生成
output/stego_*.png,右侧预览含密图。 - 点击 2 解码提取 → 从含密图还原字符串(须与嵌入使用相同 算法/p/口令)。
- 点击 3 分析 → 显示 SHA256、卡方统计、RS 缺口、估计嵌入率与隐写概率。
- 点击 生成码族与效率图 → 弹出理论 vs 实测效率对比图。
解码与嵌入参数(方法/p/口令)必须一致;口令或图像内容不匹配将无法正确解码。
目录结构
F:\Steganography
├── README.md
├── cpp
│ ├── fsfeatures.cpp # C++ 特征提取源
│ ├── fsfeatures.dll # 编译产物 (MinGW)
│ ├── nsf5embed.cpp # C++ nsF5/matrix 嵌入热路径源
│ └── nsf5embed.dll # 编译产物 (MinGW)
├── data/dataset.csv # 有监督训练数据集 (clean+stego 特征)
├── gpu
│ ├── make_imageset.py # GPU版数据集生成 (完整512, 1干净+4含密变体/照片)
│ ├── featurize_gpu.py # GPU批量向量化 11 维统计特征 (与 CPU 参考 bit 级一致)
│ ├── train_ml_gpu.py # GPU特征提取 + 按照片分组训练分类器
│ └── predict_gpu.py # 单图像 GPU 隐写检测
├── models # 训练出的分类器 stego_classifier.joblib / steg_classifier_gpu.joblib
└── src
├── ns5_core.py # nsF5 核心:汉明码、湿纸求解、哈希键控、嵌入/解码
├── cppembed.py # C++ 嵌入封装 (自校验与 Python 像素级一致)
├── steganalysis.py # 盲隐写分析:卡方 + RS 嵌入率估计
├── fsfeatures.py # C++ 特征库的 ctypes 绑定(含与 Python 一致性校验)
├── ml_predict.py # 有监督 ML 判定封装
├── make_dataset.py # 批量生成特征数据集 (支持 --preprocess srm SRM 预处理)
├── srm_filter.py # SRM 高通滤波预处理层 (numpy + torch 双实现, 30 核)
├── train_model.py # 训练/评估/保存分类器
├── efficiency.py # 码族与嵌入效率绘图
├── image_io.py # 图像读写工具
├── gui.py # tkinter GUI
├── run_e2e.py # 端到端验证脚本
├── test_core.py # 核心算法单测
├── test_steg.py # 隐写分析单测
├── test_false_positive.py # 误报回归测试
└── test_gui.py # GUI 冒烟测试
├── img # 示例封面图
└── output # 生成结果(含密图、效率图)
技术细节
伴随式矩阵编码(nsF5)
二元汉明码 [n,k,d],n = 2^p - 1,校验矩阵 H 的列向量取 GF(2)^p 全部非零向量。
载体系数 LSB 奇偶向量 x 的伴随式 s = H·x (mod 2)。
- 嵌入
p比特消息m:若s == m不改动;否则d = s ⊕ m, 找到唯一列j(H_j == d)翻转该系数 → 每块至多改 1 个系数。 - 需要改动的概率
(2^p-1)/2^p,嵌入效率α(p) = p·2^p / (2^p-1),随 p 增大而提高。
F5 → nsF5
- F5:直流/减幅归零时"收缩",该块整块重嵌、载荷下降。
- nsF5:用湿纸编码预标记"减幅会归零 = 湿"的位置,湿位不动, 在干位解 GF(2) 线性方程完成嵌入 → 无收缩,效率与安全性更高。
图像哈希键控
载入原图计算 SHA-256(cover_hash):
- 头部区:用仅口令派生的种子预埋
cover_hash(认证头); - 正文区:用
cover_hash + 口令派生的种子键控置乱路径。
解码端先用口令种子读回头部,重算正文种子解码 → 隐藏路径由图像内容唯一决定, 改动任意像素会破坏解码结构,可经头部校验感知篡改。
盲隐写分析
- 卡方检验(Westfeld):相邻灰度对
(2i,2i+1)频率在嵌密后趋近均衡, p 值高表示该区已随机化/嵌入。 - RS 分析(Fridrich-Goljan-Du):统计正/负掩码的常规-奇异缺口
Gr、Gn;干净图 LSB 平面有结构(Gn 明显为正),隐写使其随机化而下降。 - 综合多个统计量给出 0–1 隐写倾向概率 与判读(不太可能 / 可能 / 高度可能)。
盲隐写分析本质为启发式:没有原始封面时无法给出精确绝对概率, 此处概率供评估与教学参考。
有监督 ML 隐写分析(C++ 特征提取 + 校园照片训练)
将特征提取从 Python 移植到 C++(cpp/fsfeatures.dll,MinGW 编译),
可显著降低逐图统计开销;嵌入热路径同样提供 C++ 版(cpp/nsf5embed.dll,
经跨语言回环校验与 Python 像素级一致,cppembed.py 封装)。
并用真实校园照片做有监督训练,得到一个可部署的分类器。
训练管线
# 1) 用 F:\DCIM\Camera 下照片批量生成 干净/含密 特征数据集
# (每张降采样 512x512 灰度, 1 干净 + 6 含密变体, C++ 提取 11 维特征 + C++ 嵌入)
python src\make_dataset.py
# 2) 训练/评估 (预留 held-out 测试集 + 5 折 GroupKFold 交叉验证选模)
python src\train_model.py
- 特征 (全由 C++ 计算):
RS_Gn, RS_Gr, Rm, Sm, Rn, Sn、chi2_pvalue, chi2_stat、diff_entropy、lsb_diff_entropy、median_prefix_p。 - 6 档含密变体:覆盖弱→强,
nsF5 p3(弱密度)至matrix p2(强)。 414 张照片 → 2898 样本(414 干净 + 2484 含密)。 - 5 折 GroupKFold:按照片分组交叉验证选模(杜绝同源泄漏), 再在留出测试集上报综合指标与每密度层检出率。
在 GUI 中启用
GUI 新增 判定灵敏度 下拉框(严格 / 均衡 / 宽松),作用于 3 分析:
- 严格 (低误报):提高含密判定阈值 → 干净图更少被误判;
- 均衡:默认。
- 宽松 (高检出):下调阈值 → 更易检出弱密度嵌入(代价是误报略升)。
它与下方 ML 分类含密概率 联动(同一张净图在三种灵敏度下阈值
0.95→0.77→0.57,ML 判决会由"干净"切换到"含密"),启发式概率也会围绕
0.5 上下牵引。GUI 3 分析会在原有启发式结果下方追加一行 ML 分类含密概率,
输入图像会自动按训练一致的方式(转灰度→512 缩放→C++ 特征)送入模型。
模型未加载时会提示先运行 train_model.py。
# 单独用 ML 判定单张图
python -c "import sys; sys.path.insert(0,'src'); from ml_predict import get_predictor; \
import numpy as np,os; from PIL import Image; from ns5_core import embed_string; \
a=np.asarray(Image.open(r'img/cover.png').convert('L').resize((512,512)).convert('L')); \
print(get_predictor().predict(a))"
局限:真实 JPEG 照片的 LSB 位平面天然近乎随机,弱密度 nsF5 嵌入的 统计足印很弱;ML 概率应与启发式判读交叉印证,不宜单独作为铁证。
SRM 高通滤波预处理层(检测性能提升)
src/srm_filter.py 提供 SRM (Spatial Rich Model) 高通滤波预处理层,在提取
11 维统计特征之前对图像做高通滤波,突出嵌入噪声、抑制图像内容,从而增强
弱密度隐写的统计足印。
- 30 个标准 SRM 核(Fridrich 体系,与 Ye-Net 所用一致):一阶差分 8 + 二阶 4 + 三阶 8 + 边缘 3×3 4 + 边缘 5×5 4 + 方形 3×3/5×5 各 1;按标准因子归一化并补齐到 5×5。
- 合成增强图:逐像素取 30 个残差的最大绝对响应,clip 到 ±T(默认 4)后平移缩放到 uint8 [0,255],输出单通道增强图,直接喂给现有特征器,其余管线不变。
- CPU (numpy) / GPU (torch conv) 双实现,接口一致
(
srm_residuals_np/srm_residuals_torch/preprocess_batch_torch)。
接入方式(两条管线均已内置开关):
# CPU数据集: --preprocess srm (默认 none=原图基线)
py src\make_dataset.py data\campus_jpg --out campus_srm --preprocess srm -j 16
# GPU特征: extract_features_gpu(x, use_srm=True) (默认开启)
# 自检: py gpu\featurize_gpu.py (含 SRM 路径冒烟)
实测效果(同一 414 张校园照片,LR,同协议 5 折 GroupKFold + 留出测试):
| 指标 | 原图基线 (dataset.csv) | SRM 增强 (dataset_campus_srm.csv) |
|---|---|---|
| 5 折 CV-AUC | 0.7594 | 0.7922 |
| held-out 测试 AUC | 0.7811 | 0.8085 |
| 低误报点含密检出率 | 41.2% | 50.3% |
弱密度 nsF5 p3 d0.25 检出 |
51.9% | 62.5% |
nsF5 p2 d0.35 检出 |
76.0% | 90.4% |
| 干净误报率(低误报阈值) | 9.6% | 9.6% |
SRM 在保持低误报不变的同时,将 CV-AUC 提升约 3.3 个百分点、测试 AUC 提升约 2.7 个百分点,弱密度档检测增益尤其显著,且 SRM 增强图约 0.35s/张、 16 核并行下生成全量数据集不受影响。
重要(跨源合并时结论相反):SRM 的收益只出现在同源内。接入 BOSSbase 全量做跨源合并训练时,SRM 反而显著拉低性能,CPU 与 GPU 两管线 相互印证:
跨源合并 CPU (校园+全量BOSSbase, 测试AUC) GPU (校园+BOSSbase2000源, 验证AUC) 非 SRM(基线) 0.741 0.651 SRM 0.704(−3.7pp) 0.572(−7.9pp) 机理:SRM 高通滤波在抑制图像内容的同时,也把不同相机/JPEG 压缩源之间的可区分 信号一并压平——同源时被压掉的是嵌入噪声(收益),跨异构源时被压掉的是跨源可分 性(损失)。因此默认模型采用未 SRM 的合并全量(
stego_classifier.joblib, 测试 AUC≈0.741),SRM 单源校园模型另存为stego_classifier_campus_srm.joblib(AUC≈0.8085,仅供校园同源场景)。
v2 扩展特征(143 维)与多档变体 A/B(2026-09)
在原 11 维之上扩展为 143 维 v2 特征集:30 个 SRM 残差的均值 / 绝对均值 / 标准差
(90 维)+ 20 段前缀卡方 p(20 维)+ texture_noise + est_rate(2 维)+ 20 段 LSB 前缀
卡方 p(20 维)。同时把训练变体从 6 档扩展到 12 档(追加 nsF5 p3 d=0.40、
matrix p3 d=0.40/0.60、lsb d=0.30/0.50/0.70)。
# 重新生成 v2 数据集 (单进程 GPU, 414 张 ~7 min)
py src\make_dataset.py --out campus_v2 --feature-set v2 --variants all
py src\make_dataset.py --out campus_v2_min --feature-set v2 --variants minimal # 6 档对照
# 训练 v2 143d + 4 模型 stacking (5 折 GroupKFold, 留出 25% 测)
$env:DS_FILES = "dataset_campus_v2.csv"
py src\train_model.py
严格 A/B 对比(同一测试集照片 ID 分组,5 折 GroupKFold OOF):
| 数据集 | 特征 | OOF-AUC | 弱档 nsF5 p3 d=0.25 检出 |
模型 |
|---|---|---|---|---|
dataset.csv (6 档) |
v1 11 维 | 0.7678 | 49% | XGB |
dataset_campus_v2.csv (12 档) |
v1 11 维 | 0.7868 | — | LR |
dataset_campus_v2.csv (12 档) |
v2 143 维 | 0.8143 | 25% | LR |
dataset_campus_v2.csv (12 档) |
v2 143 维 (held-out) | 0.8278 | 25% | LR |
- v2 数据集 12 档 vs 6 档:+0.019(同 11 维)→ 真实信号(多档位学到档位差)。
- v2 143 维 vs v1 11 维:+0.027 → SRM 残差 / 20 段前缀 p 确实贡献判别力。
- 累计 +0.05 AUC(0.78 → 0.83)。
部署警告:v2 模型在 5 折 OOF 与 held-out 上 AUC 显著提升,但实际部署到真实 JPEG 干净图时倾向过激(SRM 残差对 JPEG 高频噪声过于敏感,干净 JPEG 几乎全被 判 1.0)。默认
stego_classifier.joblib仍为 v1 11 维 XGB 模型(held-out AUC=0.7435,干净 JPEG 误判率低);v2 模型另存为stego_classifier_v2_campus_stack.joblib(AUC=0.8278,仅供训练分布内的 PGM/BMP 灰度图使用)。
分布外(OOD)分析与缓解尝试
为修复 v2 在真实 JPEG 干净图上的过激(logit 263 vs 训练集 clean mean 1.68), 探索了两种部署抗偏移方案(均不替换默认模型,仅做参考):
| 方案 | 思路 | 真实 JPEG 干净 prob | 局限 |
|---|---|---|---|
| 原始 v2 | 无防护 | 1.000 | — |
| ① clip-to-±5σ | 把每维特征裁到训练集 μ±5σ | 0.996 | JPEG 干净 logit=5.6 已超训练集 clean p99=3.73 |
| ② OOD-cap | logit 超 clean p99 时封顶概率到 clean p99 prob (0.977) | 0.977 | 仍 ≥ thr 0.912;stego p5=1.30 已与 clean p99=3.73 重叠,单阈值无解 |
根本原因:训练集 clean 与 stego 的 logits 严重重叠(clean p99=3.73 vs stego p5=1.30)。v2 模型在训练分布内已"过激",JPEG 干净图即使 clip 也回不到训练分布内 位置。
✅ 根本修复:v2 训练集追加真实 JPEG 干净样本(2026-09)
把 data/campus_jpg/ 414 张真实 JPEG 干净图(campus PGM-derived 来源之外)作为
额外 clean 样本加入 v2 训练集,photo_id 独立区间 max_id+1000 ~ max_id+1413,
与原训练集完全 disjoint(防 group 泄漏)。重新训练 v2 4 模型 OOF stacking:
# 自动生成 v2 + JPEG 数据集 (campus_v2.csv + 414 JPEG clean -> campus_v2_jpeg.csv)
py src\_add_jpeg_clean.py
# 训练 (与 v2 同样的 5 折 GroupKFold + 4 模型 stacking)
$env:DS_FILES = "dataset_campus_v2_jpeg.csv"
py src\train_model.py
v2 特征可解释性(2026-09-06):
- v2 143d 特征组:LGB gain 占比 → LSB PREFIX 42.2%, BASE 21.6%, SRM 90 维合计 26.6%, PREFIX 9.6%
- 单特征 AUC 排名:20 段 LSB 前缀 + 11 维 BASE 全部 > 0.60;SRM 90 维平均单特征 AUC 仅 0.50~0.52(近随机)
- 结论:143d 中 ~73% 增益来自 31 个可解释特征;SRM 90 维是"压舱石",边际贡献微弱但提供 OOD 鲁棒性
双版本部署策略(2026-09-06)
经过可解释性对照实验,项目保留 143d 默认版 与 53d 可解释版 两套模型,各自适用场景不同:
| 维度 | 143d 默认版(stego_classifier.joblib) |
53d 可解释版(stego_classifier_v2_jpeg_lgb_51d.joblib) |
|---|---|---|
| 特征构成 | BASE 11 + SRM 90 + PREFIX 20 + LSB PREFIX 20 + TEX/EST 2 | BASE 11 + PREFIX 20 + LSB PREFIX 20 + TEX/EST 2(去 SRM) |
| 8-split 平均 AUC | 0.9085 | 0.9227(+0.014) |
| Held-out AUC (seed=0) | 0.8946 | 0.9100(+0.015) |
| 弱档检出 nsF5 p3 d=0.25 | 85.3% | 87.2% |
| OOD 鲁棒性 (真实 JPEG 干净 fp) | 1/8(median 0.011) | 3/8(median 0.028) |
| 模型文件大小 | 2.7 MB | 2.7 MB |
| 可解释性 | 一般(143 维,LIME/SHAP 可对单图解释) | 强(51 维有明确统计定义,可直接列 Top 贡献) |
| 推荐场景 | 通用部署 / 异构数据 / 真实图像 | 论文 / 答辩 / 教学 / 单图分析 |
核心结论:
- 53d 在 AUC 和弱档检出上全面胜出,因为 SRM 90 维是 LGB 中的"噪声特征"(平均单特征 AUC 仅 0.50~0.52)
- 但 143d 在 OOD 鲁棒性上更稳:SRM 高通残差对 JPEG 高频噪声有"过滤"作用,真实校园照片判错率更低
- 工程上保留双版本,默认加载 143d(稳健),可通过 GUI 选项切换 53d(可解释)
53d 精简实验明细
- 8 split 全部胜出(差异 +0.005 ~ +0.022):
seed 143d 53d diff 0 0.8946 0.9100 +0.0155 1 0.9228 0.9307 +0.0080 2 0.9166 0.9216 +0.0051 3 0.9083 0.9307 +0.0224 4 0.9263 0.9375 +0.0112 5 0.8933 0.9131 +0.0198 6 0.9196 0.9385 +0.0189 7 0.8866 0.8995 +0.0129 - 可解释优势:53 维中 51 维有明确统计含义
Rm/Sm/Rn/Sn/RS_Gr/RS_Gn:RS 分析 6 个规则翻转率chi2_pvalue/chi2_stat:LSB 卡方 p 值与统计量diff_entropy/lsb_diff_entropy:全局/LSB 位平面熵差median_prefix_p:20 段前缀卡方 p 中位数prefix_p1~p20:全图像 20 段卡方 plsb_prefix_p1~p20:LSB 通道 20 段卡方 ptexture_noise:图像纹理方差归一化est_rate:从 χ²p + 前缀 p 反推估计嵌入率
切换 53d 模式(代码示例)
import joblib
from ml_predict import MLPredictor
# 默认 143d
pred_143 = MLPredictor() # model_path=stego_classifier.joblib
# 切换 53d
pred_53 = MLPredictor(model_path='models/stego_classifier_v2_jpeg_lgb_51d.joblib',
clip_outliers=False) # 53d 不需要 clip(单特征 AUC 高,训练分布更稳)
# 同一张图
result_143 = pred_143.predict(img) # AUC 高 + OOD 稳
result_53 = pred_53.predict(img) # AUC 更高 + 可对每维特征解释
GUI 集成(规划)
src/gui.py 在 ML 模型加载处增加单选框:[●] 143d 默认(稳健) / [ ] 53d 可解释(AUC+)。
切换后:
- 143d 模式:与现状完全一致,部署推荐
- 53d 模式:增加"贡献特征"面板,显示 Top 5 特征 + 方向 + 强度,供研究/教学场景
| 模型 | 训练集 | Held-out AUC | 真实 JPEG 干净 prob | nsF5 p3 d=0.25 检出 |
|---|---|---|---|---|
| v1 XGB (旧默认) | dataset.csv (11维, 6档) | 0.7435 | 0.30 (正确) | 49% |
| v2 LR (未修复) | campus_v2 (143维, 12档) | 0.8278 | 1.00 (误判) | 25% |
| v2 XGB (旧默认) | campus_v2_jpeg (143维, 12档 + 414 JPEG clean) | 0.8723 | 0.14 (正确) | 15.6% |
| v2 XGB tuned | campus_v2_jpeg + 网格调优 (depth=5, n_est=500) | 0.8889 | 0.14 (正确) | 67.9% |
| v2 XGB + WEAK_WEIGHT=3 | campus_v2_jpeg + 弱档加权×3 | 0.8534 | 0.14 (正确) | 20.2% |
| v2 XGB + WEAK_WEIGHT=5 | campus_v2_jpeg + 弱档加权×5 | 0.8377 | — | 25.7% |
| v2 STACK (WEAK_WEIGHT=3) | campus_v2_jpeg + 4 模型 LR meta | 0.8321 | 0.10 (正确) | 32.1% |
| v2 LGB tuned (新默认) | campus_v2_jpeg + LGB 网格调优 (nl=31, ne=800, lr=0.03) | 0.8946 | 0.14 (正确) | 85.3% |
- 新默认
stego_classifier.joblib= v2 LGB tuned (num_leaves=31, n_estimators=800, learning_rate=0.03, min_child_samples=10)。 - 测试集 AUC 0.8946(vs XGB tuned 0.8889,+0.006);多 split 平均 AUC 0.9085(+0.007)。
- 弱档检出最强:nsF5 p3 d=0.25: 67.9% → 85.3%;nsF5 p2 d=0.35: 84.4% → 95.4%; matrix p3 d=0.40: 89.0% → 94.5%。LSB 三档全部 100% 检出。
- 真实校园 JPEG 干净照片 OOD 鲁棒性提升(8 张中 7 张 prob < 0.5;median 0.011 vs XGB tuned 0.18)。
- XGB tuned 已备份为
stego_classifier_v2_jpeg_xgb_tuned.bak.joblib。 - STACK 版 (
_weak3_stack.joblib) 仍保留供 OOD 严重场景切换(人工噪声 prob 0.24 vs LGB 0.99)。 - v1 11 维 XGB 保留为参考。
最终决策:项目保留双版本模型供不同场景使用:
版本 文件 适用 143d 默认版(稳健) stego_classifier.joblib通用部署 / 异构数据 / 真实图像(默认加载) 53d 可解释版(AUC+) stego_classifier_v2_jpeg_lgb_51d.joblib论文 / 答辩 / 教学 / 单图分析 143d 默认:LGB tuned(
num_leaves=31, n_estimators=800, learning_rate=0.03, min_child_samples=10), held-out AUC 0.8946、多 split 平均 AUC 0.9085、OOD 1/8 fp。 53d 可解释:同样超参,53 维特征(去 SRM),held-out AUC 0.9100、多 split 平均 AUC 0.9227、 OOD 3/8 fp(牺牲少量鲁棒性换 AUC+0.014 与完全可解释性)。 STACK 与 XGB tuned 仍保留供场景切换;v1 11 维 XGB 保留为参考。
GPU 版 (v1.2):PyTorch 批量向量化的统计特征分析
gpu/ 子目录提供一套 GPU(CUDA) 加速的隐写检测管线,复刻已验证的
11 维统计特征(RS、卡方、差分熵、LSB 熵、前缀中位 p,与 src/fsfeatures.py
参考实现 bit 级一致),把原来逐图 Python 循环(RS 逐组、20 段前缀卡方)
改写为 PyTorch 张量化算子,在 CUDA 上一批并行算完。
为什么是"特征法"而不是裸像素 CNN? 实测表明:在 414 张校园照片上, 从头训练的整图深度卷积网络(多架构/输入/正则组合)均无法跨照片泛化 (验证 AUC≤0.50)——有效独立样本只有照片数,弱 LSB 信号需数千张源图才能学稳。 统计特征法在相同数据上验证 AUC≈0.79,且特征提取可被 GPU 并行化, 因此 GPU 版选择加速这条真正管用的路径,而非裸 CNN。
管线
# 1) 生成数据集 (每张照片 1 干净 + 4 档含密变体, 完整 512x512, 不裁剪以保留统计)
python gpu\make_imageset.py [照片目录] [张数]
# 2) GPU 批量提取特征 + 按照片分组训练 + 评估
python gpu\train_ml_gpu.py # 输出 models\steg_classifier_gpu.joblib
# 3) 单张图像 GPU 检测
python gpu\predict_gpu.py <图像> [<图像>...]
实测 (RTX 4060 Laptop, 纯校园照片)
数据源已从 F:\DCIM\Camera 中去除 DIP4E 教材灰度 tif,改用纯校园照片目录
data/campus_jpg(仅 414 张 jpg)作为唯一数据源,CPU 与 GPU 两条管线均已全量重跑。
- GPU 统计特征管线:特征 2070 张 512² 灰度约 5s(≈397 img/s),GPU 利用率
峰值 99% / 平均 74%;验证 AUC≈0.790,acc≈0.802(Youden 阈值 0.713);逐档——
matrix p3≈99%、nsF5 p2 d0.95≈93%、nsF5 p2 d0.50≈89%、弱nsF5 p3≈64%、干净误报≈43%。 - CPU 特征管线:2898 样本(414 干净 + 2484 含密,6 档),CV 最佳为
LogisticRegression CV-AUC≈0.759;held-out AUC≈0.781、acc≈0.780;
matrix p2 d0.80/p3 d0.50检出≈100%/99%、nsF5 p2 d0.85≈95%、弱nsF5 p3 d0.25≈52%。 - 一致性:
python gpu\featurize_gpu.py自检,GPU 与 CPU 参考特征逐项一致 (RS 到 bit 级、浮点 ~1e-7)。
参考基准数据集 (BOSSbase 1.01) 重跑
用隐写分析领域事实标准基准 BOSSbase 1.01(官方 dde.binghamton.edu,1.67GB zip,
10,000 张 512×512 灰度 PGM,与管线工作尺寸完全一致)重新生成数据并重训
(make_imageset.py 已增补 *.pgm 支持)。
# 1) 下载解压至 data\BOSSbase_1.01\*.pgm (官方 zip 1.67GB)
# 2) 生成图像集 (本实验取前 2000 张源图 -> 10000 样本, 512x512)
py gpu\make_imageset.py F:\Steganography\data\BOSSbase_1.01 2000
# 3) 训练 (默认读 imageset.npz, 覆盖 steg_classifier_gpu.joblib)
py gpu\train_ml_gpu.py
- 数据:2000 源图 → 10000 样本(2000 干净 + 8000 含密,1干净+4变体),npz ≈1.57GB。
- 实测 (验证 2000 样本, Youden 阈值 0.798):AUC≈0.644,acc≈0.655;逐档——
matrix p3 d0.50≈80%、nsF5 p2 d0.95≈70%、nsF5 p2 d0.50≈68%、弱nsF5 p3 d0.30≈57%、 干净误报≈48%。 - 对比:相比校园照片基线(AUC≈0.767/0.79)下降,验证了文献公认结论——BOSSbase 经去马赛克加工、统计结构更强,弱密度 LSB 嵌入足印更弱,是更难的隐写分析基准; 远优于 CIFAR 32×32 补充实验(AUC≈0.555,已回退清理)。
- 原基于校园照片的
imageset.npz/steg_classifier_gpu.joblib已备份为imageset_photobase.npz/steg_classifier_gpu_photobase.joblib;BOSSbase 数据与 去 DIP4E 前的旧数据/模型同时备份于backup_20260905/。默认模型为纯校园照片版steg_classifier_gpu.joblib/stego_classifier.joblib。
BOSSbase 全量合并训练(校园 + BOSSbase 10,000 源图)
为最大化训练作用,将 BOSSbase 全量 10,000 张源图与校园照片合并训练,让单一 模型同时看到"自然校园 + 去马赛克基准"两类域。
- 数据生成:
make_imageset.py现以 memmap 逐张落盘(<base>_x.npy,避免大数组 一次性堆叠造成 OOM),支持--out / --id-offset / -n多源分工。py gpu\make_imageset.py data\campus_jpg --out campus # 校园 414 源 -> 2070 样本 py gpu\make_imageset.py data\BOSSbase_1.01 --out bossbase # BOSSbase 10000 源 -> 50000 样本 py gpu\train_ml_gpu.py # 合并两源训练(默认读取两源)
- 数据:校园 2070 + BOSSbase 50000 = 52070 样本(414+10000 源图,各 1干净+4变体)。
- 实测 (验证 10430 样本, Youden 阈值 0.795):AUC≈0.712,acc≈0.690;逐档——
matrix p3≈87%、nsF5 p2 d0.95≈79%、nsF5 p2 d0.50≈70%、弱nsF5 p3≈50%、干净误报≈41%。 - 对比:合并 AUC(0.712) 介于纯校园(0.790) 与 BOSSbase 单跑(0.644) 之间,符合数据 难度梯度——模型在更难基准与自然场景间取得平衡;GPU 特征提取 52k 张约 146s,GPU 满载。
CPU 全量合并训练(校园 + BOSSbase 全量)
CPU 版同样接入 BOSSbase 全量,与校园照片合并训练,覆盖 10,000 张基准源图。
py src\make_dataset.py data\campus_jpg --out campus # 校园 414 源 -> 2898 样本(1干净+6变体)
py src\make_dataset.py data\BOSSbase_1.01 --out bossbase # BOSSbase 10000 源 -> 70000 样本
py src\train_model.py # 合并两源训练(默认读取两源)
- 数据:校园 2898 + BOSSbase 70000 = 72898 样本(10414 clean + 62484 stego,11 维特征)。
- 5 折 GroupKFold CV(照片分组防泄漏):RandomForest CV-AUC≈0.745(最佳)、 XGBoost 0.744、LogisticRegression 0.736、GradientBoosting 0.732。
- held-out 测试(18228 样本):AUC≈0.741,acc≈0.685,bacc≈0.668,thr=0.834;
逐档检出——
matrix p2 d0.80≈99%、matrix p3 d0.50≈85%、nsF5 p2 d0.85≈73%、nsF5 p2 d0.35≈59%、 弱nsF5 p3≈44–55%;低误报点(thr=0.910) 干净误报≈11.6%、含密检出≈38.9%。 - 代价:CPU 全量特征提取 70000 行约 79 分钟(单进程串行,见下文并行改造说明)。
- 对比:CPU AUC(0.741) 略高于 GPU(0.712),符合 CPU 逐张 6 档、更多 stego 变体覆盖更强的预期。
多进程并行(已实现):
make_dataset.py内置多进程并行(-j / --workers N,默认 用满 CPU 核数)。每张图内 6 档 C++ 嵌入/特征有强数据依赖无法图内并行,但图与图 相互独立,按图分片交多个子进程并行处理、主进程流式合并。实测 100 张(700 样本): 单进程 53.5s → 16 核并行 9.5s,加速约 5.6 倍,多进程与单进程输出逐行一致。 注意每进程各自加载fsfeatures.dll,内存按核数倍增(单进程约 54MB)。 命令:python src\make_dataset.py <目录> --out x -j 15
依赖:
torch(CUDA)、numpy、Pillow、scipy、scikit-learn、joblib、nvidia-ml-py(可选,用于上报 GPU 利用率)。样本数据gpu/data/*较大(含_x.npy大数组),不入库,可随时重新生成。
同 CPU 版一样,GPU 检测器输出的是统计含密概率,弱密度嵌入应结合启发式判读 交叉印证。GPU 特征提取亦可作为大批量图片的批量分析入口复用。
持续集成 & 发版
- CI(
.github/workflows/ci.yml):任何对main的推送 / PR 都会自动运行test_core.py与test_steg.py(Python 3.9 / 3.11),并构建wheel + sdist。 - 自动发布:推送形如
v1.1.0的 tag 时,CI 会构建包并自动创建 GitHub Release, 附带wheel与sdist作为资产,同时自动生成发布说明。 - 发布流程:
# 提交改动并打 tag(本地)
git add -A && git commit -m "feat: v1.1"
git tag v1.1 && git push origin main --tags
版本历史
-
v1.4.0 (待发布) — 双版本 ML 模型
- 143d 默认版(
stego_classifier.joblib) — LGB tuned (num_leaves=31, n_estimators=800, learning_rate=0.03, min_child_samples=10)- Held-out AUC 0.8946,8 split 平均 0.9085
- OOD 鲁棒:1/8 fp(median prob 0.011),真实校园 JPEG 干净
- 训练数据:
data/dataset_campus_v2_jpeg.csv(143d,12 档变体 + 414 张 JPEG 干净)
- 53d 可解释版(
stego_classifier_v2_jpeg_lgb_51d.joblib) — 去 SRM 90 维- Held-out AUC 0.9100(+0.015),8 split 平均 0.9227(+0.014)
- 弱档检出全面优于 143d:nsF5 p3 d=0.25 87.2%、matrix p3 d=0.40 95.4%
- OOD 鲁棒:3/8 fp(牺牲少量鲁棒性换 AUC 与可解释性)
- 51 维有明确统计定义,可对单图输出 Top 贡献特征(论文/教学推荐)
- 关键发现:v2 143d 中
73% 增益来自 31 个可解释特征,SRM 90 维平均单特征 AUC 仅 0.500.52 (近随机),是 LGB 中的"噪声特征";但 SRM 残差对 JPEG 高频噪声有过滤作用,故 143d 在 OOD 上更稳
- 143d 默认版(
-
v1.3.1 (待发布)
- 补齐运行时依赖
matplotlib/joblib/scikit-learn/pandas(此前缺失导致 ML/绘图模块导入崩溃)
- 补齐运行时依赖
-
v1.3.0
- 新增矩阵编码演示面板(GUI):随机/可点击块 LSB,实时计算伴随式
s与目标m的差值d,在汉明校验矩阵H中定位命中的列并高亮被改系数, 执行修改后校验H·x==m。核心逻辑独立于src/matrix_demo.py。 - 新增隐写分析随载荷扫描面板:
payload滑条 0→0.4,逐档重新嵌入并实时刷新 卡方 p 值 / RS 估计嵌入率 / ML 含密概率三曲线 (单图无真 AUC,以 ML 概率作区分趋势示意)。逻辑位于src/scan_panel.py。
- 新增矩阵编码演示面板(GUI):随机/可点击块 LSB,实时计算伴随式
-
SRM 高通滤波预处理层
- 新增
src/srm_filter.py(30 个标准 SRM 核,numpy/torch 双实现,合成单通道增强图);make_dataset.py增--preprocess srm、featurize_gpu.extract_features_gpu增use_srm开关(train_ml_gpu增--srm on/off)。 - 同源校园:CV-AUC 0.7594→0.7922、测试 AUC 0.7811→0.8085,弱密度检出明显提升。
- 跨源合并(校园+BOSSbase 全量):CPU 测试 AUC 0.741→0.704、GPU 校园+BOSSbase2000源 验证 AUC 0.651→0.572,SRM 均下降,两管线相互印证(详见上文 SRM 小节"重要"段)。
- 结论与默认:默认模型=未 SRM 合并全量(≈0.741),SRM 单源校园(≈0.8085)另存为
stego_classifier_campus_srm.joblib备用。
- 新增
-
数据重跑(移除 DIP4E)
- 从数据源去除 DIP4E 教材灰度 tif,建立纯校园照片目录
data/campus_jpg(仅 414 jpg), CPU 与 GPU 管线全量重跑:GPU 验证 AUC≈0.790、CPU held-out AUC≈0.781; 旧数据/模型备份至backup_20260905/。
- 从数据源去除 DIP4E 教材灰度 tif,建立纯校园照片目录
-
v1.2.2
- GPU 数据集支持 jpg/tif 等多格式混合(
gpu/make_imageset.py),去掉默认 150 张上限、默认全量; 曾用加入 DIP4E tif 后的 682 张 / 3410 样本 重训(DIP4E 已于后续数据重跑中移除)。 - README 检测指标按数据集区分(自然照片 A:AUC≈0.79 / 含 tif 混合 B:AUC≈0.767); 模型为二进制、不入库,与 PyPI 上 ver1.2.1 明确区分。
- GPU 数据集支持 jpg/tif 等多格式混合(
-
v1.2.1
- 修复仅 1 个有效灰度对的强二值图(
letterA/B/T.tif)隐写分析lgamma(0)崩溃,返回中性 p 值。 - 新增 C++
nsf5_permute确定性置乱加速:4096² 置乱 524ms→210ms、整体嵌入约 540→281ms; DLL 缺失自动回退同算法 Python,编码/解码两端序列恒定可逆。 - GUI 绘图预览崩溃修复,并按屏幕尺寸 1:1 高质量展示。
- 修复仅 1 个有效灰度对的强二值图(
-
v1.2.0
- 新增 GPU 版(
gpu/):PyTorch 批量向量化复刻 11 维统计特征(RS/卡方/熵/前缀 p, 与 CPU 参考实现 bit 级一致),GPU 提取 2070 张特征 ≈5s、利用率峰值 99%。 - 新增 GPU 训练/推理管线
train_ml_gpu.py、predict_gpu.py;验证 AUC≈0.79。 - 实测与文档说明了"裸像素深度 CNN 需海量独立源图、局部特征法更适合小样本隐写检测"。
- 新增 GPU 版(
-
v1.1.0
- 新增 C++ 嵌入加速
cpp/nsf5embed.dll(修复汉明缓存越界;与 Python 像素级一致并经回环校验)。 - 监督学习升级:数据集扩展为 6 档密度变体、构建提速约 8→90 倍,
train_model.py改为 5 折 GroupKFold 交叉验证选模。 - 修复低误报阈值选择 bug(
threshold_for_fp取最低阈值而非最高,保障真实低误报检出率)。 - GUI 新增 判定灵敏度(严格 / 均衡 / 宽松),联动启发式与 ML 判决阈值。
- 新增 CI、Apache-2.0 License、构建与发版说明。
- 新增 C++ 嵌入加速
-
v1.0.0
- nsF5 伴随式矩阵编码 + 湿纸编码;图像哈希键控;盲隐写分析;GUI;码族与效率绘图。
- C++ 特征提取
cpp/fsfeatures.dll;一版有监督分类器(LR,AUC≈0.78)。
许可
Metadata
Release files for nsf5stego 1.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nsf5stego-1.4.0.tar.gz | 89.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nsf5stego-1.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 154.7 kB
Release files / nsf5stego-1.4.0.tar.gz
| Download URL | nsf5stego-1.4.0.tar.gz |
|---|---|
| Size | 89.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9ee546c001b9fd1df62be85852f2b2234f8079cb1296b755e313910c39f16e22
|
|
BLAKE2b-256 checksum How to use checksums |
64b9d6a9869e4f507d1adecc735609f8cea45e1eb267eef611d9c2188e64ff36
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.11
|
Release files / nsf5stego-1.4.0-py3-none-any.whl
| Download URL | nsf5stego-1.4.0-py3-none-any.whl |
|---|---|
| Size | 65.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bd4f641741fa6b13f73652c330d2f76804cffeb5fef0f98fecee6c819a0effd6
|
|
BLAKE2b-256 checksum How to use checksums |
ccf219b3fe304992a7431aada2e641f6820fffe63c58352c13d96a911b35f26a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.10.11
|