bitmap+直方图比对是最稳的入门方案,用lockbits提速并计算rgb三通道16级直方图,以卡方距离衡量相似性,阈值0.15~0.3;imagesharp.image.compare支持ssim,更符合人眼感知,需同尺寸且启用includecolor;避免opencvsharp等重型库,预处理(缩放、去alpha、裁剪)至关重要。

用 Bitmap + 直方图比对是最稳的入门方案
直接读像素或用 OpenCV.NET 太重,而纯哈希(如 PerceptualHash)对轻微缩放/旋转就失效。直方图法在 C# 里不用额外依赖,Bitmap 自带支持,适合判断“内容是否大致一致”,比如截图比对、UI 自动化校验。
关键不是算“精确相似度”,而是看颜色分布是否接近——两张相同构图但亮度微调的图,直方图距离会很小;而哪怕只换了个按钮颜色,RGB 直方图也能明显拉大距离。
- 先用
Bitmap.GetPixel()逐像素采样太慢,改用LockBits()提速 10 倍以上 - 只统计 RGB 各通道的 16 级直方图(0–15, 16–31…),既压缩数据量又抗噪
- 用
ChiSquare距离(卡方距离)比欧氏距离更合理:它对低频 bin 的差异不敏感,避免个别噪点主导结果 - 阈值建议设在
0.15~0.3之间:低于0.15基本可认为是同一张图的轻微处理版本
ImageSharp 的 Image.Compare 是最省事的现代选择
如果你项目已用 .NET 6+ 且能引入第三方库,ImageSharp 的 Image.Compare 方法封装了结构相似性(SSIM)计算,比直方图更能反映人眼感知的差异,尤其对模糊、对比度变化更鲁棒。
注意它默认只比对 Luminance(灰度)通道,想包含色彩信息得手动转成 Rgba32 并启用 CompareOptions.IncludeColor。
- 必须确保两张图尺寸一致,否则抛
InvalidOperationException: Images must have the same dimensions - SSIM 返回值范围是
-1.0到1.0,越接近1.0越相似;实际中> 0.95可视为无感知差异 - 性能比直方图慢约 3~5 倍,但比 OpenCV.NET 轻量得多,适合中小图(
1920x1080以内) - 别用
Image.CompareTo——那是字节级比对,连 PNG 压缩差异都会导致0.0
别碰 OpenCvSharp 除非你真需要特征点匹配
用 OpenCvSharp 调 Cv2.MatchTemplate 或 Cv2.Similarity 看起来很专业,但实际落地坑多:Windows 下 DLL 加载失败、.NET Core 版本兼容混乱、GPU 加速默认关着还更慢。它真正该用的场景只有一个:两张图存在平移/旋转/缩放差异,且你要定位具体偏移量。
-
Cv2.MatchTemplate返回的是匹配位置的Mat,不是相似度数值,得自己取MinMaxLoc再算置信度 - 用
AKAZE或SIFT提特征点?.NET 绑定版常缺DescriptorMatcher实现,容易卡在NullReferenceException - 如果只是判断“是不是同一张图”,用它属于杀鸡用核弹——编译体积涨 20MB,部署多一个
opencv_worldxxx.dll,错误日志全是cv::error(): OpenCV(4.8.0) ... error: (-215:Assertion failed)
最容易被忽略的预处理细节
几乎所有相似度算法都默认输入是“干净、对齐、同尺寸”的图。现实中的截图、上传图、摄像头帧几乎都不满足。跳过这步,再准的算法也白搭。
- 强制统一尺寸前先用
Graphics.DrawImage做双线性插值缩放,别用最近邻——否则小图放大后块状伪影会严重干扰直方图 - 有 alpha 通道的 PNG 必须先合成到纯白背景,否则透明区域的
ARGB值在直方图里乱飘 - 网页截图常带抗锯齿灰边,用
Bitmap.Clone截出中心区域再比对,比全图比对更稳定 - 别相信文件名或 EXIF 时间戳——
File.GetLastWriteTime和图像内容完全无关
事情说清了就结束










