尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

AI立绘被抠图搬运?用pHash+ORB指纹检测和水印保护

AI立绘被抠图搬运?用pHash+ORB指纹检测和水印保护 最近在做 AI 小镇项目时遇到一件让人哭笑不得的事自己调了很久的角色立绘被人用 AI 一键抠图工具直接抠走换了个背景就当原创素材使用了。这种事单纯靠“整图对比”很难发现原因是对方保留的是角色主体背景已经被替换。今天这篇文章我想从技术侧把 AI 立绘、AI 抠图、图像指纹检测、数字水印这几个问题完整拆一遍。不光讲原理还会给出一个能跑起来的“立绘指纹检测器”用 pHash 和 ORB 特征匹配来识别“被抠图搬运”的素材最后再聊聊隐蔽水印和版权保护的最佳实践。读完之后你可以掌握AI 立绘从生成到使用的基本 pipelineAI 抠图的底层原理与常见工具用 pHash 和 ORB 做图像相似度检测用简化频域水印给立绘加一层隐蔽标识实际项目中保护原创素材的工程建议。1. 背景与核心概念1.1 立绘是什么立绘一般指角色的全身像或半身像在游戏、漫画、视觉小说、虚拟主播等领域非常常见。以前的立绘是画师一张张手绘的现在很多项目开始用 AI 生成草稿再人工精修尤其是独立游戏或 AI 交互项目批量生成角色需求很大。在 AI 小镇这种项目里角色头像、角色立绘、场景素材都可能是 AI 生成后人工挑选出来的。一张能用的立绘并不是“点一下生成”就完事需要不断调整提示词、切换模型、做多次图生图甚至局部重绘最后才能得到一张可以放进资源目录的图。1.2 为什么会被“抠下来一块”“抠图”本质上是图像分割和抠像把目标区域从背景里分离出来。现在开源工具很多U2-Net、RemBg、BackgroundMattingV2以及各类一键去背景 SDK都可以把人物或主体从原图中抠出来。如果原图主体清晰、背景相对规整AI 抠图效果会非常好最后直接输出一张透明背景的 PNG。于是问题就来了对方不需要搬运整张原图只需要把角色主体“抠”出来换一个背景、改一下色调就变成了“新素材”。相比之下如果对方直接复制整张原图用感知哈希一查就能发现但“抠图 换背景”会让传统整图相似度判断失效这也是这个现象让人头疼的核心原因。1.3 这篇文章解决的三个问题如何理解 AI 立绘的生成成本以及 AI 抠图为什么高效。如何设计一套算法在对方已经抠图、换背景的前提下仍然识别出立绘主体来自同一张原图。如何通过数字水印和发布策略降低素材被“顺手抠走”的风险。下面我们进入正题。2. 环境准备与版本说明本文代码以 Python 为主建议环境如下Python 3.8 或以上OpenCVopencv-pythonNumPyPillow可选用于处理图片格式DCT 计算直接使用 OpenCV 的cv2.dct。安装命令pip install opencv-python numpy pillow版本请根据实际环境调整本文代码重点演示思路不依赖特定小版本。如果你在 Windows 上使用建议先在虚拟环境里安装依赖macOS 或 Linux 同理。3. AI 立绘生成与抠图原理拆解3.1 一条典型的 AI 立绘生成 pipeline现在生成立绘的主流方式是基于扩散模型的开源或在线服务比如 Stable Diffusion 等。一条典型链路是需求分析确定角色风格、体型、服装、动作、视角。提示词设计把需求转成正向提示词和负向提示词。模型推理用文本生成图像模型生成初稿。图生图 / 局部重绘继续调整构图、面部、细节。超分和修图放大、去噪、调色。抠图与合成把立绘抠出来合成到角色卡、UI、场景中。这里值得强调的是能进入“可用素材”阶段的立绘已经经过多轮调试。外人只看到最终那张图看不到背后调整了多久。这也是为什么被直接抠图搬运会更让人感到无奈。3.2 AI 抠图到底做了什么抠图在技术上分成几个层次语义分割Semantic Segmentation给每个像素打类别标签比如人、背景、物品。实例分割Instance Segmentation在语义分割的基础上区分同类的不同实例。抠图Matting不仅区分前景背景还计算前景透明度 alpha适合头发丝、半透明物体等细节。现在常见的一键去背景工具很多是基于深度模型的语义分割或抠图网络。比如U2-Net常用于显著性目标检测和去背景很多一键抠图工具早期版本都基于 U2-Net。BackgroundMattingV2更偏向视频抠图适合实时场景。MODNet适合人像快速抠图。流程上把图片输入模型模型输出一个 alpha 遮罩然后用 alpha 遮罩和原图合成透明图。对于主体清晰、边界明显的立绘这个流程非常成熟甚至不需要手动修正。3.3 为什么“换背景”难以用整图比较查出来如果对方只是整体复制用 pHash、aHash 等感知哈希算法很容易发现。但对方先抠图、再合成到新背景整张图的像素分布已经完全不同了简单哈希会失效。所以我们需要借助局部特征。立绘上的人脸、服饰轮廓、装饰元素、武器等区域会被提取成大量局部特征点。即便背景被替换角色主体上的特征点依然存在。用 ORB、SIFT 这类特征描述子做匹配仍然能得到较高的匹配数量。4. 识别搬运图像指纹与相似度检测4.1 感知哈希 pHash 的原理pHashPerceptual Hash是一种对图像内容进行“摘要”的算法。它不像 MD5 那样对文件字节敏感两张视觉上几乎一样的图即使格式不同、压缩率不同、尺寸不同pHash 也会很接近。经典 pHash 的做法把图像缩放到固定尺寸比如 32x32。转为灰度图。做 DCT离散余弦变换。取左上角 8x8 的低频系数区域。计算这 64 个系数的中位数。每个系数大于中位数记为 1小于中位数记为 0得到 64 位特征串。比较两个特征串的汉明距离距离越小越相似。这类方法对缩放、亮度变化、轻微压缩有较好的鲁棒性适合判断“两张图是不是同一张图”。4.2 ORB 特征匹配的原理ORBOriented FAST and Rotated BRIEF是一种快速局部特征提取算法。它的特点速度比 SIFT/SURF 快很多对旋转和尺度变化有一定鲁棒性适合在实时场景和工程中使用不需要额外安装 OpenCV contrib 模块。ORB 先在图像中检测关键点再为每个关键点计算二值描述子。两张图比较时用汉明距离做最近邻匹配再通过比例测试筛选可靠的匹配点。当立绘被抠出来并换到新背景后新背景新增的特征点不会影响原区域特征点的匹配角色主体上的特征点仍然能在两张图中对应上。4.3 匹配数量与阈值ORB 匹配的“好匹配数量”取决于图像内容、清晰度和主体占比。通常可以设一个经验阈值例如匹配数少于 15基本不相似15 到 60有一定相似建议人工复核大于 60很可能是同源素材。但阈值不能一概而论复杂立绘的特征点远多于简单立绘。实际项目里建议先用一批已知正负样本标定阈值再投入自动化流程。5. 实战立绘指纹检测器下面实现一个简单的 Python 小工具包含 pHash 和 ORB 两种检测方式。5.1 项目结构image_fingerprint/ ├── fingerprint.py # 核心算法 ├── detector.py # 命令行入口 ├── assets/ │ ├── original.png # 原始立绘 │ ├── stolen_new_bg.png # 被抠出来换背景的图 │ └── other.png # 无关的另一张图5.2 fingerprint.py核心算法# 文件路径image_fingerprint/fingerprint.py import cv2 import numpy as np def read_gray(image_path): img cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) if img is None: raise ValueError(f无法读取图片: {image_path}) return img def read_rgb_with_white_bg(image_path): 读取图片。如果是透明 PNG先把透明背景合成到白色画布上 避免透明区域在灰度图中变成黑色干扰后续特征提取。 img cv2.imread(image_path, cv2.IMREAD_UNCHANGED) if img is None: raise ValueError(f无法读取图片: {image_path}) if len(img.shape) 2: return cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) if img.shape[2] 4: alpha img[:, :, 3] / 255.0 rgb img[:, :, :3] white np.ones_like(rgb) * 255 merged rgb * alpha[..., None] white * (1 - alpha[..., None]) return merged.astype(np.uint8) return img[:, :, :3] def phash(image_path, resize_size32, low_size8): 计算 pHash返回 64 位二值数组 img read_gray(image_path) img cv2.resize(img, (resize_size, resize_size)) img np.float32(img) dct cv2.dct(img) dct_low dct[:low_size, :low_size] med np.median(dct_low) hash_bits (dct_low med).flatten().astype(np.uint8) return hash_bits def hamming_distance(hash1, hash2): 计算两个哈希数组的汉明距离 return int(np.count_nonzero(hash1 ! hash2)) def phash_similarity(image_path1, image_path2): 返回相似距离0 表示最相似64 表示完全不同。 一般 10 以内可以认为是同一张图或高度相似。 h1 phash(image_path1) h2 phash(image_path2) return hamming_distance(h1, h2) def orb_features(image_path, max_features1000): 提取 ORB 特征点与描述子 img read_gray(image_path) orb cv2.ORB_create(nfeaturesmax_features) keypoints, descriptors orb.detectAndCompute(img, None) return keypoints, descriptors def orb_match_score(image_path1, image_path2, ratio0.75): 返回经过比例测试后的匹配点数量 _, des1 orb_features(image_path1) _, des2 orb_features(image_path2) if des1 is None or des2 is None: return 0 bf cv2.BFMatcher(cv2.NORM_HAMMING) matches bf.knnMatch(des1, des2, k2) good [] for m, n in matches: if m.distance ratio * n.distance: good.append(m) return len(good)这里需要注意cv2.dct要求输入是浮点数所以要先将灰度图转成np.float32。pHash 的经典做法是缩小到 32x32再取 8x8 低频区所以resize_size不能小于low_size。BFMatcher.knnMatch返回的每个匹配对包含最近邻和次近邻最近邻距离明显小于次近邻时才认为是可靠匹配这就是 Lowe 比例测试的基本思路。5.3 detector.py命令行入口# 文件路径image_fingerprint/detector.py import sys from fingerprint import phash_similarity, orb_match_score def main(): if len(sys.argv) ! 3: print(用法: python detector.py 图片A 图片B) sys.exit(1) img_a sys.argv[1] img_b sys.argv[2] phash_dist phash_similarity(img_a, img_b) orb_score orb_match_score(img_a, img_b) print(fpHash 汉明距离: {phash_dist} (0~64越小越相似)) print(fORB 匹配点数: {orb_score}) if phash_dist 10: print(结论: 很可能是同一张/高度相似的图片) elif orb_score 60: print(结论: 局部特征高度相似疑似从同一立绘抠图或裁剪) elif orb_score 15: print(结论: 存在一定相似特征建议人工复核) else: print(结论: 相似度较低大概率不是同一素材) if __name__ __main__: main()5.4 运行与验证假设我们准备了三张图assets/original.png原始立绘assets/stolen_new_bg.png把立绘主体抠出来换到新背景后的图assets/other.png完全无关的另一张角色图。执行cd image_fingerprint python detector.py assets/original.png assets/stolen_new_bg.png预期输出类似pHash 汉明距离: 28 ORB 匹配点数: 86 结论: 局部特征高度相似疑似从同一立绘抠图或裁剪再执行python detector.py assets/original.png assets/other.png预期输出类似pHash 汉明距离: 42 ORB 匹配点数: 5 结论: 相似度较低大概率不是同一素材这里的数字只是演示实际数字会因图片内容不同而变化。但趋势是一致的换背景后 pHash 距离升高但 ORB 匹配数仍然很高说明局部特征匹配可以有效识别“抠图 换背景”这类搬运行为。5.5 结果说明这个实战案例告诉我们只靠整图哈希无法识别“抠图 换背景”必须引入局部特征匹配。如果你想做一个自动化监测工具可以在这个基础之上扩展维护一个正版素材库预先计算每个素材的 ORB 描述子对目标图片和库中素材跑 ORB 匹配匹配数超过阈值时告警并保存截图定时巡检或接入上传接口做实时检测。6. 进阶给立绘加一道数字水印图像指纹属于“被动检测”是事情发生后的比对。如果你希望在源头上增加追踪能力可以使用数字水印。6.1 可见水印 vs 隐蔽水印可见水印直接在图上叠加半透明文字或 logo。优点是直观缺点是影响观感而且现在部分 AI 工具可以自动去除。隐蔽水印把一段信息隐藏在图像的频率域或像素噪声中肉眼看不出来但通过程序可以提取验证。隐蔽水印的常见方案包括频域水印DCT/DWT、空域水印、基于神经网络的水印等。6.2 简化频域水印示例下面给一个简化版 DCT 水印思路演示“如何把一段 ID 嵌入图像”。核心做法把图像从 RGB 转为 YCrCb取 Y 通道。对 Y 通道做 DCT。在 DCT 系数中挑出一批位置根据水印比特是 0 还是 1把系数调整到对应奇偶性。逆 DCT得到含水印的 Y 通道。提取时再次 DCT读取这批位置的奇偶性就能还原水印。# 文件路径image_fingerprint/watermark.py import cv2 import numpy as np WATERMARK_LEN 32 def _get_indices(): 选择低频区域中的位置避开 DC 分量 indices [] for i in range(1, 9): for j in range(0, 9 - i): indices.append((i, j)) if len(indices) WATERMARK_LEN: return indices return indices def embed_watermark(image_path, output_path, watermark_bits): 把 32 位水印嵌入图像输出到 output_path img cv2.imread(image_path) if img is None: raise ValueError(f无法读取图片: {image_path}) ycrcb cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb) y, cr, cb cv2.split(ycrcb) y_float np.float32(y) dct cv2.dct(y_float) indices _get_indices() strength 8.0 for idx, bit in enumerate(watermark_bits): x, y_pos indices[idx] val dct[y_pos, x] base round(val / strength) if bit 1 and base % 2 0: base 1 if bit 0 and base % 2 1: base 1 dct[y_pos, x] base * strength y_watermarked cv2.idct(dct) y_watermarked np.uint8(np.clip(y_watermarked, 0, 255)) merged cv2.merge([y_watermarked, cr, cb]) result cv2.cvtColor(merged, cv2.COLOR_YCrCb2BGR) cv2.imwrite(output_path, result) def extract_watermark(image_path): 提取 32 位水印 img cv2.imread(image_path) if img is None: raise ValueError(f无法读取图片: {image_path}) ycrcb cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb) y, _, _ cv2.split(ycrcb) y_float np.float32(y) dct cv2.dct(y_float) indices _get_indices() strength 8.0 bits [] for x, y_pos in indices: val dct[y_pos, x] base round(val / strength) bits.append(1 if base % 2 1 else 0) return bits这段代码只是演示思路强度、位置选取、JPEG 压缩鲁棒性都需要按实际项目调整。如果要在真实场景中使用建议考虑用 DWT 代替全局 DCT抗压缩能力更强水印信息加纠错编码提升提取成功率嵌入强度不能过高否则图像质量下降明显。需要强调的是水印不能替代法律和平台规则保护但能提高追踪能力。7. 常见问题与排查思路问题现象常见原因解决思路pHash 汉明距离很高但其实是同一张图图片被大幅裁剪、旋转或加了强滤镜改用 ORB 等局部特征匹配ORB 匹配点数很少但视觉上很像图片压缩严重或主体在图中占比过小先对主体区域做 ROI 裁剪再做 ORB 匹配透明背景 PNG 直接比较失败透明区域在灰度图中变成黑色干扰特征读取时先合成到白色背景再参与计算水印提取失败图片被二次压缩、裁剪或缩放加入纠错编码提高嵌入强度或改用 DWT 水印待检测图尺寸过大程序很慢没有预处理先缩放到合理尺寸再做特征检测匹配阈值无法通用不同图片内容复杂度差异大用自定义数据集标定阈值而不是用固定阈值这里特别想强调透明背景 PNG 的坑。很多立绘会存成透明底直接用 OpenCV 读取时透明区域会变成黑色。如果拿这张图跟原图比背景差异会拉高 pHash 距离。所以我在fingerprint.py里额外提供了read_rgb_with_white_bg目的就是把透明背景合成到白色画布上再参与特征提取。8. 最佳实践AI 绘画作品的版权保护最后从工程和内容层面给出一些实际建议。第一生成过程留档。在生成立绘时建议记录下模型、prompt、seed、原图、修改记录。这样做不是为了发文章而是将来发生争议时你可以提供完整的创作链路证明。第二发布策略分层。公开渠道尽量发布带可见水印或低分辨率的预览图高清原图在确认授权后再提供给合作方。很多“顺手抠图”事件就是因为公开渠道直接放了高清无码图降低了搬运成本。第三建立素材指纹库。用本文的 pHash ORB 思路把已经发布的素材全部计算好指纹。如果有平台疑似搬运可以对截图或下载图做批量比对。频率不用太高每周或每月一次即可。第四隐蔽水印作为追踪手段。在正式对外发布前对预览图嵌入不可见水印记录批次、账号、时间。水印写入可以作为独立服务配合发布流程使用。第五维权时保持理性。先通过平台投诉、邮件联系等正常渠道沟通保留证据截图。如果涉及商业利益建议咨询专业法律人士。技术检测只能证明相似最终判定还需要结合创作过程记录和平台规则。9. 总结与学习路线本文从“AI 立绘被直接抠走”的常见场景出发梳理了三条线立绘生成链路理解一张可用立绘的产出成本抠图技术原理理解为什么一键抠图如此高效反搬运检测掌握 pHash、ORB 和频域水印的基本实现并得到一个可运行的检测小工具。如果继续深入可以从这几个方向进阶用 DWT 做更抗压缩的频域水印用深度学习做图像向量化检索对遮挡、裁剪更鲁棒把指纹库放到服务端做成 API 或定时巡检任务研究 AI 生成图像的来源追踪例如生成模型的隐式指纹。技术层面的“防搬运”从来不是一劳永逸的但记录、指纹、水印、留痕这套组合拳能让搬运成本大幅提高也能在争议发生时保留关键证据。如果你也在做 AI 绘画或素材管理工作建议先用文中的小工具给现有素材做一次指纹归档后续排查会省很多力气。
返回列表