Tommy

AI 图像篡改识别技术分析与工程验收防造假系统设计

· 14 min read
···阅读reads

作者注:本文面向”工程项目质量检测验收照片”这一具体场景,从技术原理、工程方案、实施路径三个维度系统梳理。文末给出可落地的系统架构与关键代码示意,可作为后续产品立项的技术预研基础。


引子:为什么传统”看图识假”已经不够用了

工程验收造假,过去多半是把张三项目的照片提交成李四项目的——靠人眼复审、抽查现场就能识破七八成。但近三年,造假手段有了三次代际升级:

第一代是 PS 时代——P 个时间戳、改个进度、抹掉缺陷。这一类现在用基础的图像取证技术能识别八九成。

第二代是 AI 局部编辑时代——Stable Diffusion inpainting、阿里通义万相、即梦 AI 等工具可以”补全”一张照片:原本没装好的设备 P 上去、缺失的标识牌补出来、墙面裂缝抹平、积水擦掉。这一类用传统取证已经吃力,因为 AI 生成的像素分布更”自然”,没有粗暴拼接的痕迹。

第三代是 AI 全图生成时代——给一句话:“拍一张 35kV 变电站设备安装完成的工地现场照片”,几秒钟出图,连 EXIF 都能伪造。再过 PS 一遍降清晰度、加点噪点、压缩成手机拍的样子,肉眼几乎不可能识别。

我们要建的系统,必须能同时应对这三代攻击。而且——这是最关键的一点——单纯做”事后检测”打不过攻击者。攻击者只要拿到一张验收合格的照片样本,就可以用对抗训练的方式,反复迭代生成”能骗过你检测器”的伪造图。真正可行的策略是把战线前移,让造假在源头就难以发起。

本文按这个思路展开,分五部分:

  1. 威胁建模:工程验收场景的造假手法与动机分析
  2. 技术派系综述:当前 AI 图像篡改检测的核心方法
  3. 三道防线模型:前置防御、被动检测、流程审计
  4. 系统架构设计:模块划分与数据流
  5. 关键技术实现:核心算法的代码示意

一、威胁建模——工程验收场景的造假手法图谱

做任何安全系统,第一步都是”把对手画清楚”。脱离场景谈技术容易做出花架子。工程验收照片的攻击者画像和动机,跟其他场景区别很大。

1.1 攻击者画像

工程验收造假的实施者,主要是三类人:

项目经理 / 现场工程师——动机是赶进度、掩盖缺陷、节省整改成本。技术水平中等,会用基础 PS 和市面上的”修图 App”,越来越多人开始尝试 AI 生图工具。这是最大量的一类。

分包商 / 施工队负责人——动机是冒名顶替(用别处的照片应付)、伪造未完成工序。技术水平参差,但有一个共同特征:手头有大量真实的工程现场照片素材库,很容易做”乾坤大挪移”。

项目甲方对接人 / 监理(少数)——动机是配合施工方完成虚假验收。这一类最危险,因为他们处于审核位置,能看到检测系统的反馈,会做对抗性优化。

理解这三类画像后,系统设计上就能取舍:80% 的精力对付前两类(量大、技术中等),20% 设计兜底机制对付第三类(用流程而非技术防御)。

1.2 典型造假手法分类

按伪造程度从轻到重排列:

手法 A:元数据篡改(无图像变化)

手法 B:图像复用(图像本身真实)

手法 C:传统局部 PS

手法 D:AI 局部编辑(inpainting)

手法 E:AI 全图生成 + 后处理

手法 F:物理空间造假(图像端无法识别)

把这六类列清楚,就清楚我们的检测系统需要”多个互补的检测器+流程兜底”,而不是”一个万能模型”。

1.3 攻击成本与系统响应等级

手法攻击者技术成本工具门槛占比估计系统应对优先级
A 元数据篡改极低一个 App~40%P0(必做)
B 图像复用无需技术~25%P0(必做)
C 传统 PSPS / 美图秀秀~15%P1
D AI 局部编辑Stable Diffusion / 在线 AI 工具~12%P1(增长快)
E AI 全图生成中高文生图工具~5%(增长中)P2
F 物理造假现场操作~3%流程层应对

注意一个反直觉的事实:最高级的 AI 攻击占比反而最低,最低级的元数据篡改占比最高。这是因为造假者也讲性价比,能用一个 App 解决的事不会去训练 GAN。所以系统第一阶段不要盲目追求最先进的 AI 检测能力,把基础的元数据校验和图像指纹查重做扎实,能解决 65% 以上的问题。


二、技术派系综述——AI 图像篡改检测的核心方法

了解威胁后,看看技术工具箱里有什么。当前主流方法可以分为五大派系,各有所长。

2.1 信号级取证(Signal-level Forensics)

这是最经典的一派,关注图像底层信号特征。在 AI 生成时代之前,是图像取证的主力。

ELA(Error Level Analysis,误差水平分析)

原理:JPEG 是有损压缩,每次保存都会丢失一些信息。如果图像某个区域被编辑过并重新保存,这个区域的压缩误差会与未编辑区域不同。把图像重新保存为高质量 JPEG,与原图相减,被篡改区域会”亮”起来。

