代码之家  ›  专栏  ›  技术社区  ›  Albert

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

  •  175
  • Albert  · 技术社区  · 14 年前

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

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

    我有 OpenCV 但我还是不习惯。

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

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

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


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

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

    关于感性图像散列的一些讨论: here


    有点离题:有很多方法可以创建音频指纹。 MusicBrainz ,一个为歌曲提供基于指纹的查找的Web服务,具有 good overview in their wiki . 他们正在使用 AcoustID 现在。这是为了找到精确的(或大部分精确的)匹配。为了找到相似的匹配(或者如果你只有一些片段或高噪声),看看 Echoprint . 一个相关的问题是 here . 所以这似乎是为音频解决的。所有这些解决方案都非常有效。

    一般来说,关于模糊搜索的一个更一般的问题是 here . 例如,有 locality-sensitive hashing nearest neighbor search .

    7 回复  |  直到 14 年前
        1
  •  99
  •   Karel Petranek    14 年前

    屏幕截图或图标是否可以转换(缩放、旋转、倾斜…)在我的头脑中有很多方法可以帮助你:

    • 简单欧几里得距离 如@carlosdc所述(不适用于转换后的图像,您需要一个阈值)。
    • (Normalized) Cross Correlation -可用于图像区域比较的简单指标。它比简单的欧几里得距离更坚固,但不适用于转换后的图像,您将再次需要一个阈值。
    • 直方图比较 -如果使用标准化的柱状图,此方法工作良好,不受仿射变换的影响。问题是确定正确的阈值。它对颜色变化(亮度、对比度等)也非常敏感。你可以把它和前两个结合起来。
    • 突出点/区域探测器 -例如 MSER (Maximally Stable Extremal Regions) , SURF SIFT . 这些是非常强大的算法,对于您的简单任务来说,它们可能太复杂了。好的是,你不需要只有一个图标的精确区域,这些探测器的功能足以找到正确的匹配。本文对这些方法进行了一个很好的评估: Local invariant feature detectors: a survey .

    其中大部分已经在opencv中实现-例如,请参见cvmatchtemplate方法(使用柱状图匹配): http://dasl.mem.drexel.edu/~noahKuntz/openCVTut6.html . 还提供了突出点/区域探测器-请参见 OpenCV Feature Detection .

        2
  •  36
  •   StereoMatching    7 年前

    我最近也面临同样的问题,为了解决这个问题(简单快速的算法比较两个图像),我贡献了 img_hash module 要打开“控制”,您可以从 this link .

    img_哈希模块提供六种图像哈希算法,非常容易使用。

    代码示例

    origin lena 起源Lena

    blur lena 模糊莉娜

    resize lena 调整Lena的大小

    shift lena 移动Lena

    #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

    每种算法的优缺点

    Performance under different attacks

    img_哈希的性能也很好

    与Phash库的速度比较(UKbench的100张图像) compute performance comparison performance

    如果你想知道这些算法的推荐阈值,请查看本文( http://qtandopencv.blogspot.my/2016/06/introduction-to-image-hash-module-of.html ) 如果您对我如何测量img_哈希模块的性能(包括速度和不同的攻击)感兴趣,请检查此链接( http://qtandopencv.blogspot.my/2016/06/speed-up-image-hashing-of-opencvimghash.html )

        3
  •  10
  •   carlosdc    14 年前

    屏幕截图是否只包含图标?如果是这样,两个图像的l2距离就足够了。如果二级距离不起作用,下一步就是尝试一些简单而成熟的方法,比如: Lucas-Kanade . 我相信在OpenCV中可以找到。

        4
  •  5
  •   Milan Tenk    11 年前

    如果您想获得关于两张图片相似性的索引,我建议您从度量中获得ssim索引。它更符合人眼。下面是一篇关于它的文章: Structural Similarity Index

    它也在OpenCV中实现,并且可以通过GPU加速: OpenCV SSIM with GPU

        5
  •  4
  •   rcv    14 年前

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

    如果对齐只差一点点,那么可以用 cv::GaussianBlur 在找到像素差之和之前。

    如果线路质量可能较差,我建议 Histogram of Oriented Gradients 或者OpenCV的一种方便的关键点检测/描述符算法(例如 SIFT SURF )

        6
  •  3
  •   kiranpradeep    10 年前

    如果用于匹配相同的图像-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
  •  2
  •   Hua Er Lim    12 年前

    如果您想比较图像的相似性,我建议您使用opencv。在OpenCV中,很少有特征匹配和模板匹配。对于特征匹配,有冲浪、筛选、快速等检测器。您可以使用它来检测、描述和匹配图像。之后,您可以使用特定的索引来查找两个图像之间的匹配数量。