简单快速的图像相似度比较方法


192

我需要一种简单快速的方法来比较两个图像的相似性。也就是说,如果它们包含完全相同的内容,但背景可能略有不同,并且可能会移动/调整几个像素,则我希望获得较高的值。

(更具体的说,这很重要:一张图片是图标,另一张图片是屏幕截图的子区域,我想知道该子区域是否恰好是图标。)

我手头有OpenCV,但我仍然不习惯。

到目前为止,我考虑过的一种可能性是:将两张图片分成10x10个单元格,然后针对这100个单元格中的每一个,比较颜色直方图。然后,我可以设置一些虚构的阈值,如果我得到的值高于该阈值,那么我认为它们是相似的。

我还没有尝试过这种方法,但是我想它已经足够了。这些图像已经非常相似(在我的用例中),因此我可以使用很高的阈值。

我猜有很多其他可行的解决方案,它们或多或少都可以工作(因为任务本身非常简单,我只想在相似性非常高的情况下才检测相似性)。你有什么建议?


关于从图像中获取签名/指纹/哈希,存在一些非常相关/相似的问题:

另外,我偶然发现了这些实现具有指纹功能的实现:

关于感知图像哈希的一些讨论:这里


有点题外话:存在许多创建音频指纹的方法。MusicBrainz是一项提供基于指纹的歌曲查找功能的网络服务,其Wiki中很好的概述。他们现在正在使用AcoustID。这是用于查找完全一致(或几乎完全一致)的匹配项。要查找类似的匹配项(或者如果您仅有一些片段或高噪音),请查看Echoprint。一个相关的SO问题在这里。因此,似乎音频已解决。所有这些解决方案都运行良好。

关于模糊搜索的一般问题在这里出现。例如,存在局部敏感的哈希最近邻居搜索


1
图像指纹识别可能会有所帮助吗?stackoverflow.com/questions/596262/…–
GWW

Wasserstein指标(也称为地球移动者的距离(EMD))是人们似乎不知道的东西,但是在这里可以提供您想要的东西。
mmgp 2013年


嗨,我想出了改进的dHash -我称其为IDHash:github.com/Nakilon/dhash-vips
Nakilon

Answers:


107

屏幕截图或图标可以变形(缩放,旋转,倾斜...)吗?我头顶上有很多方法可以帮助您:

  • @carlosdc提到的简单欧几里得距离(不适用于转换后的图像,您需要一个阈值)。
  • (归一化)互相关 -一种简单的指标,可用于比较图像区域。它比简单的欧式距离更健壮,但不适用于变换后的图像,因此您将再次需要一个阈值。
  • 直方图比较 -如果使用归一化的直方图,此方法效果很好,并且不受仿射变换的影响。问题是确定正确的阈值。它还对颜色变化(亮度,对比度等)非常敏感。您可以将其与前两个结合使用。
  • 显着点/区域的检测器 -例如MSER(最大稳定末端区域)SURFSIFT。这些算法非常健壮,对于您的简单任务而言可能过于复杂。好消息是,您不必具有仅一个图标的确切区域,这些检测器功能强大,可以找到正确的匹配项。本文对这些方法进行了很好的评估:局部不变特征检测器:一项调查

其中大多数已在OpenCV中实现-例如,参见cvMatchTemplate方法(使用直方图匹配):http ://dasl.mem.drexel.edu/~noahKuntz/openCVTut6.html 。突出点/区域检测器也可用-请参阅OpenCV特征检测


1
可以缩放或稍微移动它。图标的背景也会不同。我尝试进行直方图比较,但得到了很多误报。我还尝试了欧几里德距离,但也会产生太多误报(但也许我可以对图标中的alpha值进行更好的处理)。我将进一步尝试,否则我将检查MSER,SURF或SIFT。
艾伯特2010年

1
另一个想法-如果在应用sobel运算符后对图像进行直方图比较,则行不通吗?那只会比较边缘的相似性。可能有效还是无效,具体取决于背景的“前卫性”。
卡雷尔·佩特拉内克

44

我最近也面临着同样的问题,要彻底解决此问题(简单快速的算法来比较两个图像),我将img_hash模块贡献给opencv_contrib,您可以从此链接中找到详细信息。

img_hash模块提供了六种图像哈希算法,非常易于使用。

代码示例

起源莱娜起源莱娜

模糊莱娜模糊莱娜

调整莉娜调整莉娜

莱娜班莱娜班

#include <opencv2/core.hpp>
#include <opencv2/core/ocl.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/img_hash.hpp>
#include <opencv2/imgproc.hpp>

#include <iostream>