from PIL import Image, ImageChops, ImageEnhance

def ela_analysis(image_path, quality=90):
    """误差水平分析"""
    original = Image.open(image_path).convert('RGB')

    # 重新以指定质量保存
    resaved_path = '/tmp/resaved.jpg'
    original.save(resaved_path, 'JPEG', quality=quality)
    resaved = Image.open(resaved_path)

    # 计算差异
    ela_image = ImageChops.difference(original, resaved)

    # 增强对比以便观察
    extrema = ela_image.getextrema()
    max_diff = max([ex[1] for ex in extrema])
    if max_diff == 0:
        max_diff = 1
    scale = 255.0 / max_diff

    ela_image = ImageEnhance.Brightness(ela_image).enhance(scale)
    return ela_image

ELA 能发现传统 PS 痕迹,但对 AI 生成图像基本失效——AI 生成的图像整体压缩特征一致,没有”修补区域”的概念。

CFA 模式分析(Color Filter Array)

原理:相机传感器使用 Bayer 阵列采样,每个像素只采集一种颜色,其他两个通道由邻居插值得来。这形成了一种独特的 CFA 模式。一旦图像被编辑或重新生成,CFA 模式会被破坏。

噪声残差分析(PRNU - Photo Response Non-Uniformity)

原理:每台相机的传感器都有独特的噪声指纹(像人的指纹一样)。同一台相机拍的照片有相同的 PRNU 模式。如果一张图的某区域 PRNU 模式与其余区域不同,就是被替换或合成的痕迹。

PRNU 在过去十年是法医级取证的金标准。但它对 AI 生成图基本无效——因为整张图都没有真正的相机传感器,自然也没有 PRNU 模式(缺失反而成了一种特征,下文 2.4 节会讲)。

频域分析(DCT/FFT)

原理:篡改和压缩会在频域留下痕迹。例如双重 JPEG 压缩会在 DCT 系数直方图上出现周期性模式。

这一派对手法 A、C 有效,对 D、E 效果有限。在 2024 年之前是主力,现在更多作为”基础体检”模块存在。

2.2 语义级检测(Semantic-level Detection)

这一派用深度学习模型直接学习”什么是篡改痕迹”,不依赖底层信号特征。

ManTra-Net(2019):基于 CNN 的端到端篡改定位网络,输出像素级篡改概率图。是早期的代表作。

MVSS-Net(2021):多视角监督,融合边缘流和噪声流,对拼接和复制粘贴有较好效果。

TruFor(2023):当前学界 SOTA 之一。融合 RGB 信息与噪声残差,输出全局篡改概率 + 像素级定位 + 可信度图。在 CASIA、Coverage、NIST16 等公开数据集上表现领先。

HiFi-Net(2023):分层细粒度检测,能区分篡改类型(splicing / copy-move / inpainting / AIGC)。

这一派的优势是端到端、对多种攻击有较强泛化能力;劣势是需要大量标注数据、对训练集外的新型攻击(如未见过的扩散模型)泛化不稳定,且容易被对抗样本欺骗。

2.3 生成式取证(Generative Forensics)

针对 AI 生成图像的专用检测,是 2023 年以来的热点。

核心思路:AI 生成图像(无论 GAN 还是扩散模型)虽然像素分布”看起来自然”,但在频域、隐空间、生成痕迹上仍有可识别特征。

频域特征法:GAN 生成图在傅里叶谱上常有明显的网格状高频伪影;扩散模型生成图也有特定的频域分布。代表方法:Wang 等人的 CNN-generated images are surprisingly easy to spot…(CVPR 2020)。

重建误差法:训练一个扩散模型对图像做”反向加噪+去噪”重建。真实照片重建误差大,AI 生成图重建误差小——因为生成模型本身就熟悉它生成的分布。代表方法:DIRE(ICCV 2023)、AEROBLADE(CVPR 2024)。

视觉-语言模型法(2024-2025):用 CLIP、BLIP 等大模型的特征空间判别真伪。优势是泛化好(CLIP 见过海量真实和 AI 图);劣势是计算开销大。

生成器指纹法:每个具体的生成模型(SD 1.5、SDXL、Midjourney、即梦 AI)都有独特的”模型指纹”。已有研究能识别图像由哪个具体模型生成。这一思路对溯源很有价值。

2.4 主动取证(Proactive Forensics)

前面三派都是”被动取证”——拿到一张图,分析它是不是假的。主动取证是另一条路:在图像生成或拍摄时就嵌入可信信号,事后验证这个信号是否完整。

C2PA / Content Credentials(Adobe 主导,已成行业标准):在图像创建时签名,记录来源、编辑历史。Photoshop、Adobe Express、Leica M11-P 相机已原生支持。每次编辑都会在元数据中留下加密链。

数字水印:在图像中嵌入肉眼不可见但算法可读的水印。先进方法(如 StegaStamp、RoSteALS)能在打印-翻拍后仍存活。

可信硬件:手机/相机硬件级签名。Sony、Leica、Canon 都在推。

