使用图像的类似图像搜索
我正在开展一个项目,其中将检查两个图像的相似性,如“Google Image Search by image”。
我搜索了谷歌以及各种网站,包括stackoverflow,并了解了各种技术,如直方图,筛选,傅里叶变换,像素抓取等。
事情太复杂了,我无法理解成为这个领域的初学者。
我的问题是:
-
我从哪里开始? 是否有任何书籍或网站提供有关如何实际使用这些技术的教程?
-
是否有可以为此目的实施的新技术?
我想开始通过颜色搜索图像,然后如果可能的话可能会有其他属性。
首选语言是Java。
这个主题有一个类似的主题,但它是几年前写的。
为了这个目的,我已经为http://sourceforge.net/projects/imgndxr/提供了一个名为Images相似搜索器的工具作为免费软件。
它使用两个库:
- LIRE: http : //www.semanticmetadata.net/lire/
LIRE(Lucene Image REtrieval)库提供了一种基于颜色和纹理特征检索图像和照片的简单方法。 LIRE为基于内容的图像检索(CBIR)创建了Lucene图像特征索引。 有几种不同的低级function,例如MPEG-7 ScalableColor,ColorLayout和EdgeHistogram,Auto Color Correlogram,PHOG,CEDD,JCD,FCTH等等。 此外,LIRE提供了用于搜索索引和结果浏览的简单和扩展方法。 LIRE使用基于散列的近似索引可以很好地扩展到数百万个图像。 LIRE库和LIRE Demo应用程序以及所有源代码均在Gnu GPL许可下提供。
- LUCENE: http : //lucene.apache.org/core/
Apache LuceneTM是一个完全用Java编写的高性能,function齐全的文本搜索引擎库。 它是一种适用于几乎所有需要全文搜索的应用程序的技术,尤其是跨平台搜索。
Apache Lucene是一个可供免费下载的开源项目。 请使用右侧的链接访问Lucene。
这取决于您的使用案例。 图像是通用的,还是在类似的照明条件和视角下拍摄的?
可以根据模型的复杂性对方法进行分类。 从本质上讲,我们可以区分直接与基于特征的方法。
直接(或基于强度)方法试图通过基于重叠区域中的强度差异最小化误差函数来(迭代地)估计相似性。 每个图像必须表示具有相同比例,视角等的完全相同的场景,以便重叠它们。 可以通过计算和方差(SSD)或ZSSD,相关系数(CC),互信息(MI)和相关比(RC)来实现相似性测量。
直接方法可以用于例如检查新video压缩算法的工作情况。 看看下面的文章:
http://docs.opencv.org/trunk/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.html
另一方面,我们可以谈论基于特征的方法。 他们尝试在点,线或其他几何实体之间建立对应关系以估计相机参数。 当图像的结构单元(像素)不包含有关其内容的足够信息时,它们可能很有用。 因此,基于特征的方法试图用数学特征(n维向量)表示图像内容,然后使用分类器来比较这些特征以获得关于它们的相似性的度量。
通过将图像调整为8×8缩略图,然后获取每个图像中相应像素之间的8位RGB色差的均方误差,我得到了满意的结果。
步骤1.创建缩略图:
BufferedImage img = ImageIO.read(src); Image thumbnail = img.getScaledInstance(8, 8, Image.SCALE_AREA_AVERAGING);
检查https://community.oracle.com/docs/DOC-983611 ,了解为什么我选择较慢的SCALE_AREA_AVERAGING
不是更新,更快的方法。
步骤2.使用Java转换Image到BufferedImage的toBufferedImage
方法将Image
缩略图 转换为BufferedImage 。 将结果放入List
。
步骤3.计算均方误差
此方法采用两个尺寸相同的缩略图大小的图像并返回差异。 零意味着图像非常相似:
public static double compare(BufferedImage img1, BufferedImage img2) { int width1 = img1.getWidth(); int width2 = img2.getWidth(); int height1 = img1.getHeight(); int height2 = img2.getHeight(); if ((width1 != width2) || (height1 != height2)) { throw new IllegalArgumentException("Error: Images dimensions mismatch"); } int diff2 = 0; for (int i = 0; i < height1; i++) { for (int j = 0; j < width1; j++) { int rgb1 = img1.getRGB(j, i); int rgb2 = img2.getRGB(j, i); int r1 = (rgb1 >> 16) & 0xff; int g1 = (rgb1 >> 8) & 0xff; int b1 = (rgb1) & 0xff; int r2 = (rgb2 >> 16) & 0xff; int g2 = (rgb2 >> 8) & 0xff; int b2 = (rgb2) & 0xff; diff2 += Math.pow(r1 - r2, 2) + Math.pow(g1 - g2, 2) + Math.pow(b1 - b2, 2); } } return diff2 * 1.0 / (height1*width1); }
步骤4.实施搜索
这通过简单地找到具有最小差异的图像来工作。 根据您的使用情况,您可能还需要设置一个阈值,高于该阈值不会返回任何图像。 在我的应用程序中,始终向用户显示最佳匹配,以便用户可以决定它是否是正确的图像,因此不需要硬编码阈值。
public BufferedImage findImage(List haystack, BufferedImage needle) { double lastDiff = Double.MAX_VALUE; BufferedImage winner = null; for(BufferedImage candidate: haystack) { double diff = compare(candidate, needle); if(diff < lastDiff) { lastDiff = diff; winner = candidate; } } return winner; }