jpeg_ghost_quality — IMGFORENSICS compression op

• 数据种类:images → image2d

• 调用: import fullseye as fs; fs.ledger.jpeg_ghost_quality(ghosts, qualities=None) -> 'np.ndarray'(要直接调用实现,import imgforensics; imgforensics.jpeg_ghost_quality(ghosts, qualities=None) -> 'np.ndarray';从台账取用则 opsimgforensics.get("jpeg_ghost_quality"))

用法

幽灵图序列 → 逐像素残差最小的质量的地图。image2d。

省略 qualities 时假定为 jpeg_ghost_map 的默认值(40..95 step 5) —— 张数不一致时为 ValueError(不返回下标与质量错位的图)。

返回的是以质量本身为像素值的 (H, W),所以值域不是 [0, 1]。显示时请自行归一化(本 op 返回的是数值,而不是图片)。

★ 合成后哪怕保存一次,这个 argmin 就不能用了。 2026-09-06 测得。背景 q92、素材 q60,在 128x128 中贴入 32x32:

==================== ================== ================

条件 粘贴部的众数质量 背景的众数质量

==================== ================== ================

合成后不保存 60(正确) 95

合成后以 q95 保存 95(与背景相同) 95

==================== ================== ================

原因在于扫掠的残差随质量单调递减 —— 若最后的保存质量位于扫掠的上端,argmin 就会在所有像素上粘在那里(实测:保存后的残差平均从 230.7 → 5.9e-02 一路直线下降)。实际的篡改必然要经过保存,所以这种读出在真实图像上会变成常数图。现有测试 test_jpeg_ghost_finds_the_pasted_quality 能通过,是因为合成后一次也没有保存(它本身是正确的单元测试,argmin 的实现也没有错 —— 只是读出方式不适合实际场景)。

替代方案:扫掠方向上残差曲线的谷的深度(局部凹陷)。在同一实验中逐像素取 ROC,argmin 读出为 AUC 0.500(与随机相同),谷深读出则为 AUC 0.997 / 假阳性 1 % 时检出率 0.944 (examples/poc_forensics_roc.py)。即便如此,把整体以 q60 重新保存后就会降到 0.475 —— “JPEG 幽灵对后处理很脆弱”才是正确的概括。

参考(示例数据、文献)

• 示例数据目录(下载 URL / 许可证) —— 2-D 用 skimage.data(BSD/公有领域)加合成图,3-D 给出真实数据源(Stanford/PDS 等)的下载 URL。

• 算子来历与参考文献 —— 该算子族所依据的研究/方法出处。

• 算法的正典(作者、年份)与用途见上面的族使用指南。

可运行的示例(实际调用该算子并已验证的样例)

• image_forensics_audit — py -3.11 examples/image_forensics_audit.py

• poc_forensics_roc — py -3.11 examples/poc_forensics_roc.py

类型可衔接的下一个算子(可接受 image2d 作为输入)

perceptual_hash · fingerprint_correlate · error_level_map · jpeg_quality_estimate · jpeg_ghost_map · noise_inconsistency_map · copy_move_regions · watermark_embed

同类别(compression)

error_level_map · jpeg_quality_estimate · jpeg_ghost_map


*Provenance: imgforensics.py — IMGFORENSICS 算子登记表。本条目由 tools/opdocs.py md 自动生成(请勿手工编辑)。*

© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.