对工程验收场景的关键启发: 我们完全可以强制要求用专用 App 拍照,在 App 内嵌入:

这样一来,任何”非 App 拍摄”的图都直接拒收。这比试图检测每一张图是不是假的,要高效得多。这是我们后面架构设计的核心思路之一。

2.5 多模态一致性检测

最新的一派,思路是”看图像跟其上下文是否自洽”。

例如:图像 EXIF 说在北京拍的,但图像中的车牌是沪A;图像说是冬天但树都是绿的;图像声称是某变电站但背景建筑不对——用 VLM(视觉-语言模型)+ 知识图谱可以检测这类不一致。

对工程场景特别有价值:可以核对图像内容与 BIM 模型、施工进度计划、地理围栏的一致性。

2.6 派系小结

派系适用攻击手法准确率计算开销对抗鲁棒性
信号级取证A、C 部分
语义级检测C、D中-高
生成式取证D、E中-高
主动取证全部前置防御高(前提是受控)
多模态一致性B、E、F 部分

没有银弹。任何严肃的防造假系统都必须组合使用多派系方法。下面进入正题——怎么把这些技术拼成一个能用的系统。


三、三道防线模型——前置防御、被动检测、流程审计

工程验收场景有一个绝佳的设计自由度:我们可以规定提交流程。这意味着不必只做”被动检测器”。把战线分成三道,攻击成本会被指数级抬高。

┌────────────────────────────────────────────────────────────┐
│  第一道防线:前置防御(拍摄端)                              │
│  - 强制专用 App 拍摄                                         │
│  - 拍摄时签名(设备+时空+操作行为)                          │
│  - 屏蔽"从相册导入"通道                                       │
│  目标:阻断 80% 的低/中级攻击                                 │
├────────────────────────────────────────────────────────────┤
│  第二道防线:被动检测(提交后服务端分析)                       │
│  - 元数据校验                                                │
│  - 图像指纹查重(跨项目、跨时间)                              │
│  - 信号级取证                                                │
│  - AI 生成检测                                               │
│  - 篡改区域定位                                              │
│  目标:识别破坏前置防御后流入的伪造                           │
├────────────────────────────────────────────────────────────┤
│  第三道防线:流程审计(人工+随机抽查)                          │
│  - 高风险照片人工复审                                         │
│  - 随机现场抽查                                              │
│  - 异常项目深度审计                                          │
│  目标:兜底,威慑系统性造假                                    │
└────────────────────────────────────────────────────────────┘

下面分别讲每道防线的设计要点。

3.1 第一道防线:前置防御

3.1.1 强制专用 App 拍摄

这是整个系统的基石。原则:用户不能从手机相册选图上传,只能在 App 内当场拍摄

技术实现要点:

3.1.2 多维度签名

每张照片在拍摄时附加一个”可信信封”:

{
  "image_hash": "sha256:...",
  "device": {
    "device_id": "硬件标识",
    "model": "iPhone 15 Pro",
    "os": "iOS 17.4",
    "app_version": "1.2.3"
  },
  "spacetime": {
    "client_time": "2026-05-07T10:23:15+08:00",
    "server_time_anchor": "2026-05-07T10:23:14+08:00",
    "gps": {"lat": 39.9087, "lon": 116.3975, "accuracy_m": 4.2},
    "altitude": 48.5,
    "geohash": "wx4g0..."
  },
  "capture_meta": {
    "focal_length_mm": 6.86,
    "iso": 100,
    "shutter_speed": "1/120",
    "aperture": 1.78,
    "white_balance": "auto",
    "flash": false
  },
  "behavior": {
    "preview_duration_ms": 3420,
    "gyro_samples": [],
    "focus_attempts": 2,
    "burst_index": 1
  },
  "project_context": {
    "project_id": "PRJ-2026-XXX",
    "task_id": "TASK-091",
    "expected_geofence": "...",
    "expected_time_window": "..."
  },
  "signature": "ECDSA(...)"
}

注意几个设计巧思:

3.1.3 拍摄合规约束

3.2 第二道防线:被动检测

即使前置防御做到 90% 严密,仍会有:

所以服务端必须有完整的检测管线。设计为流水线,多检测器并行 + 串行融合:

[图像入站]

    ├──→ 元数据校验器(结构性+逻辑性)
    │       └─ 失败直接拒收

    ├──→ 签名验证器(C2PA-like)
    │       └─ 签名异常进入人工

    ├──→ 指纹查重器(pHash + 深度特征)
    │       └─ 命中历史库进入人工

    └──→ 检测引擎集群(并行)
            ├─ ELA / 频域分析
            ├─ AI 生成检测(DIRE / 频域分类器)
            ├─ 篡改定位(TruFor 类)
            ├─ 多模态一致性(VLM)
            └─ 场景核验(与项目元数据比对)


            [综合风险评分]

        ┌───────────┼───────────┐
        ▼           ▼           ▼
      低风险      中风险      高风险
      自动通过    人工复审    自动拒收

3.2.1 元数据校验器

这是性价比最高的检测器。规则非常具体:

