使用图像的类似图像搜索

时间:2014-08-06 16:21:00

标签: java image-processing histogram sift image-comparison

我正在开展一个项目,其中将检查两个图像的相似性,例如“按图像搜索Google图像”。

我搜索了谷歌以及各种网站,包括stackoverflow,并了解了各种技术,如直方图,筛选,傅里叶变换,像素抓取,等。
事情太复杂了我理解成为这个领域的初学者。

我的问题是:

  1. 从哪里开始?是否有任何书籍或网站提供有关如何实际使用这些技术的教程?

  2. 是否有可以为此目的实施的新技术?

  3. 我想开始通过颜色搜索图像,然后再尝试其他属性。

    首选语言是Java。

    在这个主题上有一个类似的主题,但它是几年前写的。

4 个答案:

答案 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许可证提供。

  • LUCENE:{{3}}
  

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);
    }

}

希望这会有所帮助。