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

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

我手头有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距离

// 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
}

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

其他回答

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

我最近也面临同样的问题,为了一劳永逸地解决这个问题(简单快速的算法来比较两张图片),我贡献了一个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)。

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

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

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

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