def metadata_check(image_meta, task_context):
    """元数据多维校验"""
    issues = []

    # EXIF 完整性
    required = ['DateTime', 'GPSInfo', 'Make', 'Model']
    missing = [k for k in required if k not in image_meta]
    if missing:
        issues.append({'level': 'high', 'msg': f'缺失字段: {missing}'})

    # 时间窗
    capture_time = parse_exif_time(image_meta.get('DateTime'))
    if not (task_context.start <= capture_time <= task_context.end):
        issues.append({'level': 'high', 'msg': '拍摄时间在任务窗外'})

    # 地理围栏
    gps = parse_gps(image_meta.get('GPSInfo'))
    if not in_geofence(gps, task_context.geofence):
        issues.append({'level': 'high', 'msg': 'GPS 在地理围栏外'})

    # 软件指纹
    software = image_meta.get('Software', '')
    suspicious = ['Photoshop', 'GIMP', 'Snapseed', 'Stable Diffusion']
    for s in suspicious:
        if s.lower() in software.lower():
            issues.append({'level': 'medium', 'msg': f'检测到编辑软件: {s}'})

    # 设备型号一致性
    declared_model = image_meta.get('Model', '')
    if declared_model and not validate_device_specs(declared_model, image_meta):
        issues.append({'level': 'high', 'msg': '设备参数与声明型号不匹配'})

    return issues

3.2.2 指纹查重器

手法 B(图像复用) 的关键。设计两层:

第一层:感知哈希(pHash/dHash)

快速、O(1) 查询,对裁剪和压缩鲁棒。建立全局图像指纹库:

import imagehash
from PIL import Image

def compute_phash(image_path, hash_size=16):
    """感知哈希"""
    img = Image.open(image_path)
    return str(imagehash.phash(img, hash_size=hash_size))

def find_similar(target_hash, fingerprint_db, threshold=10):
    """汉明距离查询相似图"""
    matches = []
    for record in fingerprint_db:
        dist = imagehash.hex_to_hash(target_hash) - imagehash.hex_to_hash(record['hash'])
        if dist <= threshold:
            matches.append({'record': record, 'distance': dist})
    return matches

实际部署用 FaissMilvus 存储二值哈希,亿级图片秒级查询。

第二层:深度特征

pHash 对几何变换(旋转、镜像、剧烈裁剪)和重新生成不鲁棒。深度特征兜底:

import torch
from transformers import CLIPModel, CLIPProcessor

clip = CLIPModel.from_pretrained("openai/clip-vit-large-patch14")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-large-patch14")

def compute_deep_feature(image_path):
    img = Image.open(image_path)
    inputs = processor(images=img, return_tensors="pt")
    with torch.no_grad():
        feats = clip.get_image_features(**inputs)
    feats = feats / feats.norm(dim=-1, keepdim=True)
    return feats.cpu().numpy()

CLIP 特征对内容相似的判断力强。比如同一个变压器从不同角度拍,pHash 会判不同,CLIP 能发现是同一个对象。

实际部署:pHash 做粗筛(亿级),CLIP 做精筛(粗筛后的 top-100)。查重命中后人工核验是不是合法的复用(同一项目同一节点确实可能多张相似)。

3.2.3 信号级取证

ELA + 频域分析作为基础体检。对手法 C 仍有效。注意:只在没有可信签名的图像上跑——通过前置防御进来的图,已经知道是真实拍摄,跑这些就是浪费算力。

3.2.4 AI 生成检测

这是当前研究热点也是工程难点。推荐方案:多模型集成 + 持续更新

class AIGCDetector:
    def __init__(self):
        self.detectors = [
            FrequencyDomainDetector(),     # 频域分类器
            DIREDetector(),                # 重建误差
            CLIPBasedDetector(),           # CLIP 特征分类
            ModelFingerprintDetector(),    # 已知模型指纹
        ]

    def detect(self, image):
        scores = {}
        for d in self.detectors:
            scores[d.name] = d.predict(image)

        # 加权融合(权重需在自有数据集上调)
        weights = {'freq': 0.2, 'dire': 0.35, 'clip': 0.25, 'fp': 0.2}
        combined = sum(scores[n] * weights[n] for n in scores)

        return {
            'is_ai_generated': combined > 0.5,
            'confidence': combined,
            'detail': scores
        }

关键现实:AI 生成检测的准确率会随生成模型迭代而衰减。系统必须有红队机制——内部团队定期用最新生成模型造假数据,测试系统并迭代。这个不能省。

3.2.5 篡改定位

输出像素级篡改概率热力图,让审核员一眼看出”哪里被改过”。推荐基于 TruFor 或 MVSS-Net 自训练。关键不是模型,是训练数据——通用篡改数据集与工程场景差异大,必须用工程图自建数据集。

3.2.6 多模态一致性

用 VLM(如 Qwen-VL、InternVL、GPT-4V)对图像做语义描述,与项目元数据比对:

