简单而快速的方法来比较图像的相似性
我需要一个简单而快速的方法来比较两个图像的相似性。 即我想获得高价值,如果他们包含完全相同的东西,但可能有一些略有不同的背景,并可能被移动/调整了几个像素。
(更具体地说,如果这很重要:一张图片是一个图标,另一张图片是截图的一个分区,我想知道这个分区是否正好是图标。)
我手边有OpenCV ,但我还是不习惯。
到目前为止,我考虑过的一种可能性是:将这两幅图片分成10×10个单元格,对于这100个单元格中的每一个,比较颜色直方图。 那么我可以设置一些补偿阈值,如果我得到的价值高于这个阈值,我认为它们是相似的。
我还没有尝试过,但它的效果如何,但我想这将是足够好的。 图像已经非常相似(在我的使用情况),所以我可以使用一个相当高的阈值。
我想有很多其他可能的解决scheme,这可能会或多或less地工作(因为任务本身非常简单,因为我只是想检测相似性,如果他们真的很相似)。 你会build议什么?
有几个关于从图像获取签名/指纹/散列的非常相关/类似的问题:
- OpenCV / SURF如何从描述符中生成图像哈希/指纹/签名?
- 图像指纹比较许多图像的相似性
- 近乎重复的图像检测
- OpenCV:指纹图像,并与数据库进行比较 。
- 更多 , 更多 , 更多 , 更多 , 更多 , 更多
另外,我偶然发现了这些具有获取指纹function的实现:
- pHash
- imgSeek ( GitHub repo )(GPL)基于论文的Fast Multiresolution Image Querying
- 图像匹配 。 非常相似,我正在寻找。 与pHash类似,基于任何types图像的图像签名,Goldberg等人 使用Python和Elasticsearch。
- iqdb
- ImageHash 。 支持pHash。
关于感知图像哈希的一些讨论: 在这里
有点偏离:有很多方法来创buildaudio指纹。 MusicBrainz是一种为歌曲提供基于指纹的查询的networking服务, 在他们的wiki中有很好的概述 。 他们现在正在使用AcoustID 。 这是为了find确切的(或大多数确切的)匹配。 为了find类似的匹配(或者如果你只有一些片段或高噪音),看看Echoprint 。 一个相关的SO问题在这里 。 所以这似乎解决了audio。 所有这些解决scheme都很好。
一般来说,关于模糊search的更通用的问题就是这里 。 例如存在局部敏感的散列和最近邻居search 。
屏幕截图或图标是否可以转换(缩放,旋转,倾斜…)? 有很多方法可以帮助你:
- @carlosdc提到的简单的欧几里得距离 (不适用于转换的图像,并且需要一个阈值)。
- (标准化)交叉相关 – 一个简单的指标,你可以用它来比较图像区域。 它比简单的欧几里德距离更强大,但对转换后的图像不起作用,您将再次需要一个阈值。
- 直方图比较 – 如果使用标准化的直方图,此方法运行良好,不受仿射变换的影响。 问题是确定正确的阈值。 它也对颜色变化(亮度,对比度等)非常敏感。 你可以把它和前两个结合起来
- 显着点/区域的探测器 – 如MSER(最大稳定极值区域) , SURF或SIFT 。 这些都是非常强大的algorithm,对于您的简单任务它们可能太复杂了。 好的是,你不必拥有一个只有一个图标的确切区域,这些探测器足够强大,find正确的匹配。 这些方法的一个很好的评价是在本文: 局部不变特征探测器:一项调查 。
其中大部分已经在OpenCV中实现 – 例如参见cvMatchTemplate方法(使用直方图匹配): http ://dasl.mem.drexel.edu/~noahKuntz/openCVTut6.html。 显着点/区域检测器也可用 – 请参阅OpenCV特征检测 。
我最近面临同样的问题,为了解决这个问题(简单而快速的algorithm来比较两个图像),我贡献了一个img_hash模块来opencv_contrib,你可以从这个链接中find细节。
img_hash模块提供了六种图像哈希algorithm,相当容易使用。
代码示例
起源lena
模糊莱娜
调整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
- 换class攻击:0.229728
- resize的攻击:0.229358
每种algorithm的优缺点
img_hash的性能也不错
与PHash库进行速度比较(来自ukbench的100张图片)
如果你想知道这些algorithm的推荐阈值,请检查这篇文章( 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 )。
屏幕截图是否只包含图标? 如果是这样,那么这两个图像的L2距离就足够了。 如果L2距离不起作用,下一步就是尝试一些简单而又完善的方法,比如: Lucas-Kanade 。 我确信在OpenCV中可用。
如果你想得到一个有关这两张图片的相似度的指标,我build议你从SSIM指标的指标。 它更符合人眼。 这里有一篇关于它的文章: 结构相似性指数
它也是在OpenCV中实现的,可以用GPU加速OpenCV SSIM和GPU
如果您可以确保将模板(图标)与testing区域精确alignment,那么任何旧的像素差异总和都将起作用。
如果alignment只是稍微偏离一点,那么在find像素差异的总和之前,可以用cv :: GaussianBlur低通两个图像。
如果alignment的质量可能很差,那么我会推荐一个面向梯度的直方图,或者OpenCV的一个方便的关键点检测/描述符algorithm(如SIFT或SURF )。
如果匹配相同的图像 – 代码为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 }
快速。 但对照明/视点等变化不强
如果你想比较图像的相似性,我build议你使用OpenCV。 在OpenCV中,很less有特征匹配和模板匹配。 对于特征匹配,有SURF,SIFT,FAST等检测器。 你可以用它来检测,描述和匹配图像。 之后,您可以使用特定索引查找两个图像之间的匹配数。