OpenCVSharp4图片相似度识别

需求背景:需要计算两个图片的相似度,然后将相似的图片进行归纳

一、图片相似度算法

由于我是CRUD后端仔,对图像处理没什么概念。因此网上调研了几种相似度算法分析其适用场景。

直方图算法

获取要比较的2个图片的直方图数据,然后再将直方图数据归一化比较,最终得到一个相似指数,通过设定相似指数的边界,以此判断是否相同图片。

平均值哈希算法 aHash

转灰度压缩之后计算均值,最终通过像素比较得出哈希值,速度很快,但敏感度很高,稍有变化就会极大影响判定结果,精准度较差。因此比较适用于缩略图比较,最常用的就是以图搜图

感知哈希算法 pHash

在均值哈希基础上加入DCT(离散余弦变化),两次DCT就可以很好的将图像按照频度分开,取左上角高能低频信息做均值哈希,因此,精确度很高,但是速度方面较差一些。相比较aHashpHash更加适合用于缩略图比较,也非常适合比较两个近似图片是否相等。

差异值哈希算法 dHash

灰度压缩之后,比较相邻像素之间差异。假设有10×10的图像,每行10个像素,就会产生9个差异值,一共10行,就一共有9×10=90个差异值。最终生成哈希值即指纹。速度上来说,介于aHashpHash之间,精准度同样也介于aHashpHash之间。

结构相似性算法 SSIM

SSIM(structural similarity),结构相似性,是一种衡量两幅图像相似度的指标。SSIM算法主要用于检测两张相同尺寸的图像的相似度、或者检测图像的失真程度。原论文中,SSIM算法主要通过分别比较两个图像的亮度,对比度,结构,然后对这三个要素加权并用乘积表示。

SSIM算法在设计上考虑了人眼的视觉特性,它能够考虑到图像的结构信息在人的感知上的模糊变化,该模型还引入了一些与感知上的变化有关的感知现象,包含亮度mask和对比mask,结构信息指的是像素之间有着内部的依赖性,尤其是空间上靠近的像素点。这些依赖性携带着目标对象视觉感知上的重要信息。

经过调研对比,这里就选择SSIM算法。

二、下载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



opencv两张图片叠加 不同大小 python opencv对比两张图片相似度_哈希算法

三、 使用

在项目中引入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;  // 变化率,即差异

}

实际检测效果如下



opencv两张图片叠加 不同大小 python opencv对比两张图片相似度_哈希算法_02

这两幅图的相似度大约是92.21%,基本符合预期



opencv两张图片叠加 不同大小 python opencv对比两张图片相似度_直方图_03

这两幅图居然还有约18%的相似度,根据SSIM算法特性,这应该是图片大小的相似。

虽然也是拿来主义,毕竟我不是研究算法的大佬,需要站在巨人肩膀上干活~

宣君


- EOF -