【问题标题】:Simple and fast method to compare images for similarity比较图像相似度的简单快速方法
【发布时间】:2011-05-10 22:25:18
【问题描述】:

我需要一种简单快速的方法来比较两张图片的相似度。 IE。如果它们包含完全相同的东西,但背景可能略有不同,并且可能会移动/调整几个像素,我想获得一个高值。

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

我手头有 OpenCV,但我还是不太习惯。

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

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

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


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

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

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


有点题外话:有很多方法可以创建音频指纹。 MusicBrainz,一个提供基于指纹的歌曲查找的网络服务,有一个good overview in their wiki。他们现在使用AcoustID。这是为了查找精确(或大部分精确)匹配。要查找相似的匹配项(或者如果您只有一些 sn-ps 或高噪音),请查看 Echoprint。一个相关的 SO 问题是here。所以这似乎解决了音频问题。所有这些解决方案都非常有效。

关于模糊搜索的一般性问题是here。例如。有locality-sensitive hashingnearest neighbor search

【问题讨论】:

标签: image-processing opencv computer-vision


【解决方案1】:

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

  • @carlosdc 提到的简单欧式距离(不适用于转换后的图像,您需要一个阈值)。
  • (Normalized) Cross Correlation - 一个简单的指标,可用于比较图像区域。它比简单的欧几里得距离更稳健,但不适用于转换后的图像,您将再次需要一个阈值。
  • 直方图比较 - 如果您使用归一化直方图,此方法效果很好,并且不受仿射变换的影响。问题在于确定正确的阈值。它对颜色变化(亮度、对比度等)也非常敏感。您可以将其与前两者结合使用。
  • 突出点/区域的检测器 - 例如MSER (Maximally Stable Extremal Regions)SURFSIFT。这些是非常强大的算法,对于您的简单任务来说它们可能太复杂了。好消息是您不必只有一个图标的确切区域,这些检测器足够强大,可以找到正确的匹配项。本文对这些方法进行了很好的评估:Local invariant feature detectors: a survey

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

【讨论】:

  • 它可以缩放或轻微移动。图标的背景也会有所不同。我尝试了直方图比较,但我得到了很多误报。我也尝试了欧几里得距离,但这也给出了太多的误报(但也许我可以更好地处理图标中的 alpha 值)。我会进一步尝试,否则我会检查 MSER、SURF 或 SIFT。
  • 另一个想法 - 如果您在应用 sobel 算子后使用图像的直方图比较,它不会起作用吗?那只会比较边缘的相似性。可能会也可能不会,这取决于背景的“前卫”程度。
  • 嘿,我想知道,随着最近神经网络的发展,现在有更好的神经方法吗?也许还更快。
  • 我不知道任何现成的解决方案,但您始终可以在包含您希望视为“相同”的转换的合成数据集上训练连体网络。也就是说,您可以生成带有噪声、轻微旋转和移位的图像,并训练网络为原始图像和转换后的图像提供相同的输出。
  • 最好也提一下 opencv 的 structural_similarity 函数,这是一个 SSIM 实现,并且几乎正是您需要标记“差异”的内容。然后可以将其与findContours 配对以获取具有差异的区域周围的边界框,然后使用模板匹配进一步构建以查看任何生成的 bbox 是否代表修改后的内容与重新定位的内容。
【解决方案2】:

我最近遇到了同样的问题,为了一劳永逸地解决这个问题(比较两个图像的简单快速算法),我向 opencv_contrib 贡献了一个img_hash module,您可以从this link 找到详细信息。

img_hash 模块提供六种图片哈希算法,非常好用。

代码示例

origin 莉娜

blur 莉娜

resize 莉娜

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 库的速度比较(来自 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)。

【讨论】:

  • 干得好!根据您的经验,对于不同纵横比的移位攻击,哪种算法是最好和快速的?
【解决方案3】:

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

【讨论】:

  • 子区域仅包含图标(带有一些随机背景)或其他内容。我想看看是什么情况。虽然,它可能会略微移动或调整大小,这就是为什么我不确定我是否可以只看距离(以任何标准)。但我会尝试缩小版本。
【解决方案4】:

如果你想得到一张关于两张图片相似度的指数,我建议你从metrics的SSIM指数。更符合人眼。这是一篇关于它的文章:Structural Similarity Index

OpenCV也实现了,可以用GPU加速:OpenCV SSIM with GPU

【讨论】:

    【解决方案5】:

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

    如果对齐只会有一点点偏差,那么您可以使用cv::GaussianBlur 对两个图像进行低通处理,然后再找到像素差异的总和。

    如果对齐的质量可能很差,那么我会推荐 Histogram of Oriented Gradients 或 OpenCV 方便的关键点检测/描述符算法之一(例如 SIFTSURF)。

    【讨论】:

      【解决方案6】:

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

      快。但对照明/视点等的变化并不稳健。 Source

      【讨论】:

        【解决方案7】:

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

        【讨论】:

        • 您说“之后,您可以使用特定索引查找两个图像之间的匹配数。”两个图像之间的最小匹配数可以是多少才能说它们“包含”同一个对象?
        【解决方案8】:

        Hu invariant moments 是比较两张图片的强大工具

        【讨论】:

          猜你喜欢
          • 2012-04-22
          • 1970-01-01
          • 2011-08-09
          • 2010-10-25
          • 1970-01-01
          • 1970-01-01
          • 2012-10-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多