我需要一种简单快速的方法来比较两个图像的相似性。即我希望得到一个很高的价值,如果它们包含完全相同的东西,但可能有一些稍微不同的背景,可能会移动/调整几个像素。
(更具体的是,如果重要的话:一张图片是一个图标而另一张图片是截图的子区域,我想知道该子区域是否与图标完全相同。)
我手边有 OpenCV ,但我仍然不习惯。
到目前为止我想到的一种可能性:将两张图片分成10x10个单元格,对于这100个单元格中的每一个,比较颜色直方图。然后我可以设置一些补偿阈值,如果我得到的值高于该阈值,我认为它们是相似的。
我还没有尝试过它的效果如何,但我想它会足够好。图像已经非常相似(在我的用例中),所以我可以使用相当高的阈值。
我想有很多其他可能的解决方案可以或多或少地工作(因为任务本身非常简单,因为我只想检测相似性,如果它们非常相似)。你会建议什么?
关于从图像中获取签名/指纹/哈希,有一些非常相关/类似的问题:
另外,我偶然发现了这些具有获取指纹功能的实现:
关于感知图片哈希的一些讨论:here
有点offtopic:有很多方法可以创建音频指纹。 MusicBrainz是一种为歌曲提供基于指纹的查找的网络服务,其good overview in their wiki为AcoustID。他们现在正在使用Echoprint。这是为了找到精确(或大部分精确)的匹配。要查找类似的匹配(或者如果您只有一些片段或高噪音),请查看here。相关的SO问题是here。所以这似乎解决了音频问题。所有这些解决方案都很有效。
关于模糊搜索的一般性问题是locality-sensitive hashing。例如。有nearest neighbor search和{{3}}。
答案 0 :(得分:99)
屏幕截图或图标可以转换(缩放,旋转,倾斜......)吗?我脑子里有很多方法可以帮助你:
其中大部分已在OpenCV中实现 - 请参阅cvMatchTemplate方法(使用直方图匹配):http://dasl.mem.drexel.edu/~noahKuntz/openCVTut6.html。突出点/面积检测器也可用 - 请参阅OpenCV Feature Detection。
答案 1 :(得分:36)
我最近面临同样的问题,为了解决这个问题(简单快速的算法来比较两个图像),我一劳永逸地向opencv_contrib贡献img_hash module,你可以从{{3}找到详细信息}。
img_hash模块提供了六种图像哈希算法,非常易于使用。
代码示例
this link来源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给我们最好的结果
每种算法的优缺点
img_hash的表现也很好
与PHash库进行速度比较(来自ukbench的100张图片)
如果您想了解这些算法的建议阈值,请查看此帖子()。 如果您对如何衡量img_hash模块的性能(包括速度和不同的攻击)感兴趣,请查看此链接(http://qtandopencv.blogspot.my/2016/06/introduction-to-image-hash-module-of.html)。
答案 2 :(得分:10)
屏幕截图是否仅包含图标?如果是这样,两个图像的L2距离可能就足够了。如果L2距离不起作用,则下一步是尝试简单且完善的内容,例如:Lucas-Kanade。我确信在OpenCV中可用。
答案 3 :(得分:5)
如果你想获得两张图片相似度的索引,我建议你从SSIM索引的指标。它与人眼更加一致。这是一篇关于它的文章:Structural Similarity Index
它也在OpenCV中实现,可以通过GPU加速:OpenCV SSIM with GPU
答案 4 :(得分:4)
如果您可以确保模板(图标)与测试区域的精确对齐,那么任何旧的像素差异都将起作用。
如果对齐只是一点点偏移,那么在找到像素差异之和之前,你可以使用cv::GaussianBlur低通两个图像。
如果对齐的质量可能很差,那么我建议使用Histogram of Oriented Gradients或OpenCV方便的关键点检测/描述符算法(例如SIFT或SURF)。< / p>
答案 5 :(得分:3)
如果匹配相同的图像 - 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
答案 6 :(得分:2)
如果你想比较图像的相似性,我建议你使用OpenCV。在OpenCV中,很少有功能匹配和模板匹配。对于特征匹配,有SURF,SIFT,FAST等探测器。您可以使用它来检测,描述然后匹配图像。之后,您可以使用特定索引查找两个图像之间的匹配数。
答案 7 :(得分:0)
Hu invariant moments 是非常强大的工具来比较两个图像