def semantic_consistency_check(image, task_meta, vlm_client):
    prompt = """
请分析这张工程现场照片,提取以下信息(JSON 格式):
- detected_objects: 主要设备/构件
- estimated_time_of_day: 早晨/中午/傍晚/夜间
- estimated_season: 春夏秋冬
- weather: 晴/阴/雨/雪
- visible_text: 图中可见的所有文字(标识牌、铭牌等)
- scene_type: 室内/室外/地下
- construction_phase: 基础/主体/装饰/竣工
"""
    parsed = vlm_client.analyze(image, prompt)

    issues = []

    # 时间一致性
    actual_time = task_meta['capture_time']
    if not time_of_day_match(parsed['estimated_time_of_day'], actual_time):
        issues.append('图像光照与声称拍摄时间不符')

    # 季节一致性
    if not season_match(parsed['estimated_season'], actual_time):
        issues.append('图像季节特征与拍摄时间不符')

    # 任务一致性
    expected_phase = task_meta['expected_phase']
    if parsed['construction_phase'] != expected_phase:
        issues.append(f"图像显示阶段为{parsed['construction_phase']},预期{expected_phase}")

    # 标识核验
    expected_text = task_meta.get('expected_visible_text', [])
    for txt in expected_text:
        if not any(txt in v for v in parsed['visible_text']):
            issues.append(f'未检测到预期标识: {txt}')

    return issues

这一类检查特别能防”用其他项目照片冒充”——即使图是真的,语义上和这个项目对不上。

3.2.7 综合风险评分

各检测器输出汇总成单一风险分。推荐规则 + 学习结合:

阈值要在自有数据上调,且需要长期 A/B 测试观察误报率和漏报率。

3.3 第三道防线:流程审计

技术不是万能的。流程层兜底:

人工复审队列:所有中风险照片进入人工。审核员看到的不是裸图,是带高亮的”风险证据视图”——元数据问题列表 + 篡改热力图 + 相似图候选。让人工高效。

随机抽查:所有”自动通过”的照片中随机 2-5% 进入复审。这是抗对抗性优化的关键——攻击者无法保证哪张被抽中,整体造假风险不可控。

项目级风控:跟踪每个项目的拒收率、风险分分布。异常项目(拒收率持续偏低、风险分异常分布)触发深度审计。

现场抽查:高风险项目派人到现场实地核验。技术筛出可疑,人去现场敲定。

威慑机制:对查实的造假,惩罚要可信、显著(信用扣分、合同处罚、行业通报)。系统如果”查得出但不处理”,所有技术投入都白费。


四、系统架构设计

把上述拼起来,形成完整系统:

┌──────────────────────────────────────────────────────────────────┐
│                        客户端(移动 App)                         │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐  ┌────────────┐  │
│  │ 任务列表   │  │ 受控相机   │  │ 签名引擎   │  │ 上传管理   │  │
│  └────────────┘  └────────────┘  └────────────┘  └────────────┘  │
│                          │                                        │
│                  [安全签名信封]                                    │
└──────────────────────────┼───────────────────────────────────────┘
                           │ HTTPS + 双向证书

┌──────────────────────────────────────────────────────────────────┐
│                     接入网关 (API Gateway)                        │
│           认证、限流、初步合法性校验、请求路由                      │
└──────────────────────────┬───────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────┐
│                    检测调度引擎 (Orchestrator)                    │
│         任务编排、并行调度、结果融合、阈值判定                     │
└─┬──────┬──────┬──────┬──────┬──────┬──────┬──────────────────────┘
  │      │      │      │      │      │      │
  ▼      ▼      ▼      ▼      ▼      ▼      ▼
┌────┐┌────┐┌────┐┌────┐┌────┐┌────┐┌─────────┐
│ 元 ││ 签 ││ 指 ││ 信 ││AIGC││ 篡 ││ 多模态  │
│ 数 ││ 名 ││ 纹 ││ 号 ││检测││ 改 ││ 一致性  │
│ 据 ││ 验 ││ 查 ││ 取 ││引擎││ 定 ││ (VLM)   │
│ 校 ││ 证 ││ 重 ││ 证 ││    ││ 位 ││         │
│ 验 ││    ││    ││    ││    ││    ││         │
└────┘└────┘└────┘└────┘└────┘└────┘└─────────┘


┌──────────────────────────────────────────────────────────────────┐
│                     风险评估与决策模块                            │
│           规则引擎 + 加权融合 + 阈值分级 + 决策输出                │
└──┬─────────────────────────────────────────────────┬─────────────┘
   │                                                 │
   ▼ 自动通过/拒收                                    ▼ 人工复审
┌────────────────┐                        ┌──────────────────────┐
│  归档与索引     │                        │  审核工作台          │
│  - 对象存储     │                        │  风险证据视图         │
│  - 元数据库     │                        │  审核员决策          │
│  - 指纹库       │                        │  反馈到训练集         │
│  - 全文索引     │                        └──────────────────────┘
└────────────────┘                                   │

                                          ┌──────────────────────┐
                                          │  审计与风控分析      │
                                          │  项目级、人员级风险   │
                                          │  异常监控、告警       │
                                          └──────────────────────┘

4.1 核心数据模型

