OpenCVSharp4
来源: C#结合OpenCVSharp4图片相似度识别 – 宣君 – 博客园
OpenCVSharp4图片相似度识别
需求背景:需要计算两个图片的相似度,然后将相似的图片进行归纳
1. 图片相似度算法
由于我是CRUD后端仔,对图像处理没什么概念。因此网上调研了几种相似度算法分析其适用场景。
直方图算法
获取要比较的2个图片的直方图数据,然后再将直方图数据归一化比较,最终得到一个相似指数,通过设定相似指数的边界,以此判断是否相同图片。
平均值哈希算法 aHash
转灰度压缩之后计算均值,最终通过像素比较得出哈希值,速度很快,但敏感度很高,稍有变化就会极大影响判定结果,精准度较差。因此比较适用于缩略图比较,最常用的就是以图搜图
感知哈希算法 pHash
在均值哈希基础上加入DCT
(离散余弦变化),两次DCT
就可以很好的将图像按照频度分开,取左上角高能低频信息做均值哈希,因此,精确度很高,但是速度方面较差一些。相比较aHash
,pHash
更加适合用于缩略图比较,也非常适合比较两个近似图片是否相等。
差异值哈希算法 dHash
灰度压缩之后,比较相邻像素之间差异。假设有10×10的图像,每行10个像素,就会产生9个差异值,一共10行,就一共有9×10=90个差异值。最终生成哈希值即指纹。速度上来说,介于aHash
和pHash
之间,精准度同样也介于aHash
和pHash
之间。
结构相似性算法 SSIM
SSIM(structural similarity)
,结构相似性,是一种衡量两幅图像相似度的指标。SSIM
算法主要用于检测两张相同尺寸的图像的相似度、或者检测图像的失真程度。原论文中,SSIM
算法主要通过分别比较两个图像的亮度,对比度,结构,然后对这三个要素加权并用乘积表示。
SSIM
算法在设计上考虑了人眼的视觉特性,它能够考虑到图像的结构信息在人的感知上的模糊变化,该模型还引入了一些与感知上的变化有关的感知现象,包含亮度mask和对比mask,结构信息指的是像素之间有着内部的依赖性,尤其是空间上靠近的像素点。这些依赖性携带着目标对象视觉感知上的重要信息。
经过调研对比,这里就选择SSIM
算法。
2. 下载OpenCVSharp4
通过NuGet包管理器进行下载。搜索OpenCVSharp4
下载。
请注意其描述信息:OpenCV wrapper for .NET. Since this package includes only core managed libraries, another package of native bindings for your OS is required (OpenCvSharp4.runtime.*).
这是说:OpenCV 包只是一个核心库,如需在你的系统上使用,还需要对应的运行时包,这里是Windows系统,因此还需下载 OpenCvSharp4.runtime.win
3. 使用
在项目中引入OpenCvSharp
using OpenCvSharp; |
由于OpenCVSharp4
没有直接提供封装SSIM
算法的接口,因此需要自行写这部分代码。完整代码如下
public Scalar Compare_SSIM(string imgFile1, string imgFile2) | |
{ | |
var image1 = Cv2.ImRead(imgFile1); | |
var image2Tmp = Cv2.ImRead(imgFile2); | |
// 将两个图片处理成同样大小,否则会有错误: The operation is neither ‘array op array’ (where arrays have the same size and the same number of channels), nor ‘array op scalar’, nor ‘scalar op array’ | |
var image2 = new Mat(); | |
Cv2.Resize(image2Tmp, image2, new OpenCvSharp.Size(image1.Size().Width, image1.Size().Height)); | |
double C1 = 6.5025, C2 = 58.5225; | |
var validImage1 = new Mat(); | |
var validImage2 = new Mat(); | |
image1.ConvertTo(validImage1, MatType.CV_32F); //数据类型转换为 float,防止后续计算出现错误 | |
image2.ConvertTo(validImage2, MatType.CV_32F); | |
Mat image1_1 = validImage1.Mul(validImage1); //图像乘积 | |
Mat image2_2 = validImage2.Mul(validImage2); | |
Mat image1_2 = validImage1.Mul(validImage2); | |
Mat gausBlur1 = new Mat(), gausBlur2 = new Mat(), gausBlur12 = new Mat(); | |
Cv2.GaussianBlur(validImage1, gausBlur1, new OpenCvSharp.Size(11, 11), 1.5); //高斯卷积核计算图像均值 | |
Cv2.GaussianBlur(validImage2, gausBlur2, new OpenCvSharp.Size(11, 11), 1.5); | |
Cv2.GaussianBlur(image1_2, gausBlur12, new OpenCvSharp.Size(11, 11), 1.5); | |
Mat imageAvgProduct = gausBlur1.Mul(gausBlur2); //均值乘积 | |
Mat u1Squre = gausBlur1.Mul(gausBlur1); //各自均值的平方 | |
Mat u2Squre = gausBlur2.Mul(gausBlur2); | |
Mat imageConvariance = new Mat(), imageVariance1 = new Mat(), imageVariance2 = new Mat(); | |
Mat squreAvg1 = new Mat(), squreAvg2 = new Mat(); | |
Cv2.GaussianBlur(image1_1, squreAvg1, new OpenCvSharp.Size(11, 11), 1.5); //图像平方的均值 | |
Cv2.GaussianBlur(image2_2, squreAvg2, new OpenCvSharp.Size(11, 11), 1.5); | |
imageConvariance = gausBlur12 – gausBlur1.Mul(gausBlur2);// 计算协方差 | |
imageVariance1 = squreAvg1 – gausBlur1.Mul(gausBlur1); //计算方差 | |
imageVariance2 = squreAvg2 – gausBlur2.Mul(gausBlur2); | |
var member = ((2 * gausBlur1.Mul(gausBlur2) + C1).Mul(2 * imageConvariance + C2)); | |
var denominator = ((u1Squre + u2Squre + C1).Mul(imageVariance1 + imageVariance2 + C2)); | |
Mat ssim = new Mat(); | |
Cv2.Divide(member, denominator, ssim); | |
var sclar = Cv2.Mean(ssim); | |
return sclar; // 变化率,即差异 | |
} |
实际检测效果如下
这两幅图的相似度大约是92.21%,基本符合预期
这两幅图居然还有约18%的相似度,根据SSIM
算法特性,这应该是图片大小的相似。
虽然也是拿来主义,毕竟我不是研究算法的大佬,需要站在巨人肩膀上干活~
做个笔记。