void compute(cv::Ptr<cv::img_hash::ImgHashBase> algo)
{
    auto input = cv::imread("lena.png");
    cv::Mat similar_img;

    //detect similiar image after blur attack
    cv::GaussianBlur(input, similar_img, {7,7}, 2, 2);
    cv::imwrite("lena_blur.png", similar_img);
    cv::Mat hash_input, hash_similar;
    algo->compute(input, hash_input);
    algo->compute(similar_img, hash_similar);
    std::cout<<"gaussian blur attack : "<<
               algo->compare(hash_input, hash_similar)<<std::endl;

    //detect similar image after shift attack
    similar_img.setTo(0);
    input(cv::Rect(0,10, input.cols,input.rows-10)).
            copyTo(similar_img(cv::Rect(0,0,input.cols,input.rows-10)));
    cv::imwrite("lena_shift.png", similar_img);
    algo->compute(similar_img, hash_similar);
    std::cout<<"shift attack : "<<
               algo->compare(hash_input, hash_similar)<<std::endl;

    //detect similar image after resize
    cv::resize(input, similar_img, {120, 40});
    cv::imwrite("lena_resize.png", similar_img);
    algo->compute(similar_img, hash_similar);
    std::cout<<"resize attack : "<<
               algo->compare(hash_input, hash_similar)<<std::endl;
}

int main()
{
    using namespace cv::img_hash;

    //disable opencl acceleration may(or may not) boost up speed of img_hash
    cv::ocl::setUseOpenCL(false);

    //if the value after compare <= 8, that means the images
    //very similar to each other
    compute(ColorMomentHash::create());

    //there are other algorithms you can try out
    //every algorithms have their pros and cons
    compute(AverageHash::create());
    compute(PHash::create());
    compute(MarrHildrethHash::create());
    compute(RadialVarianceHash::create());
    //BlockMeanHash support mode 0 and mode 1, they associate to
    //mode 1 and mode 2 of PHash library
    compute(BlockMeanHash::create(0));
    compute(BlockMeanHash::create(1));
}

在这种情况下,ColorMomentHash给我们最好的结果

  • 高斯模糊攻击:0.567521
  • 转移攻击:0.229728
  • 调整攻击强度:0.229358

每种算法的优缺点

不同攻击下的性能

img_hash的性能也不错

与PHash库进行速度比较(来自ukbench的100张图像) 计算性能 比较表现

如果您想了解这些算法的建议阈值,请查看此帖子(http://qtandopencv.blogspot.my/2016/06/introduction-to-image-hash-module-of.html)。如果您对我如何测量img_hash模块的性能(包括速度和其他攻击)感兴趣,请检查此链接(http://qtandopencv.blogspot.my/2016/06/speed-up-image-hashing-of -opencvimghash.html)。


11

屏幕截图是否仅包含图标?如果是这样,则两个图像的L2距离可能就足够了。如果L2距离不起作用,则下一步是尝试一些简单而完善的方法,例如:Lucas-Kanade。我确定OpenCV中可用。


该分区仅包含图标(带有一些随机背景)或其他内容。我想看看是哪种情况。虽然,它可能会稍微移动或调整大小,这就是为什么我不确定是否可以看一下距离(以任何标准)的原因。但我将尝试使用缩小版本。
艾伯特2010年

6

如果要获取有关两张图片相似性的索引,建议您从SSIM索引的指标中进行建议。它更符合人眼。这是一篇有关它的文章:结构相似性指数

它也在OpenCV中实现,并且可以通过GPU进行加速:具有GPU的OpenCV SSIM


5

如果可以确保模板(图标)与测试区域精确对齐,那么任何旧的像素差异总和都可以使用。

如果对齐仅需要一点点时间,则可以在找到像素差异之和之前,使用cv :: GaussianBlur对两个图像进行低通。

如果对齐质量可能很差,那么我建议您使用“定向梯度直方图”或OpenCV便捷的关键点检测/描述符算法(例如SIFTSURF)之一。


4

如果要匹配相同的图像-L2距离代码

// Compare two images by getting the L2 error (square-root of sum of squared error).
double getSimilarity( const Mat A, const Mat B ) {
if ( A.rows > 0 && A.rows == B.rows && A.cols > 0 && A.cols == B.cols ) {
    // Calculate the L2 relative error between images.
    double errorL2 = norm( A, B, CV_L2 );
    // Convert to a reasonable scale, since L2 error is summed across all pixels of the image.
    double similarity = errorL2 / (double)( A.rows * A.cols );
    return similarity;
}
else {
    //Images have a different size
    return 100000000.0;  // Return a bad value
}

快速。但对照明/视点等的更改不可靠。 来源


2

如果您想比较图像的相似性,建议您使用OpenCV。在OpenCV中,很少有功能匹配和模板匹配。对于特征匹配,有SURF,SIFT,FAST等检测器。您可以使用它来检测,描述和匹配图像。之后,您可以使用特定的索引来查找两个图像之间的匹配数。


1
您说:“此后,您可以使用特定的索引来查找两个图像之间的匹配数。” 说两张图片“包含”同一物体的最小匹配数是多少?
INES马丁斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.