用java查找图片中的图片?

我想要的是以图片的forms分析屏幕输入。 我希望能够在更大的图像中识别图像的一部分,并在更大的图像中获得其坐标。 例:

框

必须位于

大图

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

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

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

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

可能的方法

在这种情况下,问题与互相关和卷积的信号处理概念密切相关,这通常使用FFT实现,因为它非常快(在名称中!)。 这是您链接到的方法中使用的内容, FFTW库在尝试这样的实现时可能会有用,因为它有Java包装器。 如本问题所示,使用互相关非常有效,以及着名的waldo问题。

另一种选择是不使用所有像素进行比较,而是仅使用更容易找到且更可能是唯一的特征。 这将需要像SIFT , SURF或许多其他function之一的特征描述符。 您需要找到两个图像中的所有要素,然后查找与模板图像中具有相似位置的要素。 有了这种方法,我建议你使用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文件格式,因为它不使用有损压缩,巧妙地扭曲图像中的颜色。

比较图像

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

蛮力实施

这是一个使用蛮力方法搜索图像的简单实现。 使用您的示例图像,它发现位于(139,55)的位置是具有最佳匹配(看起来正确)的区域的左上角位置。 在我的电脑上运行大约需要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图像处理库中的一个也可能有用:

  • 马文项目
  • ImageJ的

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

你想要的是通过掩码/边界找到图片块。

它可以在没有外部库的情况下完成。 在低级别,每张图片都是数字矩阵,你的面具也是数字矩阵。 您可以直接扫描大矩阵,找到遵循掩码定义的规则的区域。

示例

大矩阵:

 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] )。 为了使算法更好地工作,匹配意味着不是精确的数字匹配,但可能有一些偏差+ -5或类似的东西。