Skip to main content

nsF5 图像隐写工具 (Steganography)

CI version license python DOI

目录

English Overview

nsF5 Steganography is an open-source teaching and research toolkit for image steganography and steganalysis. It implements the classic nsF5 algorithm - Hamming syndrome-matrix coding combined with wet paper coding - plus blind steganalysis (chi-square and RS), SHA-256 content keying, and supervised machine-learning detection with two deployable LightGBM models.

The project is pure Python at its core and runs on Windows, Linux, macOS, and Colab. Optional C++ accelerators speed up feature extraction, embedding, and shuffling; build them with make cpp (or python scripts/build_cpp.py). They are never shipped as binaries - the same sources build a .dll, .so or .dylib as appropriate - and when they are absent the code falls back to equivalent pure-Python implementations, so notebooks, Docker, and cloud environments work out of the box. (Feature extraction is bit-identical between the two; the nsF5 embedder is interoperable but not pixel-identical -- see the note in the Chinese section.)

Highlights

  • Embedding / decoding - ASCII messages hidden in 8-bit grayscale or color images with password keying and self-synchronizing SHA-256 content hashing;
  • nsF5 core - binary Hamming codes [n=2^p-1, k, 3] with syndrome matrix embedding, F5-style magnitude decrease, and wet paper coding (no shrinkage, no retries);
  • Blind steganalysis - Westfeld chi-square and Fridrich RS analysis with a content-aware verdict and three sensitivity modes;
  • ML steganalysis - 11-D statistical features (v1) and 143-D v2 features (SRM residuals + prefix chi-square statistics), trained with photo-grouped cross-validation; ships both a 143-D robust model and a 53-D interpretable model;
  • Cross-platform - pure-Python fallbacks for features, embedding, and model inference; CI verifies Ubuntu, macOS, and Windows on every change;
  • Teaching-first - GUI matrix-coding animation, one-click Colab/Jupyter notebooks per chapter, dataset downloader, Docker/JupyterLab image, and a bilingual web handbook.

Quick Start (Linux / macOS / Windows)

git clone https://github.com/Yukinoshita-lin/nsf5-steganography.git
cd nsf5-steganography
python3 -m venv .venv
source .venv/bin/activate            # Windows: .venv\Scripts\activate
python -m pip install -e .

python src/test_core.py              # algorithm self-tests
python src/test_steg.py              # steganalysis self-tests
python src/run_e2e.py                # full embed -> decode -> analyze demo
nsf5stego embed cover.png -m "msg"   # CLI: embed / extract / analyze / gui
python src/gui.py                    # GUI (requires tkinter)

Or use the bundled Makefile: make install, make test, make e2e, make notebooks, make dataset, make docker.

Windows installer: no Python needed — download nsf5stego-setup-<version>.exe from the Releases page (Start-menu shortcut, optional PATH entry, Chinese wizard). See the Chinese section "方式 0:Windows 安装版".

Learning Resources

License

Apache-2.0 - see LICENSE and NOTICE.


针对 8bit 灰度/彩色图像 的隐写研究工具,实现了基于伴随式矩阵编码(二元汉明码) 的 nsF5 隐写算法,并附带盲隐写分析、图像哈希键控与码族/嵌入效率可视化。

项目核心为纯 Python(依赖 numpy/Pillow,GUI 使用标准库 tkinter),可在 Windows / Linux / macOS / Colab 直接运行。另提供可选的 C++ 加速库 (cpp/fsfeatures.cpp 特征提取、cpp/nsf5embed.cpp 嵌入热路径 + 确定性置乱 nsf5_permute):三平台同一份源码,用 make cpp 编译成 .dll/.so/.dylib (不入库,见 scripts/build_cpp.py)。库缺失时自动回退到同算法纯 Python 实现:嵌入/解码仍可逆、11-D/143-D 特征与双版本 ML 模型均可使用。

互动教学网站