-- 图像主表
CREATE TABLE submitted_images (
    image_id VARCHAR(64) PRIMARY KEY,
    project_id VARCHAR(64) NOT NULL,
    task_id VARCHAR(64) NOT NULL,
    submitter_id VARCHAR(64) NOT NULL,

    storage_uri VARCHAR(512),
    file_size BIGINT,

    -- 受控签名包
    envelope_json JSON,
    signature_valid BOOLEAN,

    -- 检测结果
    metadata_check_result JSON,
    fingerprint_match_result JSON,
    signal_forensics_result JSON,
    aigc_detection_result JSON,
    tampering_localization_uri VARCHAR(512),
    semantic_check_result JSON,

    -- 综合
    risk_score FLOAT,
    risk_level ENUM('low', 'medium', 'high'),
    final_decision ENUM('auto_pass', 'manual_review', 'auto_reject', 'reviewed_pass', 'reviewed_reject'),

    submitted_at TIMESTAMP,
    decided_at TIMESTAMP,

    INDEX idx_project (project_id, submitted_at),
    INDEX idx_submitter (submitter_id, submitted_at),
    INDEX idx_risk (risk_level, submitted_at)
);

-- 指纹库
CREATE TABLE image_fingerprints (
    image_id VARCHAR(64) PRIMARY KEY,
    phash VARCHAR(64),
    dhash VARCHAR(64),
    deep_feature_id BIGINT,  -- 指向向量库 ID
    project_id VARCHAR(64),
    captured_at TIMESTAMP,
    INDEX idx_phash (phash)
);

-- 审核记录
CREATE TABLE review_records (
    review_id VARCHAR(64) PRIMARY KEY,
    image_id VARCHAR(64),
    reviewer_id VARCHAR(64),
    decision ENUM('pass', 'reject', 'escalate'),
    reason TEXT,
    feedback_to_training BOOLEAN DEFAULT FALSE,
    reviewed_at TIMESTAMP
);

-- 项目级风险画像
CREATE TABLE project_risk_profile (
    project_id VARCHAR(64),
    period DATE,
    total_submissions INT,
    auto_pass_count INT,
    manual_review_count INT,
    rejected_count INT,
    average_risk_score FLOAT,
    anomaly_flags JSON,
    PRIMARY KEY (project_id, period)
);

4.2 技术选型建议

考虑你已有的基础设施栈,给一份具体建议:

模块技术选型理由
移动端Flutter你已有积累,跨平台
接入网关Nginx + 自研鉴权层你已有 Nginx 部署经验
调度编排Go + GoFrame你的强项
检测引擎Python + PyTorch生态成熟
任务队列NATS(你已部署)+ Dkron 定时复用现有
对象存储MinIO 自建数据敏感,自托管
元数据库MySQL 8.4(你已用)复用
指纹库Qdrant / Milvus你部署过 Qdrant
全文索引Elasticsearch / OpenSearch审计查询需要
VLMQwen2.5-VL(自部署) / 即梦 / GPT-4V自部署优先(数据敏感)
模型服务vLLM / Triton你已有 vLLM 经验
GPURTX 4090 起步,规模化用 A100/H100你已有 4090
审核工作台Next.js + Tailwind标配
监控Prometheus + Grafana标配

五、关键技术实现示意

下面给几个关键模块的核心代码,作为后续实现的起点。注意:这些是骨架,生产代码需要完善错误处理、性能优化、安全加固。

5.1 客户端签名信封生成(Flutter 伪代码)

class TrustedCaptureService {
  Future<CaptureEnvelope> capture(TaskContext task) async {
    // 1. 拉取服务端时间令牌
    final timeToken = await _fetchTimeToken();

    // 2. 启动陀螺仪采样
    final gyroBuffer = _startGyroSampling();

    // 3. 调起受控相机
    final captureResult = await _controlledCamera.shoot();
    final imageBytes = captureResult.bytes;
    final exif = captureResult.exif;

    // 4. 停止陀螺仪,截取拍摄前后样本
    final gyroSamples = gyroBuffer.extractAround(captureResult.timestamp);

    // 5. 采集设备与位置
    final device = await _getDeviceFingerprint();
    final location = await _getCurrentLocation(highAccuracy: true);

    // 6. 构造信封
    final envelope = CaptureEnvelope(
      imageHash: sha256(imageBytes),
      device: device,
      spacetime: SpaceTime(
        clientTime: DateTime.now(),
        serverTimeToken: timeToken,
        gps: location,
      ),
      captureMeta: exif,
      behavior: BehaviorMeta(
        previewDurationMs: captureResult.previewDuration,
        gyroSamples: gyroSamples,
        focusAttempts: captureResult.focusAttempts,
      ),
      projectContext: task.toContext(),
    );

    // 7. 签名
    envelope.signature = await _signer.sign(envelope.serialize());

    return envelope;
  }
}

5.2 服务端检测编排(Go + GoFrame)

package detector

import (
    "context"
    "github.com/gogf/gf/v2/frame/g"
    "sync"
)

type DetectionResult struct {
    DetectorName string
    Score        float64
    Issues       []string
    Detail       map[string]interface{}
}

