用java在图片中查找图片?

Posted

技术标签:

【中文标题】用java在图片中查找图片?【英文标题】:Finding a picture in a picture with java? 【发布时间】:2012-09-17 21:46:20 【问题描述】:

我想要以图片的形式分析来自屏幕的输入。我希望能够在更大的图像中识别图像的一部分,并在更大的图像中获取它的坐标。示例:

必须位于

结果将是大图中图片的右上角和大图中部分的左下角。如您所见,图片的白色部分无关紧要,我基本上需要的只是绿框。有没有图书馆可以为我做这样的事情?运行时并不是真正的问题。

我想要做的只是生成一些随机像素坐标并识别大图中该位置的颜色,以便稍后快速识别绿色框。如果中间的白框是透明的,它会如何降低性能?

这个问题已经在 SO 上被问过好几次了,因为似乎没有一个答案。我发现我在 http://werner.yellowcouch.org/Papers/subimg/index.html 找到了解决方案。不幸的是它在 C++ 中,我什么都不懂。在 SO 上有一个 Java 实现会很好。

【问题讨论】:

嗨。来这里搜索 LoL 启动器自动化。 【参考方案1】:

这个问题通常很难回答,因为人们通常对什么算作图像匹配有不同的要求。有些人可能想要搜索可能与他们提供的模板图像具有不同大小或方向的图像,在这种情况下,需要使用比例或旋转不变的方法。有多种选择,例如寻找相似的纹理、特征或形状,但我将专注于只寻找与模板图像位置完全相同的颜色相似的像素的方法。这似乎最适合您的示例,该示例似乎属于 template matching 类别。

可能的方法

在这种情况下,问题与cross-correlation 和convolution 的信号处理概念密切相关,这通常使用FFT 来实现,因为它非常快(正如其名!)。这就是您linked 所使用的方法,FFTW 库在尝试这样的实现时可能很有用,因为它具有 Java 包装器。正如this 问题和著名的waldo 问题所示,使用互相关效果很好。

另一种选择是不使用所有像素进行比较,而是仅使用更容易找到且更有可能独特的特征。这将需要一个特征描述符,如 SIFT、SURF 或许多 others 之一。您需要找到两个图像中的所有特征,然后寻找与模板图像中位置相似的特征。使用这种方法,我建议您使用JavaCV。

您提到的随机猜测方法应该在可能的情况下快速工作,但不幸的是,它并不普遍适用,因为它仅适用于在正确位置附近产生紧密匹配的某些图像组合。

除非您使用外部库,否则 Java 中最简单的方法就是我所说的蛮力方法,尽管它有点慢。蛮力方法只涉及在整个图像中搜索与您正在寻找的图像最匹配的子区域。我将进一步解释这种方法。首先,您需要定义如何确定两个相同大小的图像之间的相似性。这可以通过对像素颜色之间的差异求和来完成,这需要定义 RGB 值之间的差异。

颜色相似度

确定两个 RGB 值之间差异的一种方法是使用欧式距离:

sqrt( (r1-r2)^2 + (g1-g2)^2 + (b1-b2)^2 )

可以使用与 RGB 不同的颜色空间,但由于您的子图像很可能几乎相同(而不仅仅是视觉上相似),这应该可以正常工作。如果您有 ARGB 颜色空间并且不希望半透明像素对结果产生太大影响,则可以使用:

a1 * a2 * sqrt( (r1-r2)^2 + (g1-g2)^2 + (b1-b2)^2 )

如果颜色具有透明度(假设 a1a2 介于 0 和 1 之间),则会给出较小的值。我建议您使用透明度而不是白色区域并使用 PNG 文件格式,因为它不使用会轻微扭曲图像颜色的有损压缩。

比较图像

要比较大小相同的图像,您可以将它们各自像素之间的差异相加。然后,此总和是差异的度量,您可以搜索图像中具有最低差异度量的区域。如果您甚至不知道图像是否包含子图像,这将变得更加困难,但这将通过具有高差异度量的最佳匹配来指示。如果需要,您还可以通过将其除以子图像的大小和最大可能的 RGB 差异 (sqrt(3) 与欧几里德距离和 RGB 值从 0 到 1 来将差异度量标准化为介于 0 和 1 之间)。零将是一个相同的匹配,任何接近一的东西都将尽可能不同。

蛮力实现

