为什么我在图像功能中查找图像与非完全匹配?

时间:2015-01-27 16:57:15

标签: java image computer-vision bufferedimage

我正在尝试在图像中实现各种查找图像的方法。我开始严格匹配。让我们为我们正在搜索的图片image和我们正在 中搜索的图片bigImage

  /** Finds image in bigImage by exact pixel match (all pixels must be exactly the same color).
   * 
   * @param image the smaller image you want to find
   * @param bigImage the big image you're searching in
   * @return Rect object describing the location where the small image was found. Returns null if nothing was found.
   */
  public static Rect findByExactMatch(BufferedImage image, BufferedImage bigImage) {
    //I marked these final so that I don't accidentally change them later
    final int iw = image.getWidth();
    final int ih = image.getHeight();
    final int bw = bigImage.getWidth();
    final int bh = bigImage.getHeight();

    //Loop from 0 to big image width/height MINUS the small image width/height
    //The MINUS there is, because once you are at the end, the small image overlaps to undefined area
    for(int rect_x=0, mrx=bw-iw; rect_x<mrx; rect_x++) {
      for(int rect_y=0, mry=bh-ih; rect_y<mry; rect_y++) {
          //This is where pixel looping begins
          int x = 0;
          int y = 0;
          for (; x < iw; x++) {
              for (; y < ih; y++) {
                //Get RGB returns 0x00rrggbb
                if(image.getRGB(x, y)!=
                    bigImage.getRGB(x+rect_x, y+rect_y)) {
                  //If the color does not match, break back to the rectangular search
                  //WITHOUT -1 THE VALUE OVERFLOWS ON NEXT ITERATION (damnit, debuged this like an idiot!!!)
                  y = x = Integer.MAX_VALUE-1;
                  break;
                }
              }
          }
          //This statement asks if the loop ended normally
          // - otherwise, the x and y are MAX_INT and greater than iw
          if(x==iw) {
            return Rect.byWidthHeight(rect_x, rect_y, iw, ih); 
          }
      }
    }
    //Nothing found - return null
    return null;
  }

如您所见,该功能非常简单,大部分代码都是注释。前两个循环移动我们正在比较的位置的 frame ,而内循环比较小图像和当前偏移处的大图像。

然后用法如下:

   public static void main(String[] args)
   {
     //The small image to search for
     BufferedImage thing = loadFromPath("thing.png");
     //The big image to search in
     BufferedImage screenshot = loadFromPath("screenshot.png");
     if(thing!=null && screenshot!=null) {
       Rect pos = autoclick.ScreenWatcher.findByExactMatch(thing, screenshot);
       if(pos!=null) {
          System.out.println("Found object: "+pos);
          //Draw rectangle on discovered position
          Graphics2D graph = screenshot.createGraphics();
          graph.setColor(Color.RED);
          graph.drawRect(pos.top, pos.left, pos.width, pos.height);
          graph.dispose();
          //Save the file for review
          try {
            ImageIO.write(screenshot, "png", new File("output.png"));
          } catch (IOException ex) {
            Logger.getLogger("wtf goes here?").log(Level.SEVERE, null, ex);
          }
       }
     }
   }

不幸的是,它似乎不起作用(红框显示程序识别的位置):

searching that failed and found just white area

然而,当我向我的朋友抱怨时,并且想告诉他邪恶的代码不想工作时,它突然起作用了:

the case when the match was found

需要注意的重要一点是,如果图片的第一列不是白色,则可以。或者在我看来。

我制作了一个包含所有必要文件的测试项目:https://gist.github.com/Darker/f08b2fbf1795af9ebbe2。默认情况下,它需要&#39; thing.png&#39;作为image和&#39; screenshot.png&#39;作为'bigImage。

1 个答案:

答案 0 :(得分:1)

您的内部匹配循环(x和y)被错误编码;仔细看看。在循环之前初始化两个循环变量,这意味着当第一列中没有不匹配时,y已递增到ih的值,绕过x的所有后续传递的y循环-loop。

修复它的一种方法是将x和y初始化移动到它们各自的for语句(它们所属的位置)并在for(rect_y...)语句上放置一个标签。在像素不匹配上,而不是中断,使用continue recty中止x和y循环。这样就无需操作x / y和人工检查。

         for (rect_x ...) {
label:       for (rect_y...) {
                 for (x = 0; ...) {
                     for (y = 0; ....) {
                         if (mismatch) {
                             continue label;
                         }
                      }
                 }
                 return "match";
             }
         }
         return null;

为了减少错综复杂,您还可以将内部两个循环重构为一个单独的方法,并在发生不匹配时简单地使用return来打破循环。