type DetectionOrchestrator struct {
    metadataChecker  *MetadataChecker
    signatureVerifier *SignatureVerifier
    fingerprintDB    *FingerprintDB
    signalForensics  *SignalForensicsClient   // 调 Python 服务
    aigcDetector     *AIGCDetectorClient       // 调 Python 服务
    tamperLocator    *TamperLocatorClient      // 调 Python 服务
    vlmAnalyzer      *VLMAnalyzerClient        // 调 VLM 服务
}

func (o *DetectionOrchestrator) Process(
    ctx context.Context,
    image *SubmittedImage,
) (*FinalDecision, error) {

    // 第一阶段:快速校验(同步、串行)
    // 任何一个失败可以直接终止

    metaResult := o.metadataChecker.Check(image)
    if metaResult.HasFatalIssue() {
        return &FinalDecision{Level: "high", Action: "auto_reject",
            Reason: "metadata_fatal"}, nil
    }

    if image.Envelope != nil {
        sigResult := o.signatureVerifier.Verify(image.Envelope)
        if !sigResult.Valid {
            return &FinalDecision{Level: "high", Action: "auto_reject",
                Reason: "signature_invalid"}, nil
        }
    }

    // 指纹查重(同步,命中也不直接拒绝,进入人工)
    fpResult := o.fingerprintDB.Query(image)

    // 第二阶段:重型检测(并行)
    var wg sync.WaitGroup
    results := make(chan DetectionResult, 4)

    detectors := []func() DetectionResult{
        func() DetectionResult { return o.signalForensics.Analyze(image) },
        func() DetectionResult { return o.aigcDetector.Detect(image) },
        func() DetectionResult { return o.tamperLocator.Locate(image) },
        func() DetectionResult { return o.vlmAnalyzer.Check(image) },
    }

    for _, fn := range detectors {
        wg.Add(1)
        go func(f func() DetectionResult) {
            defer wg.Done()
            results <- f()
        }(fn)
    }

    wg.Wait()
    close(results)

    // 第三阶段:融合
    allResults := []DetectionResult{}
    for r := range results {
        allResults = append(allResults, r)
    }

    decision := o.fuseAndDecide(metaResult, fpResult, allResults)

    // 写入数据库 & 路由到下一步
    o.persist(ctx, image, decision)
    o.route(ctx, image, decision)

    return decision, nil
}

func (o *DetectionOrchestrator) fuseAndDecide(
    meta MetadataResult,
    fp FingerprintResult,
    results []DetectionResult,
) *FinalDecision {
    // 规则层
    if meta.HighIssueCount > 0 {
        return &FinalDecision{Level: "high", Action: "manual_review"}
    }
    if fp.HasMatch && fp.MatchConfidence > 0.85 {
        return &FinalDecision{Level: "high", Action: "manual_review",
            Reason: "duplicate_suspect"}
    }

    // 融合层
    weights := map[string]float64{
        "signal": 0.15, "aigc": 0.30, "tamper": 0.30, "vlm": 0.25,
    }
    var score float64
    for _, r := range results {
        score += r.Score * weights[r.DetectorName]
    }

    // 分级
    switch {
    case score < 0.3:
        return &FinalDecision{Score: score, Level: "low", Action: "auto_pass"}
    case score < 0.7:
        return &FinalDecision{Score: score, Level: "medium", Action: "manual_review"}
    default:
        return &FinalDecision{Score: score, Level: "high", Action: "auto_reject"}
    }
}

5.3 AI 生成检测器(Python,集成多种方法)

import torch
import torch.nn.functional as F
from PIL import Image
import numpy as np

