copy_move op• 数据种类:image2d → table
• 调用: import fullseye as fs; fs.ledger.copy_move_regions(image, method: 'str' = 'keypoint', min_matches: 'int' = 4, min_offset: 'float' = 16.0, offset_tol: 'float' = 2.0, ratio: 'float' = 0.6, patch: 'int' = 11, block: 'int' = 8, step: 'int' = 1, n_dct: 'int' = 10, min_variance: 'float' = 0.0001, max_feature_dist: 'float' = 0.02, neighbours: 'int' = 2, ransac_thresh: 'float' = 3.0, ransac_iters: 'int' = 300, seed: 'int' = 0) -> 'list'(要直接调用实现,import imgforensics; imgforensics.copy_move_regions(image, method: 'str' = 'keypoint', min_matches: 'int' = 4, min_offset: 'float' = 16.0, offset_tol: 'float' = 2.0, ratio: 'float' = 0.6, patch: 'int' = 11, block: 'int' = 8, step: 'int' = 1, n_dct: 'int' = 10, min_variance: 'float' = 0.0001, max_feature_dist: 'float' = 0.02, neighbours: 'int' = 2, ransac_thresh: 'float' = 3.0, ransac_iters: 'int' = 300, seed: 'int' = 0) -> 'list';从台账取用则 opsimgforensics.get("copy_move_regions"))
单张图像内 复制-粘贴(自我复制)区域的配对。table。
method="keypoint"(默认)
用 features.harris_corners 取角点,
用 features.describe_patches 生成归一化补丁描述子,
以离自身 min_offset px 以上的最近邻和 Lowe 的比率检验建立对应
(_self_match)。按平移向量把对应归束,对每个群
用 mosaic.proj_match_points_ransac 确认几何一致性。
相似变换用 fit_transform.vector_to_similarity(Umeyama)拟合,
放入 similarity。
method="block"
Fridrich, Soukal & Lukáš 2003。以 step px 的步长取 block 见方的
重叠块(默认 step=1 —— 这不是摆设。参见下文“步幅”),
以各块 DCT 的低频 n_dct 个系数为特征并按字典序排列,
把字典序上相近的最多 neighbours 个作为候选,只对特征距离
在 max_feature_dist 以下的按平移向量计数。
对旋转、缩放无效,但在角点少的图像上比 keypoint 法能捡到更多。
方差小于 min_variance 的块会被丢弃(把均匀的天空复制到天空上特征也相同 = 检测器必然产生的假阳性的主因)。
把步幅(step)设为 1 的理由(由实测决定):块方法能找到“相同特征”,只有在复制源与复制目标落在同一网格上时。设为 step=4 时,平移不是 4 的倍数的复制(例如 (110, 128)) 原理上一次也不会匹配。实测中 step=4 连 1 个真实平移也没返回,反而返回了 60 个假群。step=1 时真实平移排在第 1 群。大图像太重时可以提高 step,但要清楚提高后就只能找到步幅倍数的平移,再去提高。
返回区域对的 list(按对应数从多到少)。各元素:
offset 平移 (dy, dx)(row, col)。方向按位置归一化
(按字典序为正的方向)—— 若按下标决定,同一复制会
既是 (110, 128) 又是 (-110, -128)(实测后修正)
n_matches 该群的对应数
n_inliers RANSAC 的内点数(method="block" 时与 n_matches 相同)
inlier_ratio 内点率 src_bbox / dst_bbox (r0, c0, r1, c1) src_points / dst_points (N, 2) 的 (row, col)
similarity 用 Umeyama 拟合的 3x3(仅 method="keypoint",没有则为 None)
method 使用的方法
caveats 此结果无法断言的事
因为手头有正确答案,所以能命中这一点已用数值固定 (tests/test_imgforensics.py::test_copy_move_finds_the_known_offset,把 256x256 纹理图像 (40, 32) 处的 64x64 复制到 (150, 160) = 真实平移 (110, 128)):
============ ================= ============ ========= ==============
method 第 1 群的 offset n_matches 群数 误差
============ ================= ============ ========= ==============
keypoint (110.0, 128.0) 15 1 0 px
block (110.0, 128.0) 3249 1 0 px
============ ================= ============ ========= ==============
假阳性也测过:在未篡改的同类图像 3 张上,keypoint 法、block 法的群都是 0 个(seed 4/5/6)。
无法断言的事(都在同一测试中测过):
• 均匀区域(天空、墙壁)即使不复制特征也相同。在上半为均匀“天空” 的(未篡改)图像上,min_variance=1e-4 与 1e-6 都是 0 个,但设为 min_variance=0 时会出现 264 对的假群 1 个 (平移 (1, -247) = 天空中随意的对应)。
• method="keypoint" 用的是归一化补丁描述子,所以对旋转无效。把复制旋转后贴入,群数为 0 度 1 个 → 5 度 0 个 → 15 度 0 个 → 30 度 0 个。similarity 实际上不会带着旋转返回。
• method="block" 也对旋转完全无效(5 度时 0 个)。
• 检测为零 ≠ 没有复制。中间经过再压缩的复制,特征距离会拉长而超过 max_feature_dist。实测(把同一复制图像过一遍 JPEG 后检测):
========== ============================ ==========================
质量 keypoint 第 1 群的 n_matches block 第 1 群的 n_matches
========== ============================ ==========================
无压缩 15 3249
95 10 138
85 7 10
75 7 0 件(无群)
========== ============================ ==========================
平移在所有质量下都保持 (110.0, 128.0),是正确的。坏掉的是 “能否找到”,而不是找到时的答案。
• 示例数据目录(下载 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
table 作为输入)copy_move)—
*Provenance: imgforensics.py — IMGFORENSICS 算子登记表。本条目由 tools/opdocs.py md 自动生成(请勿手工编辑)。*
© 2026 Kazufumi Furuse — Fullseye operator documentation. Licensed under Apache-2.0.