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

(更具体地说,如果这很重要的话:一张图片是一个图标,另一张图片是截图的一个子区域,我想知道这个子区域是否就是图标。)

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

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

我还没有试过它的效果如何,但我想它应该足够好了。图像已经非常相似(在我的用例中),所以我可以使用一个相当高的阈值。

我猜还有其他几十种可能的解决方案,它们或多或少会起作用(因为任务本身很简单,因为我只想检测相似性,如果它们真的非常相似)。你有什么建议吗?


关于从图像中获取签名/指纹/散列,有几个非常相关/相似的问题:

如何生成图像哈希/指纹/签名的描述符? 图像指纹可以比较多个图像的相似度 近重复图像检测 OpenCV:指纹图像和比对数据库。 更多,更多,更多,更多,更多,更多

此外,我偶然发现了这些实现,它们有这样的功能来获取指纹:

pHash 基于imgSeek (GitHub repo) (GPL)的快速多分辨率图像查询 图像匹配。跟我一直在找的东西很像。类似于pHash,基于任何类型图像的图像签名,Goldberg等人。使用Python和Elasticsearch。 iqdb ImageHash。支持pHash。 Image重复数据删除器(imagededup)。支持CNN, PHash, DHash, WHash, AHash。

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


有点跑题:有很多方法可以创建音频指纹。MusicBrainz,一个提供基于指纹的歌曲查找的网络服务,在他们的维基中有一个很好的概述。他们现在正在使用AcoustID。这是为了找到精确的(或大部分精确的)匹配。为了找到相似的匹配(或者如果你只有一些片段或高噪音),看看echopprint。这里有一个相关的SO问题。音频问题似乎解决了。所有这些解决方案都很有效。

这里有一个关于模糊搜索的更一般的问题。例如,有位置敏感哈希和最近邻居搜索。


截图中是否只有图标?如果是这样,两幅图像的L2距离可能就足够了。如果L2距离不起作用,下一步就是尝试一些简单而成熟的方法,比如:Lucas-Kanade。我确定OpenCV中有。


屏幕截图或图标可以转换(缩放、旋转、倾斜……)?我有很多方法可以帮助你:

Simple euclidean distance as mentioned by @carlosdc (doesn't work with transformed images and you need a threshold). (Normalized) Cross Correlation - a simple metrics which you can use for comparison of image areas. It's more robust than the simple euclidean distance but doesn't work on transformed images and you will again need a threshold. Histogram comparison - if you use normalized histograms, this method works well and is not affected by affine transforms. The problem is determining the correct threshold. It is also very sensitive to color changes (brightness, contrast etc.). You can combine it with the previous two. Detectors of salient points/areas - such as MSER (Maximally Stable Extremal Regions), SURF or SIFT. These are very robust algorithms and they might be too complicated for your simple task. Good thing is that you do not have to have an exact area with only one icon, these detectors are powerful enough to find the right match. A nice evaluation of these methods is in this paper: Local invariant feature detectors: a survey.

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


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

如果对齐只会有一点点偏差,那么你可以在找到像素差的和之前,用cv::GaussianBlur低通两个图像。

如果对齐的质量可能很差,那么我会建议使用定向梯度直方图或OpenCV方便的关键点检测/描述符算法(如SIFT或SURF)。


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


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

它也在OpenCV中实现,并且可以使用GPU进行加速:OpenCV SSIM with GPU


如果用于匹配相同的图像-代码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
}

快。但对光线/视点等的变化不稳定。 源


我最近也面临同样的问题,为了一劳永逸地解决这个问题(简单快速的算法来比较两张图片),我贡献了一个img_hash模块给opencv_contrib,你可以从这个链接找到详细信息。

Img_hash模块提供了6种图像哈希算法,使用起来相当简单。

代码示例

起源莉娜

模糊莉娜

调整莉娜

shift键莉娜

#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库的速度比较(100张图片来自ukbench)

如果你想知道这些算法的推荐阈值,请查看这篇文章(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)。


胡不变矩是比较两幅图像非常有力的工具


在双重库中使用哈希函数来检测(接近)相同的图像(免责声明:我也是作者)。这是一种简单而快速的方法来比较两个或多个图像的相似性。它使用预处理图像(灰度化、归一化和缩放)、计算图像哈希以及基于阈值对图像进行分组的多步过程来工作。