GitHub Pages 首页已升级为交互式双语教学网站(不依赖手册即可动手理解项目):

  • 🔗 主站: https://yukinoshita-lin.github.io/nsf5-steganography/
  • 🌐 语言切换: 页面右上角一键中英切换,或使用 ?lang=zh / ?lang=en
  • 🧪 交互实验: LSB 位平面(可抽出单层观察 + 按权重叠加还原)/ LSB 嵌入→解码闭环
    • 卡方/RS 实时自检 + 改动像素掩码 / 汉明伴随式找位 / 湿纸干点求解(自动演示) / nsF5 全流程对比(朴素 LSB vs 项目真实 nsF5Pixel:矩阵编码 + 减幅修改 + 湿点避让, 改动统计 / PSNR / 卡方·RS 指纹一图对比,含解码往返与固定种子置乱路径)/ ML 阈值判别 / 载荷扫描(真实项目统计)/ 10 题双语自测计分
  • 🗺 教学层次: 首页"本页导航"把整页分成动手实验/原理/学习路径/FAQ+自测 四部分, 每节带部分徽章,末尾附双语 FAQ 手风琴答疑
  • 🖼 可视化: 16 张教学示意图,覆盖 cover↔stego、位平面分层加权叠加、统计直方图、 湿纸、效率曲线、ROC 与双模型对比
  • 📚 原手册仍保留: /zh 与 /en
  • 💻 本地体验: 仓库根目录 make webapp(即 python -m http.server 8080 --directory webapp) 后打开 http://127.0.0.1:8080。直接双击 webapp/index.html 时 file:// 下 fetch 本地 JSON 会被浏览器拦, 载荷扫描实验拿不到数据 —— 所以要起 http 服务
  • 🧪 自动化回归: webapp/tests/(DOM 冒烟 + 交互 + 桌面/移动布局), 由 .github/workflows/webapp-tests.yml 在每次改动 webapp/** 时执行

功能总览

模块 说明
嵌入 / 解码 将 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 载入图 → 嵌入/解码 → 分析 → 绘图 一体化界面

学习手册

项目提供中英文双语学习手册,从零基础开始,12 周快速入门;想深入可预留 6–12 个月(见手册附录 F 的完整路线):

姊妹项目:yccstego(pip install yccstego)—— 把 nsF5 搬到 JPEG 量化 DCT 系数(Y 通道)上的压缩域实现,含自写的 DCT/Huffman 编解码。 它不在本仓库内(独立仓库与 PyPI 包),是第 11 章与附录 F 推荐的下一步方向;手册里 提到它的地方都给出了地址。

涵盖: 数字图像基础 → Python 入门 → LSB 隐写 → 卡方/RS 分析 → 汉明矩阵编码 → F5/nsF5 → 湿纸编码 → 哈希键控 → 机器学习基础 → v1/v2 特征工程 → SRM 滤波 → 143d/53d 双版本模型 → C++/GPU 加速 → 综合实验。每章配有"动手做"实验与"想一想"思考题, 适合本科毕设自学。

模型双版本(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}

指标口径(重要):头条只引用 BOSSbase

同一个 143d 模型在 BOSSbase 上是 0.8062,在自建校园语料上是 0.8939 —— 差值反映 的是语料(难度、负样本构成都不同),不是模型强弱。对外引用、论文对比一律用 下面主表的 BOSSbase 数字:

语料 模型 AUC 协议 说明
BOSSbase 1.01(领域标准基准,头条口径) LGB-143d 0.8062 [0.7894, 0.8225] 按源图 holdout 对外一律引用这一行
LGB-53d 0.7172 [0.6943, 0.7406] 同上 同一份划分,可与 CNN 对比
LGB-11d 0.7128 [0.6913, 0.7360] 同上 11 维基线
校园照片(自建语料,附表) 143d 部署模型 0.8939;8-split 均值 0.8980 按源图 holdout 含 414 张真实 JPEG 干净图,与上表不可并列
53d 部署模型 0.8391;8-split 均值 0.8461 同上 同上

⚠ 2026-09-14 审计更正:校园语料此前的数字(0.8946 / 0.9100,8-split 0.9085 / 0.9227)含源图泄漏 —— 414 个 clean_jpeg 行被赋予了独立 photo_id,使"按源图划分"失效;同一批 GPU 产出的 SRM 特征尺度也与推理端 不一致。两处均已修复并重跑,详见 CHANGELOG 1.6.2。 修复后 143d 优于 53d(此前结论相反:SRM 90 维在错误尺度下才是"噪声")。

完整权威表(逐行标注语料 / 协议 / 是否可溯源)见 docs/RESULTS.md, 由 experiments/build_results_table.py 生成;README 内任何口径冲突都以它为准。

版本 文件 校园语料 AUC (8-split 均值) 弱档 nsF5 p3 d=0.25 推荐场景
143d 默认 stego_classifier.joblib 0.8980 50.0% 通用部署 / 真实图
53d 可解释 stego_classifier_v2_jpeg_lgb_51d.joblib 0.8461 41.4% 论文 / 答辩 / 教学(逐维可解释)

本表是校园照片语料(自建,含真实 JPEG 干净图)上的部署指标;与 BOSSbase 的数字分属不同语料,不能并列。

OOD(真实干净照片)误报率(2026-09-14 重建,1514 张 = 校园 414 + DIV2K 100 + ALASKA#2 1000,可追到 experiments/data/ood_summary.csv): 143d 9.58% [8.20, 11.16]、53d 28.86% [26.64, 31.20]。 旧的"1/8 / 3/8"只有 8 张样本、且生产者已丢失,已作废。 详见下文的 双版本部署策略 与 docs/RESULTS.md 第 5 节。

更正记录:曾经出现过的错误

这一节列的是本项目真实犯过、并且已经修复的错误。写在这里的动机很直接:隐写分析 项目如果连自己的评测数字都不可信,它的教学价值就是负的。逐条细节见 CHANGELOG.md 的 1.6.1–1.6.4,权威数字见 docs/RESULTS.md。

# 曾经的错误 影响(实测) 现状
1 源图泄漏:414 个 clean_jpeg 行被赋了独立 photo_id,而其特征与对应 clean 行逐位相同(是副本) "按源图划分"名存实亡 —— 同一张源图可以跨训练/验证两侧。单独修分组后 143d held-out AUC 0.8946 → 0.7555、弱档检出 85.3% → 44.2% 已修:experiments/add_jpeg_clean.py 让 photo_id 继承源图;train_deploy_models.py 开训前强制校验分组不变量
2 SRM 特征尺度不一致:GPU 端在高通滤波前把像素 /255,残差缩小 255 倍、clip 形同虚设 语料用 GPU 特征、单图推理用 CPU 特征,两者相差约 30 倍,143d 模型一直吃分布外输入。修好后 BOSSbase 143d 0.7529 → 0.8062 已修:CPU/GPU 143 维特征逐项一致(max|Δ|≈3e-5),并有回归测试与 CI job
3 C++ 与 Python 特征不一致,且被自检掩盖:卡方自由度用了 n 而非 n−1(p 值差 26%)、20 段中位数取"上中位";自检把它误诊成"MinGW lgamma 精度偏移",容差放宽到 0.2 Windows(带 DLL)与 Linux(纯 Python)对同一张图给出不同的 chi2_pvalue / median_prefix_p 已修:11 维逐项一致(max|d|≈6e-14),自检容差收回 1e-9
4 结论建立在错误数据上:曾写"53d 精简版 AUC 更高""SRM 90 维是噪声特征、去掉反而更好" 该结论完全来自第 1、2 条缺陷 已推翻并重跑:143d 更准(8-split 0.8980 vs 0.8461);SRM 占 LightGBM gain 52.6%,去掉它 OOF AUC 掉 0.05
5 OOD 数字只有 8 张样本("1/8、3/8"),且产生它的脚本与数据一起丢失 无法复核,也没有统计意义 已重建 experiments/ood_eval.py:1514 张真实干净照片,143d 9.58%、53d 28.86%(Wilson 95% CI)
6 同一指标名跨语料/跨协议混用:"8-split" 指过两种协议;校园语料数字与 BOSSbase 数字被并列比较 读者会看到互相矛盾的数值 已修:docs/RESULTS.md 把语料与协议做成每行必填字段,并规定 README 头条只引用 BOSSbase
7 多处数字只打印不落盘(GPU 管线 0.790/0.644/0.712、train_model 家族、gain importance) 不可溯源,无法复核 已修:四个生产者补齐并重跑,不可溯源行 9 → 0(0.7903 / 0.6438 / 0.8889 等逐位复现)
8 部署模型不可复现:仓库里没有能产出两个随包 .joblib 的脚本 clone 之后无法重建模型,"可用但不可复现" 已补 experiments/train_deploy_models.py,并在全部 5796 个样本上验证其产出与随包模型预测一致
9 教学手册带着已被推翻的结论(DOCX、网页版、PDF 三处都有) 教学材料带错结论比没有结论更糟 已修:teaching/handbook_facts.py 统一口径并纳入 CI;PDF 按新口径重新导出(中文 67 页 / 英文 74 页)
10 教学 notebook 从未被执行过 03 号让学员嵌入 5000 字符,而封面图容量只有 3494 字节 —— 这个 cell 一直在抛 ValueError 已修并进 CI:10/10 逐本执行通过,另有"入库 notebook 与生成器一致"的漂移检查
11 LICENSE 缺 APPENDIX 段,结尾被换成自定义版权块 GitHub 把 Apache-2.0 识别成 NOASSERTION,与徽章不符 已恢复标准 Apache-2.0 全文
12 若干使用即踩的缺陷:python src/run_e2e.py 在中文 Windows 控制台崩溃(✓ 无法用 GBK 编码);make_dataset 打印的样本数恒比真实值多 1;train_model 遇到空环境变量直接崩溃;README 引用过从未存在的 src/_add_jpeg_clean.py;wheel 安装示例版本过期 使用者直接踩到 均已修复,并新增控制台编码护栏测试
13 wheel 里没有模型文件:两个部署模型只在仓库里,pyproject 没有把它们打进包,而 ml_predict 也只按仓库布局找路径 pip install nsf5stego 之后依赖里装着 lightgbm、README 写着有 ML 判定,但 MLPredictor.available 恒为 False(真实验证:安装布局下 FileNotFoundError) 已修(2026-09-15):models/ 以 nsf5_models 包打进 wheel,ml_predict 按"仓库布局 → 安装布局"查找;CI 的 build 作业在干净 venv 里断言模型可用并能打出概率
14 事实校验器把一处陈旧引用放过去了:图 9-2 的图注写着"论文图 / thesis figure",而论文稿 2026-09-14 已删除;FORBIDDEN 用的是 "(论文图" / "(thesis figure"(左括号紧贴),实际文本是 "(log–log 坐标,论文图)",子串匹配被绕过 —— 于是这句话同时留在 DOCX、网页版和入库 PDF 里 教学材料指向不存在的文件;而"进了 CI 就不会再犯"的假设也因此不成立 已修:匹配放宽到 "论文图" / "thesis figure"(另禁 "project thesis",注意不能用裸 "thesis" —— 会命中 "hypothesis"),三处共 6 处修正并重新导出 PDF(67 / 74 页);PDF 的导出步骤也补成了脚本 teaching/export_handbook_pdf_word.py(make handbook-pdf)
15 手册把姊妹项目说成本项目的一部分:中英文 ch03 / ch11 / 附录 F 都写"项目 yccstego 扩展",而 yccstego 是独立仓库与 PyPI 包(pip install yccstego),本仓库里没有它的代码、也没有任何链接 读者按手册去找,什么也找不到 —— 与第 14 条同类:教学材料指向不存在的东西 已修(1.7.1):中英各 5 处改成"姊妹项目 yccstego"并给出仓库地址,README 增"姊妹项目"一行,PDF 重新导出(英文 75 页);handbook_facts.py 的 REQUIRED 加上该地址,三份材料缺它就红

为什么保留这些记录,而不是悄悄把数字改掉: 第 1 条和第 3 条恰好是"评测设计本身 出错"的两个典型样本 —— 前者说明"按源图分组"这种纪律会在 id 分配这种细节上悄悄失效, 后者说明一个被误诊的容差可以把两种实现的差异藏住很久。它们现在是教学材料的一部分 (见第 7、8 章与 docs/RESULTS.md 第 1 节)。

提交署名的一次清理(2026-09-14)

用 AI 编程助手协作时,它会在提交信息里自动追加 Co-Authored-By: Claude Code <noreply@anthropic.com> 这类尾注。GitHub 会把它当成 共同作者显示在提交流里 —— 与"贡献者列表"不同(本项目贡献者 API 一直只有仓库 所有者),但同样显眼。

git bundle create .git/backup.bundle --all        # 先全量备份
python scripts/strip_ai_trailers.py < msg > new   # 或直接用下面的 msg-filter
git filter-branch -f --msg-filter \
  'python scripts/strip_ai_trailers.py' -- <起点>^..HEAD
git push --force-with-lease origin main

以后不会再发生:.githooks/commit-msg 会在提交时当场剔除这类尾注 (启用一次:make hooks,即 git config core.hooksPath .githooks),CI 的 attribution job 则对整段历史兜底检查。


安装与运行

方式 0:Windows 安装版(推荐普通用户)

到 Releases 下载 nsf5stego-setup-<版本>.exe 双击安装(简体中文向导,每用户免管理员), 装完后像普通桌面软件一样使用,不需要 Python:

  • 开始菜单(可选桌面快捷方式)→ nsF5 隐写工具,双击即开图形界面;
  • 向导里勾选"加入用户 PATH"后,nsf5stego embed / extract / analyze 命令行 直接可用(重开终端生效),卸载时自动从 PATH 移除;
  • 产物(含密图 / 效率图)写在 %APPDATA%\nsf5stego\output,不污染安装目录;
  • 免安装选择:同一 Release 的 nsf5stego-portable-<版本>-win64.zip 解压即用;
  • 首次运行若遇 SmartScreen 提示,点"仍要运行"(项目未做代码签名)。

方式 A:从源码运行(Windows / Linux / macOS 通用)

git clone https://github.com/Yukinoshita-lin/nsf5-steganography.git
cd nsf5-steganography
python3 -m venv .venv
source .venv/bin/activate          # Linux / macOS
# Windows: .venv\Scripts\activate
python -m pip install -e .         # 或只装 numpy pillow
nsf5stego --help                   # 命令行界面(embed / extract / analyze / gui)
python src/gui.py                  # 图形界面(需要 tkinter)

跨平台说明:无需任何预编译二进制——fsfeatures、cppembed、 featurize_v2 与 ml_predict 在检测不到 cpp/ 下的库时自动使用纯 Python 实现;想要加速就 make cpp(需要 g++/clang++)。仅 GUI 需要系统自带 tkinter(Ubuntu/Debian:sudo apt install python3-tk)。 也可用仓库根目录的 Makefile:make test、make cpp、make e2e、make notebooks。

方式 B:安装打包的模块(wheel)

每个版本会以源码包发布,可构建并安装:

# 构建 wheel + sdist(需已安装 build)
python -m build

# 安装 wheel(核心模块:ns5_core / steganalysis / gui 等;文件名带版本号)
pip install dist/nsf5stego-<版本>-py3-none-any.whl

注意:wheel 仅含纯 Python 核心;C++ 加速库需自行 make cpp 编译,缺失时 自动回退纯 Python。两个部署模型(models/*.joblib)已打进 wheel (安装为 nsf5_models/,含模型卡),ml_predict 会按"仓库布局 → 安装布局" 的顺序查找,所以 pip install 之后 ML 判定直接可用:

from ml_predict import MLPredictor
p = MLPredictor()
assert p.available, p.load_error      # 缺 lightgbm/pickle 版本不符时这里会说明原因
print(p.predict(img))                  # {probability, verdict, threshold}

(2026-09-15 修正:此前 wheel 里没有模型文件,安装后 available 恒为 False。)

命令行界面(1.8.0 起)

安装后除 GUI 外还有一条与 GUI 参数一一对应的命令行(服务器 / 脚本 / 批量 场景不再需要自己拼 ns5_core 调用):

nsf5stego embed cover.png -m "秘密文本" -p 口令 -o stego.png
cat msg.txt | nsf5stego embed cover.png            # 文本也可从 stdin 传入
nsf5stego extract stego.png -p 口令                # 方法/p/口令 须与嵌入一致
nsf5stego analyze stego.png --sensitivity 宽松 --json   # 盲分析, --json 供脚本解析
nsf5stego analyze *.png                            # 批量: 逐图一行汇总
nsf5stego gui                                      # 图形界面(与 python src/gui.py 相同)
  • 退出码:成功 0;运行失败(容量超限 / 口令不匹配 / 图像无法读取)1; 参数错误 2 —— 脚本可直接判断成败。
  • 解码失败不输出乱码:口令或 方法/p 不匹配时解出的会是无效文本,CLI 统一 以非零码退出并提示"请确认 方法/p/口令 与嵌入时一致",而不是把替换字符打印出来。
  • analyze 批量语义:单图 --json 输出对象,多图输出对象数组(每项带 image 键);批量里某张图读不出来会记为 error 条目继续跑完,整体以 非零码退出。通配符(如 *.png)由 CLI 自己展开 —— Windows 的 shell 不 展开,这条示例在三大平台都能直接用。单图行为与 1.8.0 初版完全兼容。
  • 默认输出 <原名>_stego.png;目标文件已存在时会在 stderr 明示覆盖。
  • 源码运行的等价形式:python src/cli.py <子命令> ...。
  • GUI 产物目录:源码运行写 仓库/output/;pip install 安装后 GUI/效率图 自动改写当前工作目录的 output/(不会写进 site-packages 或解释器目录)。

运行测试

python src/test_core.py    # 核心算法自测(嵌入/解码 + 汉明矩阵 + 湿纸 + 口令)
python src/test_steg.py    # 盲隐写分析自测(区分 干净/含密 图)
python src/run_e2e.py      # 端到端验证(嵌入→保存→解码→分析→绘图)
python src/test_gui.py     # GUI 冒烟测试(构建窗口/载入/预览)
# 全套(模型卡契约 / OOD 与实验链冒烟 / README 目录 / 权威表同步;条数见 CI 日志)
python -m pytest -q
make coverage              # 同上 + 覆盖率报告(门槛 65%,当前约 69%)

GUI 使用流程

  1. 点击 载入原始图 / 含密图 选择 8bit 图像。
  2. 选择 算法(nsF5 或 matrix)、参数 p(块比特数,越大效率越高)、可选口令。
  3. 在文本框中输入待嵌入的 ASCII 字符串。
  4. 点击 1 嵌入并保存 → 生成 output/stego_*.png,右侧预览含密图。
  5. 点击 2 解码提取 → 从含密图还原字符串(须与嵌入使用相同 算法/p/口令)。
  6. 点击 3 分析 → 显示 SHA256、卡方统计、RS 缺口、估计嵌入率与隐写概率。
  7. 点击 生成码族与效率图 → 弹出理论 vs 实测效率对比图。

解码与嵌入参数(方法/p/口令)必须一致;口令或图像内容不匹配将无法正确解码。


目录结构

nsf5-steganography/
├── README.md / CHANGELOG.md / LICENSE / NOTICE
├── Makefile                 # install/test/e2e/notebooks/docker/web 快捷命令
├── docker-compose.yml
├── cpp/                     # 可选 C++ 加速库源码(make cpp 编译,缺失时纯 Python 回退)
├── data/                    # 数据集 CSV 与 campus_jpg/BOSSbase 目录
├── docker/Dockerfile        # Linux JupyterLab 教学镜像
├── gpu/                     # PyTorch 批量特征 / GPU 训练
├── img/                     # 示例封面图
├── models/                  # 训练出的分类器 joblib
├── notebooks/               # 10 个按章 Colab/Jupyter Notebook
├── scripts/                 # 数据集下载、网页资源生成等脚本
├── src/
│   ├── ns5_core.py          # nsF5 核心:汉明码、湿纸、哈希键控、嵌入/解码
│   ├── cppembed.py          # C++ 嵌入封装(含纯 Python 回退)
│   ├── py_features.py       # 纯 Python 11 维特征(跨平台)
│   ├── featurize_v2.py      # v2 143 维特征
│   ├── srm_filter.py        # SRM 高通滤波(numpy/torch)
│   ├── fsfeatures.py        # C++ 特征绑定(DLL 缺失回退)
│   ├── steganalysis.py      # 卡方 + RS 盲隐写分析
│   ├── ml_predict.py        # 143d/53d 双版本模型推理
│   ├── make_dataset.py / train_model.py
│   ├── gui.py / efficiency.py / image_io.py
│   └── test_*.py            # 回归测试
├── teaching/                # 教学文档、Notebook 生成器、Jupyter Book 站点
├── webapp/                  # 交互式双语教学网站(Pages 首页)
└── output/                  # 运行产物(gitignore)

技术细节

伴随式矩阵编码(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.cpp), 可显著降低逐图统计开销;嵌入热路径同样提供 C++ 版(cpp/nsf5embed.cpp, cppembed.py 封装)。 并用真实校园照片做有监督训练,得到一个可部署的分类器。

关于"两条路径是否一致":特征提取逐位一致(fsfeatures 与 py_features 在 RS/卡方/熵上误差 <1e-6,卡方 p 值因 MinGW 的 lgamma 半整数精度有约 0.02 的容忍度)。嵌入路径则不是逐像素一致:matrix 逐像素相同;nsF5 在 p≥3 时会有几十个像素不同(65536 中约 8–32 个),因为湿纸编码在多个等价解中 挑选哪一个 —— C++ 按固定下标顺序扫,Python 按种子派生顺序扫。两者都是合法解, 各自回环正确,且能互相解码。契约由 src/test_pipeline.py::test_cpp_python_embed_contract 固定。

训练管线

# 1) 用 data/campus_jpg 下照片批量生成 干净/含密 特征数据集
#    (每张降采样 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 缩放→特征提取)送入模型。

模型从哪来(重要):两个部署模型 models/stego_classifier.joblib(143d 默认) 与 models/stego_classifier_v2_jpeg_lgb_51d.joblib(53d 可解释)已随仓库分发, clone 之后 ML 判定即可用(需要 lightgbm,已列为依赖)。

模型可复现:experiments/train_deploy_models.py 就是这两个文件的生产者, 口径已冻结:

  • 语料 data/dataset_campus_v2_jpeg.csv(414 张校园照片 × 14 = 5796 样本)
  • 按源图划分 GroupShuffleSplit(test_size=0.25, random_state=seed)
  • LightGBM learning_rate=0.03, num_leaves=31, n_estimators=800, min_child_samples=10, subsample=0.9, colsample_bytree=0.8

复现证据(2026-09-14 审计后重跑):143d 在 seed=0..7 上的 AUC 为 0.8939 / 0.8998 / 0.8987 / 0.8972 / 0.8892 / 0.9009 / 0.9145 / 0.8901 (均值 0.8980),53d 为 0.8391 / 0.8326 / 0.8531 / 0.8589 / 0.8408 / 0.8500 / 0.8615 / 0.8330(均值 0.8461);随仓库分发的两个 .joblib 就是该脚本的产物。

生产者会在训练前检验分组不变量(每个 photo_id 的 variant 集合必须一致), 语料一旦出现"孤儿 id 块"就直接拒绝运行 —— 这正是 1.6.2 修掉的那类源图泄漏。 语料本身的生成链见 python experiments/add_jpeg_clean.py(JPEG 干净行的 photo_id 继承源图,且是真实 JPEG 往返而非 clean 行的副本)。

可复现性的边界(如实说明):校园语料的 414 张照片是作者本人的 data/campus_jpg/(不入库,也无法分发)。因此两个部署模型的训练语料 不能从零重建——脚本链(make_dataset.py → add_jpeg_clean.py → train_deploy_models.py)是完整的,但需要自备同规模的 JPG 照片目录。 想完全从零复现,请走 BOSSbase 路线(scripts/download_datasets.py + experiments/featurize_bossbase_npz.py + experiments/sota_compare.py), 那条链只用公开基准,docs/RESULTS.md 的主表就是它。

python experiments/train_deploy_models.py             # 重训并覆盖 models/*.joblib(约 3 分钟)
python experiments/train_deploy_models.py --no-splits # 只跑 seed 0(约 10 秒)
python experiments/train_deploy_models.py --no-save   # 只评估,不落盘
# 单独用 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% 测)
DS_FILES="dataset_campus_v2.csv" python 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)
python experiments/add_jpeg_clean.py

# 训练 (与 v2 同样的 5 折 GroupKFold + 4 模型 stacking)
DS_FILES="dataset_campus_v2_jpeg.csv" python src/train_model.py

v2 特征可解释性(2026-09-14 审计后重算):

  • 单特征 AUC(校园 v2_jpeg 语料,取 max(AUC, 1-AUC) 的中位数): BASE 11 → 0.610, PREFIX 20 → 0.646, LSB PREFIX 20 → 0.607, SRM absmean → 0.512, SRM std → 0.550
  • 特征消融(5 折 GroupKFold OOF, 8 seed 均值): B11 0.8708 → 53d 子集 0.8513 → 完整 143d 0.9010,即 SRM 90 维贡献 +0.050 AUC; 按源图 GroupKFold(8) 上 143d 对 53d 是 8/8 全胜
  • 结论(与此前相反): SRM 90 维在尺度正确时不是噪声。2026-09-06 那版结论 ("SRM 近随机、去掉反而更好",gain 占比 SRM 26.6% / LSB PREFIX 42.2% 等)是在 GPU 端把像素先 /255 的错误尺度上算出来的,已作废。 现已按当前口径重算(experiments/gain_importance.py):SRM 90 维占 gain 的 52.6%,BASE 11 占 20.7%、LSB PREFIX 20 占 20.6%、PREFIX 20 占 6.2%; Top-5 特征为 Rm(7.6%) / lsb_prefix_p4(7.0%) / srm_absmean_c29(6.1%) / RS_Gr(5.5%) / srm_std_c29(4.6%)。旧表(SRM 26.6%)已作废。

双版本部署策略(2026-09-14 审计后更新)

经过可解释性对照实验,项目保留 143d 默认版 与 53d 可解释版 两套模型,各自适用场景不同:

语料提醒:本节(以及下方"模型对比""最终决策")的 AUC 全部来自校园照片 语料(自建,含 414 张真实 JPEG 干净图)。同一族模型在 BOSSbase 1.01 上是 0.8062(143d)/ 0.7172(53d),两者不可并列。权威表见 docs/RESULTS.md,头条口径见上文"指标口径"一节。

维度 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.8980 0.8461
Held-out AUC(校园语料, seed=0) 0.8939 0.8391
弱档检出 nsF5 p3 d=0.25 50.0% 41.4%
特征消融(5 折 OOF, 8 seed 均值) 0.9010 0.8513
模型文件大小 2.8 MB 2.8 MB
可解释性 一般(143 维,LIME/SHAP 可对单图解释) 强(51 维有明确统计定义,可直接列 Top 贡献)
推荐场景 通用部署 / 异构数据 / 真实图像 论文 / 答辩 / 教学 / 单图分析

模型卡(2026-09-15):两个模型各有一份入库的 JSON 模型卡 —— 语料与协议、held-out / 8-split 指标、逐档检出率、OOD 误报率、特征列表与顺序、 超参、训练环境、适用边界与已知局限,以及该 .joblib 的 sha256。 入口:models/README.md(说明)与 models/*.card.json(机器可读)。 校验:python experiments/model_card.py --check —— 它检查卡片与二进制是否脱钩, CI 里随 pytest 一起跑;重训后由 experiments/train_deploy_models.py 自动刷新。

核心结论(审计后):

  • 143d 在 AUC 与弱档检出上更好:去掉 SRM 90 维后消融 OOF AUC 从 0.9010 掉到 0.8513
  • 53d 的价值在可解释性:53 维里 51 维有明确统计含义,可逐维列出贡献;代价是 AUC 低约 0.05
  • 此前"53d 全面胜出、143d 靠 OOD 稳"的结论建立在两个缺陷之上(SRM 尺度错误 + 语料源图泄漏),见 1.6.2
  • 工程上保留双版本,默认加载 143d(更准),教学/答辩场景切 53d(可解释)

53d 精简版定位(审计后更正)

  • 按源图 GroupKFold(8) 的 OOF:143d 0.8966 vs 53d 0.8470 —— 143d 8/8 全胜 (此前记录的是"53d 7/8 胜", 那是错误尺度 + 泄漏语料的产物)
  • 可解释优势: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 段卡方 p
    • lsb_prefix_p1~p20:LSB 通道 20 段卡方 p
    • texture_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 更高 + 弱档检出更强
result_53 = pred_53.predict(img)    # AUC 略低, 但可对每维特征解释

GUI 集成(规划)

src/gui.py 在 ML 模型加载处增加单选框:[●] 143d 默认(稳健) / [ ] 53d 可解释(AUC+)。 切换后:

  • 143d 模式:与现状完全一致,部署推荐
  • 53d 模式:增加"贡献特征"面板,显示 Top 5 特征 + 方向 + 强度,供研究/教学场景

下表是校园照片语料上的历史记录。2026-09-14 已把其中"只打印不落盘"的配置 全部重跑并落盘(experiments/data/train_model_metrics.csv),所以下面的数字现在 都有产物可查;重跑值与原值并列,差异来自修正后的特征口径与语料。 与 BOSSbase 的数字不可并列。

模型 训练集 Held-out AUC 真实 JPEG 干净 prob nsF5 p3 d=0.25 检出
v1 XGB (旧默认) dataset.csv (11维, 6档) 0.7435 → 重跑 0.7371 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.8889 0.14 (正确) 67.9%
v2 XGB + WEAK_WEIGHT=3 campus_v2_jpeg + 弱档加权×3 0.8534 → 重跑 XGB 0.8832 / LR 0.9055 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.8672 0.10 (正确) 32.1%
v2 LGB tuned (新默认) campus_v2_jpeg + LGB 网格调优 (nl=31, ne=800, lr=0.03) 0.8939 见下 50.0%

重跑还暴露一件事:在当前(修正后的)语料上,LR(标准化 + 校准)常常是最强的 单模型(0.9055),高于调优后的 XGB(0.8889)与 STACK(0.8672)。这与旧口径 "XGB/LGB 更强"的印象相反,值得在下一轮实验里单独查清。

  • 新默认 stego_classifier.joblib = v2 LGB tuned (num_leaves=31, n_estimators=800, learning_rate=0.03, min_child_samples=10)。
  • 现口径(2026-09-14 重跑,可追到 experiments/data/deploy_model_metrics.csv): held-out AUC 0.8939、8-split 平均 0.8980 ± 0.0074;弱档 nsF5 p3 d=0.25 检出 50.0%、 nsF5 p2 d=0.35 71.2%、matrix p3 d=0.40 74.0%;验证集干净误报 27.9%。
  • 表内"测试集 AUC 0.8946 / 弱档 85.3%"等为审计前记录,含源图泄漏与 SRM 尺度错误,不再引用。
  • 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 可解释版 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.8939(校园语料)、8-split 平均 0.8980、弱档检出 50.0%。 53d 可解释:同样超参,53 维特征(去 SRM),held-out AUC 0.8391、8-split 平均 0.8461、 弱档检出 41.4% —— 用约 0.05 AUC 换"每一维都能解释"。 两者在 BOSSbase 上的同族数字是 0.8062 / 0.7172(头条口径,见上文"指标口径")。

⚠ 上述 AUC 均为校园语料(自建,更容易)。同一族模型在 BOSSbase 1.01 上是 0.7529 / 0.7172 —— 对外引用、论文对比请用后者,见 docs/RESULTS.md。 两个模型现已可由 experiments/train_deploy_models.py 现场复现(逐位一致)。 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, 纯校园照片)

数据源已从 data/campus_jpg 中去除 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)。

    2026-09-14 复核:这一行已可复现(experiments/data/gpu_pipeline_metrics.csv, --datas imageset --srm off 得到 AUC 0.7903、阈值 0.7130、acc 0.8024)。 注意它对应 --srm off;默认 --srm auto(= on)在 imageset 上只有 AUC 0.6544 —— 两条口径不同,数字不可混用。

  • 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)。注意该自检走 use_srm=False;而 gpu/train_ml_gpu.py 默认 --srm on,训练时先做 SRM 高通预处理再过 11 维 —— 与 CPU 侧"原始图直接提特征"不是同一口径,两边数字不可直接并列。

参考基准数据集 (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 nsf5-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%、

    2026-09-14 复核:这一行已可复现(--datas imageset_bossbase --srm off → AUC 0.6438、阈值 0.7983、acc 0.6550)。 干净误报≈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%。

    2026-09-14 复核:本仓库现有的 BOSSbase 图像集只有 10000 样本(旧数是 50000 样本的全量集),因此无法逐位复现 0.712。用现有数据重跑合并配置 (3410 + 10000 = 13410 样本, --srm off)得到 AUC 0.6672、阈值 0.7921、 acc 0.6776,见 experiments/data/gpu_pipeline_metrics.csv。

  • 对比:合并 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。 pytest 作业另外守着模型卡与二进制不脱钩(src/test_model_cards.py: sha256 + payload + 指标交叉核对)、手册事实与代码片段、notebook 可执行。 浏览器回归(含 axe 无障碍审计)在 webapp-tests.yml 里单独跑。
  • 自动发布:推送形如 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.8.2 (当前) — 一个 Release 里出现了两个不同的 wheel

    • 审计 v1.8.1 产物时发现 PyPI 的 wheel 与 Release 里的同名 wheel 哈希不同: ci.yml(Linux)与 release.yml(Windows)各构建了一次,后者以同名文件 覆盖了前者。两份的成员文件内容一致,只有 METADATA/RECORD 与时间戳不同, 但"一个版本一份字节"的溯源因此断了。
    • 现在 Windows 作业不再上传 wheel,Release 的 wheel/sdist 一律来自 ci.yml (与 PyPI 同源同字节),它只提供 setup exe 与便携 zip。
  • v1.8.1 — 审计 1.8.0:发布链路的可验证性

    • 审计发现 v1.8.0 的 tag CI 是红的(PyPI 作业走 trusted publishing,而 pypi.org 端从没配 pending publisher),修复只落在 tag 之后的提交上;不过 PyPI 上的 wheel 与 Release 里的那个字节相同(sha256 02d2177a…), 产物确实来自 tag 那次构建。
    • 打包链此前只在 tag 上跑,所以它前两次真实运行(v1.8.0)才暴露问题。 现在 release.yml 支持 workflow_dispatch 预演,且手动运行不会发布; 新增安装器静默安装/卸载冒烟(装到临时目录、不选任务 → 断言用户 PATH 没被动过 → 跑装好的 CLI 往返 → 静默卸载 → 断言目录清空)。
    • ci.yml 改最小权限(workflow 级 contents: read,只有 release 作业 write; 去掉已不需要的 id-token: write),并在 build 作业里真的调用安装后的 nsf5stego(--version / embed→extract / analyze --json)—— 此前 CI 只验"模型随包可用",没验过发布入口本身。
    • docs/PACKAGING.md 的体积与验收按 CI 产物更正(85.9 / 113.4 MiB), PATH 还原的措辞按安装器真实行为收紧;make help 加了完整性护栏 (这条漂移犯过两次);.zcodeignore 收进 .gitignore。
  • v1.8.0 — 命令行界面

    • 此前 pip install 之后唯一的入口是弹 tkinter 窗口(nsf5stego = "gui:main"), 服务器、脚本与批量场景只能自己 import ns5_core 拼代码。1.8.0 起入口改为 src/cli.py:embed(文本可 -m 或 stdin)/ extract / analyze(卡方 + RS
      • ML,--json 机器可读)/ gui 四个子命令,参数与方法/p/口令和 GUI 一一对应; 退出码区分 成功(0)/运行失败(1)/参数错误(2),解码失败与容量超限都以明确提示 收场而不是栈回溯或乱码。nsf5stego gui 与 python src/gui.py 行为不变, 且在 tkinter 缺失 / 无显示环境时给出可行动提示。
    • 另有三处小体验: embed 的 stdin 文本按 UTF-8 优先解码(cp936 控制台管道 传中文不再乱码嵌入); GUI 标题栏显示版本号; make webapp 一条命令本地起 交互实验室。
    • 安装布局修复: pip install 后 GUI 的产物目录原来是解释器根下的 output/(系统 Python 直接 PermissionError),现在自动改写当前工作目录; analyze 支持多图批量(逐行汇总, --json 数组, 坏图容错); GUI 演示图 缺失时按 seed=42 确定性配方一键生成。
    • GUI 蓝白"国企风"改版: 深蓝横幅 + 白色卡片 + 主操作深蓝按钮, 统一浅蓝边框与微软雅黑字体; 演示面板同风格、教学语义配色不变; 顺手修复灵敏度提示叠字与窗口高度不足两处布局问题。
    • 测试:src/test_cli.py 十五个用例走真实子进程 + 进程内 stdin 解码单测, 另新增 src/test_pathutil.py(输出目录两种布局),CLI 控制台输出同时受 GBK 静态护栏约束。
  • v1.7.2 — 同类隐患的全仓排查

    • 1.7.1 修掉 OOD 评估的 fork 死锁之后,把这类隐患全仓扫了一遍:进程池只有三处 (ood_eval 已修、make_dataset 本次修、video_engine_v2 本来就是 spawn), DataLoader 默认 num_workers=0。src/make_dataset.py 改为显式 spawn + --pool-timeout 断路器,并补两条慢测试:workers=2 与 workers=1 逐行一致、 超时必须以非零码报错 —— 这条多进程分支此前从未被执行过(CLI 冒烟只看 --help)。
    • 给部署模型的生产者补上第一条端到端冒烟测试(自造 143 维小语料跑完整条链), 它第一次跑就抓到:youden_threshold 会返回 inf(roc_curve 的首个阈值就是 inf,弱可分数据上 argmax 常落在那里)——阈值成了 inf 之后,模型对任何图都不判 含密且不报错。两处实现都已加 np.isfinite 过滤;随仓库分发的两个模型没踩到 (0.9493 / 0.9595 都是有限值),重跑真实语料与入库指标逐项一致。
    • 手册里的"置换加速 263 倍"被更正:它与项目自己的 bench_permute.csv 对不上, 而且自己给的耗时(12.3 s / 0.223 s)算出来也只有约 55 倍。现在重跑基准并统一为 "4096²:15.6 s → 0.24 s(约 65×),小 N 处最高约 240×,数据见 experiments/data/bench_permute.csv",网页 / DOCX / PDF / 视频脚本七处一致, 并让 handbook_facts.py 守住(263 列入禁词、产物名列入必填)。
  • v1.7.1 — 教学材料的悬空引用

    • 手册(中英文 ch03 / ch11 / 附录 F)把 yccstego 写成"项目 yccstego 扩展", 但它是独立仓库与 PyPI 包(pip install yccstego),本仓库里没有它的代码 —— 读者按手册去找会一无所获。现已改为"姊妹项目 yccstego"并给出仓库地址, README 新增"姊妹项目"一行;handbook_facts.py 把该地址列进 REQUIRED, DOCX / 网页 / 入库 PDF 三份材料缺它即 CI 红。入库 PDF 重新导出(英文 74 → 75 页)。
    • OOD 评估进 CI:experiments/ood_eval.py 产出 README 的头条数字,此前从未 在 CI 里跑过(要 1514 张外部照片),1.6.9 新增的 --workers 并行路径更是零覆盖。 新增 src/test_ood_smoke.py:用合成照片跑通这条链,钉住"判据=payload 阈值""并行 与单进程逐位一致""fp_rate/Wilson CI/ALL 行自洽"。
    • 备用 PDF 路径补字形:xelatex 那条路径此前有 22 个符号(① ᵖ ₄ 等)在字体里 没有字形,日志里只有一行 Missing character、退出码仍是 0,两份 PDF 各有 80 余处 会变空白;现已逐个映射并让脚本缺字形时返回非零。
    • 一次 CI 挂死事故的修复:新加的 OOD 冒烟测试在 pytest 进程里 fork 出进程池 (Linux 默认),与已初始化的 LightGBM/OpenMP 线程池撞成死锁,让 pytest 作业从 1 分 44 秒变成挂满 6 小时。现在 ood_eval.py 显式用 spawn、每个分片带超时 (卡住即报错)、冒烟测试改跑 CLI 子进程,CI 各作业也设了 timeout-minutes 兜底。
  • v1.7.0 — 模型治理:模型卡

    • 两个随仓库分发的 .joblib 此前是裸二进制:语义只存在于 README 的散文 与 pickle 的 payload 里,读 payload 得先装齐依赖再反序列化,而且 "模型换了、文档没换"没有任何护栏。现在每个模型配一份入库的 JSON 模型卡 (models/*.card.json):语料与协议、指标(held-out / 8-split / 逐档检出 / 真实照片误报率 / 跨语料参考)、特征列表与顺序、超参、训练环境与 git 版本、 适用边界、不适用场景、已知局限,以及 sha256。
    • 新增契约测试 src/test_model_cards.py(sha256 硬绑定 + payload 逐字段核对 + 有数据时与 experiments/data/*.csv 交叉核对),随 pytest 进 CI; 生产者 experiments/train_deploy_models.py 在落盘后自动刷新模型卡, make model-cards / make model-cards-check 是本地入口。
    • 卡里如实写下边界:143d 在 1514 张公开真实干净照片上误报 9.58%、53d 28.86%(DIV2K 那 100 张是 51% / 47%),因此两者都不适合单独定案; 对外引用请用 BOSSbase 口径(0.8062 / 0.7172),而不是校园语料的 0.8939 / 0.8391。
  • v1.6.1–v1.6.9 — 审计修复、发布链路与性能

    • 逐版记录见 CHANGELOG.md。要点:源图泄漏与 SRM 特征尺度 两处缺陷的修复(1.6.2)、DOI/PyPI 发布链路与仓库治理(1.6.6)、 覆盖率测量口径的三次修正(1.6.7/1.6.8)、SRM 残差向量化与 OOD 并行 (20 分钟 → 1.7 分钟)以及彩色输入的训练/推理偏差修复(1.6.9)。
  • v1.6.0 — 可验证性加固

    • CNN 对比实验补上真实实现与真实数据:gpu/train_cnn.py 与 gpu/models/{xunet,yenet}.py 此前并不存在(论文引用的路径是悬空的), 而 sota_compare.py 会把 53 维请求静默降级成 11 维、仍标成 LGB-53d。 现在特征列缺失即报错,feat_set 与 dataset 分列,所有基线共用同一份 按源图划分与同一套按源图 bootstrap 的置信区间。实测(BOSSbase,按源图 holdout):Ye-Net 0.9541、LGB-143d 0.7529、LGB-53d 0.7172、LGB-11d 0.7128、 Xu-Net 0.5007(未收敛,训练集 AUC 也是 0.50,故不构成"CNN 不如手工 特征"的证据)。CNN 的实现与训练入口在 gpu/train_cnn.py 与 gpu/models/。
    • 两个部署模型入库:models/stego_classifier.joblib(143d) 与 models/stego_classifier_v2_jpeg_lgb_51d.joblib(53d),并补上此前缺失的 lightgbm 依赖 —— 否则 clone 后 ML 判定仍是 available=False。
    • C++ 改为源码跨平台编译:不再入库任何二进制,make cpp 三平台各自 产出 .dll/.so/.dylib;CI 现在真的编译并执行 C++ 一致性自检, 随后再删掉产物验证纯 Python 回退路径。
    • 测试全量进 CI:新增 pytest job 与无头 GUI job(xvfb);test_gui.py 此前不在任何 workflow 里;test_false_positive.py 里一处 ok = ok 恒真 赋值让整个假阳性测试变成空断言。
    • 浏览器回归测试真正跑起来:webapp-tests.yml 此前未被提交,且即使提交 也会因 python 命令与 npmmirror 镜像源而失败。
    • 教学视频质检修复:两张质检图不可用(一张 33 字节空图、一张缺失), 根因是 qa_sheet() 在无抽帧时间时静默写出零高度 PNG 并中断后续章节。
    • 已知缺口(如实记录,已于 v1.6.1 关闭):两个部署模型当时没有仓库内的 生产者,src/train_model.py 训不出它们 —— 现由 experiments/train_deploy_models.py 补齐(见本文件"模型从哪来"一节)。
    • 注:实验数据(experiments/data/、data/dataset_*.csv)按项目约定不入库 (可重新生成)。本版新增的 CNN 实现放在 gpu/。
  • v1.5.0 — 学习手册发布 + Zenodo DOI

    • 发布中英文学习手册(PDF)至 docs/:
      • docs/学习手册-从零读懂nsF5隐写项目.pdf (中文, 12 周快速入门路线, 深入版见附录 F, 67 页)
      • docs/Learning-Handbook-From-Zero-to-nsF5-Steganography.pdf (英文, 12-week quick-start roadmap, deep 6–12 month track in Appendix F, 74 页)
    • 涵盖 v1.4.0 全部新特性: 143d/53d 双版本 ML 模型、SRM 高通滤波、特征可解释性分析
    • 零基础: 从"像素与二进制"到"LGB 分类器超参调优"的完整学习路径
    • 新增 Zenodo 存档 DOI 徽章
  • v1.4.0 — 双版本 ML 模型 ⚠ 本节数字已于 2026-09-14 审计作废 (源图泄漏 + SRM 特征尺度错误;现口径见 CHANGELOG 1.6.2: 143d held-out 0.8939 / 8-split 0.8980,53d 0.8391 / 0.8461,且 143d 优于 53d)

    • 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 上更稳
  • 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。
  • 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/。
  • 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 明确区分。
  • v1.2.1

    • 修复仅 1 个有效灰度对的强二值图(letterA/B/T.tif)隐写分析 lgamma(0) 崩溃,返回中性 p 值。
    • 新增 C++ nsf5_permute 确定性置乱加速:4096² 置乱 524ms→210ms、整体嵌入约 540→281ms; DLL 缺失自动回退同算法 Python,编码/解码两端序列恒定可逆。
    • GUI 绘图预览崩溃修复,并按屏幕尺寸 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 需海量独立源图、局部特征法更适合小样本隐写检测"。
  • 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、构建与发版说明。
  • v1.0.0

    • nsF5 伴随式矩阵编码 + 湿纸编码;图像哈希键控;盲隐写分析;GUI;码族与效率绘图。
    • C++ 特征提取 cpp/fsfeatures.dll;一版有监督分类器(LR,AUC≈0.78)。

许可

本项目基于 Apache License 2.0 发布,详见 LICENSE 与 NOTICE。

Metadata

Release files for nsf5stego 1.8.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nsf5stego 1.8.2
File Size Uploaded
nsf5stego-1.8.2.tar.gz 2.5 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for nsf5stego 1.8.2
File Interpreter ABI Platform
nsf5stego-1.8.2-py3-none-any.whl Python 3 none any Details

Total release size: 4.9 MB

Release files / nsf5stego-1.8.2.tar.gz

Download URL nsf5stego-1.8.2.tar.gz
Size 2.5 MB
Tags Source
SHA-256 checksum
How to use checksums
28157e6f832ad860683c13d8ab753aaf65cff9f5b82fc0693a3b290b7bcce84e
BLAKE2b-256 checksum
How to use checksums
7fe52fe1ace6768a8d121d238ab33de7d2d8b355aa64c46f1f414f6065fa4165
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / nsf5stego-1.8.2-py3-none-any.whl

Download URL nsf5stego-1.8.2-py3-none-any.whl
Size 2.4 MB
Tags Python 3
SHA-256 checksum
How to use checksums
339d3bba54df7d6e17a9e86fedf945187bc523d8a8dd847b89df48a51ac2f61d
BLAKE2b-256 checksum
How to use checksums
866ade630ae83cfae236711bf1ffe757a326a9c5c3d0b912c2334c2d0a678f5d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

1.9.1

2 release files

1.9.0

2 release files

1.8.4

2 release files

1.8.3

2 release files

This release

1.8.2 This release

2 release files

1.8.1

2 release files

1.8.0

2 release files

1.4.0

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page