为什么我的在图像中查找图像功能匹配非精确匹配?
Why does my find image in image function match non-exact matches?
我正在尝试实现在图像中查找图像的各种方法。我从严格匹配开始。让我们为正在搜索的图像设置 image
,为正在搜索的图像设置 bigImage
in.
/** 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);
}
}
}
}
不幸的是,它似乎不起作用(红框显示程序识别的位置):
然而,当我向我的朋友抱怨,并想告诉他邪恶的代码不起作用时,它突然起作用了:
需要注意的重要一点是,如果图像的第一列不是白色,它就可以工作。或者在我看来是这样。
我制作了一个包含所有可用文件的测试项目:https://gist.github.com/Darker/f08b2fbf1795af9ebbe2。默认情况下,它期望 'thing.png' 作为 image
和 'screenshot.png' 作为 `bigImage.
您的内部匹配循环(x 和 y)编码错误;仔细看看。您在循环之前初始化 both 循环变量,这意味着当第一列中没有不匹配时,y 已递增到 ih 的值,绕过所有后续遍历的 y 循环x 循环的。
修复它的一种方法是将 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
来中断循环。
我正在尝试实现在图像中查找图像的各种方法。我从严格匹配开始。让我们为正在搜索的图像设置 image
,为正在搜索的图像设置 bigImage
in.
/** 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);
}
}
}
}
不幸的是,它似乎不起作用(红框显示程序识别的位置):
然而,当我向我的朋友抱怨,并想告诉他邪恶的代码不起作用时,它突然起作用了:
需要注意的重要一点是,如果图像的第一列不是白色,它就可以工作。或者在我看来是这样。
我制作了一个包含所有可用文件的测试项目:https://gist.github.com/Darker/f08b2fbf1795af9ebbe2。默认情况下,它期望 'thing.png' 作为 image
和 'screenshot.png' 作为 `bigImage.
您的内部匹配循环(x 和 y)编码错误;仔细看看。您在循环之前初始化 both 循环变量,这意味着当第一列中没有不匹配时,y 已递增到 ih 的值,绕过所有后续遍历的 y 循环x 循环的。
修复它的一种方法是将 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
来中断循环。