class AIGCDetector:
    """多方法集成的 AI 生成检测"""

    def __init__(self, config):
        self.freq_classifier = self._load_freq_model(config['freq_model_path'])
        self.dire_pipeline = self._load_dire(config['dire_model_path'])
        self.clip_classifier = self._load_clip_classifier(config['clip_head_path'])
        self.fingerprint_db = self._load_fingerprint_db(config['fingerprint_db'])

    def detect(self, image_path):
        img = Image.open(image_path).convert('RGB')

        results = {}

        # 1. 频域分类器
        freq_score = self._frequency_check(img)
        results['frequency'] = freq_score

        # 2. DIRE 重建误差
        dire_score = self._dire_check(img)
        results['dire'] = dire_score

        # 3. CLIP 特征分类
        clip_score = self._clip_check(img)
        results['clip'] = clip_score

        # 4. 已知模型指纹比对
        fp_match = self._fingerprint_match(img)
        results['fingerprint'] = fp_match

        # 加权融合
        weights = {'frequency': 0.20, 'dire': 0.35, 'clip': 0.30, 'fingerprint': 0.15}
        combined = sum(results[k] * weights[k] for k in weights)

        return {
            'is_ai_generated': combined > 0.5,
            'confidence': float(combined),
            'sub_scores': {k: float(v) for k, v in results.items()},
            'verdict': self._verdict_text(combined, results),
        }

    def _frequency_check(self, img):
        """基于傅里叶谱的高频伪影检测"""
        gray = np.array(img.convert('L'))
        f = np.fft.fft2(gray)
        fshift = np.fft.fftshift(f)
        magnitude = 20 * np.log(np.abs(fshift) + 1)

        # 提取高频统计特征
        h, w = magnitude.shape
        center_h, center_w = h // 2, w // 2

        # 高频环带
        y, x = np.ogrid[:h, :w]
        dist = np.sqrt((x - center_w)**2 + (y - center_h)**2)
        high_freq_mask = (dist > min(h, w) * 0.35) & (dist < min(h, w) * 0.5)
        high_freq_mean = magnitude[high_freq_mask].mean()
        high_freq_std = magnitude[high_freq_mask].std()

        # 输入到训练好的小分类器
        features = torch.tensor([high_freq_mean, high_freq_std])
        with torch.no_grad():
            score = torch.sigmoid(self.freq_classifier(features)).item()
        return score

    def _dire_check(self, img):
        """DIRE: 通过扩散模型重建误差判别"""
        # 1. 用预训练扩散模型对 img 加噪
        # 2. 用同模型反向去噪重建
        # 3. 计算重建误差
        # 真图重建误差大,AI 生成重建误差小
        with torch.no_grad():
            error = self.dire_pipeline.compute_reconstruction_error(img)
        # 转为 [0,1] 概率(误差越小越像 AI)
        score = 1.0 - torch.sigmoid(error * 0.1).item()
        return score

    def _clip_check(self, img):
        """CLIP 特征 + 训练的二分类头"""
        feat = self._extract_clip_feature(img)
        with torch.no_grad():
            score = torch.sigmoid(self.clip_classifier(feat)).item()
        return score

    def _fingerprint_match(self, img):
        """与已知 AI 模型生成指纹库比对"""
        feat = self._extract_clip_feature(img)
        match = self.fingerprint_db.search(feat, top_k=5)
        if match['top_distance'] < 0.15:
            return 0.95  # 高度疑似某已知模型生成
        return 0.0

    def _verdict_text(self, score, sub):
        if score > 0.85:
            return "高度疑似 AI 生成"
        elif score > 0.5:
            return f"中度疑似 AI 生成(主要疑点:{max(sub, key=sub.get)})"
        elif score > 0.3:
            return "存在轻微疑点,建议人工核验"
        return "未检出 AI 生成特征"

5.4 篡改区域定位(基于 TruFor 思路简化版)

class TamperLocalizer:
    """输出像素级篡改概率热力图"""

    def __init__(self, model_path):
        self.model = self._load_model(model_path)
        self.model.eval()

    def locate(self, image_path):
        img = Image.open(image_path).convert('RGB')

        # RGB 通道
        rgb_tensor = self._preprocess(img)

        # 噪声残差通道(高通滤波后的图像)
        noise_tensor = self._compute_noise_residual(img)

        # 双流推理
        with torch.no_grad():
            # 输出像素级概率图 + 全局篡改概率
            pixel_map, global_score, confidence_map = self.model(rgb_tensor, noise_tensor)

        # 后处理
        pixel_map_np = pixel_map.squeeze().cpu().numpy()

        # 用置信度图加权(低置信区域降权)
        confidence_np = confidence_map.squeeze().cpu().numpy()
        weighted_map = pixel_map_np * confidence_np

        # 找出疑似篡改区域的边界框
        bboxes = self._extract_bboxes(weighted_map, threshold=0.5)

        # 生成可视化叠加图(供审核员查看)
        heatmap_uri = self._save_heatmap(img, weighted_map)

        return {
            'global_tampering_prob': float(global_score),
            'has_tampering_region': len(bboxes) > 0,
            'tampered_regions': bboxes,
            'heatmap_uri': heatmap_uri,
            'mean_confidence': float(confidence_np.mean()),
        }

    def _compute_noise_residual(self, img):
        """SRM 高通滤波提取噪声残差"""
        # SRM 滤波核(图像取证经典)
        # 用三个预设的高通核组合
        ...

结语

工程验收照片防造假,本质上不是一个”AI 模型够不够强”的问题,而是一个**“系统设计够不够整体”**的问题。

我希望这篇文章传达的最重要的一点是:单纯依赖 AI 检测模型对抗 AI 造假,是一场注定输的军备竞赛。生成模型的进化速度永远快于检测模型的迭代速度。真正可行的路径是把战线前置、把流程闭合、把代价对齐——让攻击者面对的不是”如何骗过一个检测器”,而是”如何骗过受控相机+多重签名+跨项目查重+多模态核验+人工抽查+现场复核”组成的多重屏障,且要承担实质的法律和商业代价。

技术上,三道防线缺一不可;策略上,主动取证比被动检测优先;落地上,小步快跑比一步到位务实。先把基础打扎实,再逐步上高级武器。先解决 60% 的简单造假,再去攻克 5% 的高级造假,是工程理性。

最后一句话留给后续立项的团队:工程验收造假是一个真实存在、规模巨大、技术门槛快速降低的问题。它值得一个严肃的产品来解决。

展开评论Show comments