5

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

我通过 Google 和包括 stackoverflow 在内的各种网站进行搜索,并了解了各种技术,如直方图、筛选、傅立叶变换、像素抓取

。对于这个领域的初学者来说,这些东西太复杂了,我无法理解。

我的问题是:

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

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

我想开始通过颜色搜索图像,然后如果可能的话,稍后再搜索其他属性。

首选语言是Java。

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

4

4 回答 4

2

为此,我制作了一个名为“图像相似性搜索器”的工具,作为免费软件,可在http://sourceforge.net/projects/imgndxr/获得

它使用两个库:

LIRE(Lucene Image REtrieval)库提供了一种基于颜色和纹理特征来检索图像和照片的简单方法。LIRE 为基于内容的图像检索 (CBIR) 创建图像特征的 Lucene 索引。有几种不同的低级功能可用,例如 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。

于 2014-08-06T16:27:28.240 回答
2

通过将图像大小调整为 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.使用ImageBufferedImageJava 中将 Image 转换为 BufferedImagetoBufferedImage方法 将缩略图转换为。将结果放入.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;
}
于 2017-07-05T21:28:54.480 回答
1

我只是参考了@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);
    }

}

希望这会有所帮助。

于 2020-01-10T12:16:21.860 回答
0

这取决于您的用例。这些图像是通用的,还是在相似的照明条件和视角下拍摄的?


这些方法可以根据模型的复杂程度进行分类。本质上,我们可以区分直接方法和基于特征的方法。

直接(或基于强度)方法试图(迭代地)通过基于重叠区域的强度差异最小化误差函数来估计相似度。每个图像必须以相同的比例、视角等表示完全相同的场景,以便重叠。相似度测量可以通过计算平方和差 (SSD) 或 ZSSD、相关系数 (CC)、互信息 (MI) 和相关比 (RC) 来实现。

直接方法可能很有用,例如检查新的视频压缩算法的工作情况。看看下面的文章:

http://docs.opencv.org/trunk/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.html


另一方面,我们可以谈论基于特征的方法。他们试图建立点、线或其他几何实体之间的对应关系,以估计相机参数。当图像的结构单元(像素)不包含有关其内容的足够信息时,它们可能很有用。因此,基于特征的方法尝试用数学特征(n维向量)来表示图像内容,然后使用分类器来比较这些特征,以获得关于它们相似性的度量。

于 2014-08-07T11:05:43.543 回答