这是一个使用蛮力搜索图像的简单实现。通过您的示例图像,它发现 (139,55) 的位置是具有最佳匹配的区域的左上角位置(看起来正确)。在我的 PC 上运行大约需要 10 到 15 秒,并且该位置的标准化差异测量值约为 0.57。

 /**
 * Finds the a region in one image that best matches another, smaller, image.
 */
 public static int[] findSubimage(BufferedImage im1, BufferedImage im2)
   int w1 = im1.getWidth(); int h1 = im1.getHeight();
   int w2 = im2.getWidth(); int h2 = im2.getHeight();
   assert(w2 <= w1 && h2 <= h1);
   // will keep track of best position found
   int bestX = 0; int bestY = 0; double lowestDiff = Double.POSITIVE_INFINITY;
   // brute-force search through whole image (slow...)
   for(int x = 0;x < w1-w2;x++)
     for(int y = 0;y < h1-h2;y++)
       double comp = compareImages(im1.getSubimage(x,y,w2,h2),im2);
       if(comp < lowestDiff)
         bestX = x; bestY = y; lowestDiff = comp;
       
     
   
   // output similarity measure from 0 to 1, with 0 being identical
   System.out.println(lowestDiff);
   // return best location
   return new int[]bestX,bestY;
 

 /**
 * Determines how different two identically sized regions are.
 */
 public static double compareImages(BufferedImage im1, BufferedImage im2)
   assert(im1.getHeight() == im2.getHeight() && im1.getWidth() == im2.getWidth());
   double variation = 0.0;
   for(int x = 0;x < im1.getWidth();x++)
     for(int y = 0;y < im1.getHeight();y++)
        variation += compareARGB(im1.getRGB(x,y),im2.getRGB(x,y))/Math.sqrt(3);
     
   
   return variation/(im1.getWidth()*im1.getHeight());
 

 /**
 * Calculates the difference between two ARGB colours (BufferedImage.TYPE_INT_ARGB).
 */
 public static double compareARGB(int rgb1, int rgb2)
   double r1 = ((rgb1 >> 16) & 0xFF)/255.0; double r2 = ((rgb2 >> 16) & 0xFF)/255.0;
   double g1 = ((rgb1 >> 8) & 0xFF)/255.0;  double g2 = ((rgb2 >> 8) & 0xFF)/255.0;
   double b1 = (rgb1 & 0xFF)/255.0;         double b2 = (rgb2 & 0xFF)/255.0;
   double a1 = ((rgb1 >> 24) & 0xFF)/255.0; double a2 = ((rgb2 >> 24) & 0xFF)/255.0;
   // if there is transparency, the alpha values will make difference smaller
   return a1*a2*Math.sqrt((r1-r2)*(r1-r2) + (g1-g2)*(g1-g2) + (b1-b2)*(b1-b2));
 

我没看过,但也许这些 Java 图像处理库之一也可能有用:

Marvin project ImageJ

如果速度真的很重要,我认为最好的方法是使用互相关或使用外部库的特征描述符来实现。

【讨论】:

非常好的解决方案。明天早上会测试它:)【参考方案2】:

您想要的是按掩码/边界查找图片块。

它可以在没有外部库的情况下完成。在底层,每张图片都是一个数字矩阵,你的掩码也是一个数字矩阵。您可以线性扫描大矩阵并找到遵循掩码定义的规则的区域。

示例

大矩阵:

1 0 1 1 1 1 
0 1 0 1 0 0
0 0 0 1 1 1
0 1 1 0 0 0

面具:

1 1 1
1 0 0
1 1 1

应用此算法,您可以在右上角的大矩阵中检测匹配块,从而为您提供开始/结束矩阵索引,您可以以像素为单位计算这些值。

在实际问题中,您不会有一个数字集 [0, 1],而是更大的数字集 - 例如 byte ([0, 256])。为了让算法更好地工作,matched 意味着不是精确的数字匹配,但可能会有一些偏差 +-5 或类似的东西。

【讨论】:

以上是关于用java在图片中查找图片?的主要内容,如果未能解决你的问题,请参考以下文章

厉害了,用 Java 也能实现图片识别!

用AutoHotkey删除Word里图片后面的特定字符(Word查找图片的BUG)

java项目客户上传的图片放到哪好

java项目客户上传的图片放到哪好

word文档中如何替换图片

怎么找相似图