我正在开展一个项目,其中将检查两个图像的相似性,例如“按图像搜索Google图像”。
我搜索了谷歌以及各种网站,包括stackoverflow,并了解了各种技术,如直方图,筛选,傅里叶变换,像素抓取,等。
事情太复杂了我理解成为这个领域的初学者。
我的问题是:
从哪里开始?是否有任何书籍或网站提供有关如何实际使用这些技术的教程?
是否有可以为此目的实施的新技术?
我想开始通过颜色搜索图像,然后再尝试其他属性。
首选语言是Java。
在这个主题上有一个类似的主题,但它是几年前写的。
答案 0 :(得分:2)
我已为此tool提供http://sourceforge.net/projects/imgndxr/名为图像相似性搜索器的免费软件http://www.semanticmetadata.net/lire/
它使用两个库:
LIRE(Lucene Image REtrieval)库提供了一种简单的方法 根据颜色和纹理检索图像和照片 特点。 LIRE为图像特征创建Lucene索引 基于内容的图像检索(CBIR)。几个不同的低级别 功能可用,例如MPEG-7 ScalableColor,ColorLayout和 EdgeHistogram,Auto Color Correlogram,PHOG,CEDD,JCD,FCTH等等 更多。此外,用于搜索索引的简单和扩展方法 和结果浏览由LIRE提供。 LIRE可以很好地扩展到 数百万图像具有基于散列的近似索引。 LIRE 库和LIRE Demo应用程序以及所有源代码都是 根据Gnu GPL许可证提供。
Apache LuceneTM是一种高性能,功能齐全的文本搜索 完全用Java编写的引擎库。这是一项适合的技术 几乎所有需要全文搜索的应用程序 跨平台。
Apache Lucene是一个可供免费下载的开源项目。 请使用右侧的链接访问Lucene。
答案 1 :(得分:0)
这取决于您的使用案例。图像是通用的,还是在类似的光照条件和视角下拍摄的?
可以根据模型的复杂性对方法进行分类。从本质上讲,我们可以区分直接与基于特征的方法。
直接(或基于强度)方法试图通过基于重叠区域中的强度差异最小化误差函数来(迭代地)估计相似性。每个图像必须表示具有相同比例,视角等的完全相同的场景,以便重叠它们。通过计算总和平方差(SSD)或ZSSD,相关系数(CC),互信息(MI)和相关比(RC)可以实现相似性测量。直接方法可能是有用的,例如检查新视频压缩算法的工作情况。看看下面的文章:
http://docs.opencv.org/trunk/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.html
另一方面,我们可以谈论基于特征的方法。他们尝试在点,线或其他几何实体之间建立对应关系以估计相机参数。当图像的结构单元(像素)不包含有关其内容的足够信息时,它们可能很有用。因此,基于特征的方法试图用数学特征(n维向量)表示图像内容,然后使用分类器来比较这些特征,以便得到关于它们相似性的度量。
答案 2 :(得分:0)
通过将图像调整为8x8缩略图,然后在每个图像中的相应像素之间获取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 converting Image to BufferedImage中的Image
方法将BufferedImage
缩略图转换为toBufferedImage
。将结果放入List<BufferedImage>
。
步骤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<BufferedImage> 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;
}
答案 3 :(得分:0)
我只是参考@Alex代码,并添加一些其他方法来从位置获取所有特定文件并逐一处理图像。
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
public class ImageSearch {
static HashMap<String, BufferedImage> imgMap = new HashMap<>();
public static void main(String imgToFind, String...img2)throws IOException{
System.out.println("---------------------Images is processing--------------------- ");
search("path/to/images");
System.out.println("----------------------Images processing is completed--------------------- ");
System.out.println("----------------------Searching Images---------------------");
System.out.println("Wiiner is :"+findImage(imgMap,getThumbnil(imgToFind)));
}
public static void search(String path) throws IOException{
File file = new File(path);
File[] fArray = file.listFiles();
for (File f : fArray) {
if (!f.isDirectory()) {
if(f.getAbsolutePath().toUpperCase().endsWith(".JPG")){
BufferedImage bufImage = getThumbnil(f.getAbsolutePath());
imgMap.put(f.getAbsolutePath(), bufImage);
}
}
if (f.isDirectory()) {
search(f.getAbsolutePath());
}
}
}
public static BufferedImage getThumbnil(String imgSrc) throws IOException{
BufferedImage img = ImageIO.read(new File(imgSrc));
Image thumbnail = img.getScaledInstance(8, 8, Image.SCALE_AREA_AVERAGING);
return toBufferedImage(thumbnail);
}
public static BufferedImage toBufferedImage(Image img)
{
if (img instanceof BufferedImage)
{
return (BufferedImage) img;
}
// Create a buffered image with transparency
BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
// Draw the image on to the buffered image
Graphics2D bGr = bimage.createGraphics();
bGr.drawImage(img, 0, 0, null);
bGr.dispose();
// Return the buffered image
return bimage;
}
public static String findImage(HashMap<String, BufferedImage> imgMap, BufferedImage needle) {
double lastDiff = Double.MAX_VALUE;
BufferedImage winner = null;
String winnerPath = "";
for(Map.Entry<String, BufferedImage> candidate: imgMap.entrySet()) {
String path = candidate.getKey();
BufferedImage bufferedImage = candidate.getValue();
double diff = compare(bufferedImage, needle);
if(diff < lastDiff) {
lastDiff = diff;
winner = bufferedImage;
System.out.println("path :"+path);
winnerPath = path;
}
}
return winnerPath;
}
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);
}
}
希望这会有所帮助。