Skip to content

Instantly share code, notes, and snippets.

@Darker
Created January 27, 2015 16:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Darker/f08b2fbf1795af9ebbe2 to your computer and use it in GitHub Desktop.
Save Darker/f08b2fbf1795af9ebbe2 to your computer and use it in GitHub Desktop.
/* License:
* Open source ftw. I doubt anybody would actually like to use this file.
* But if you want, use it, change it or share it under any license you like.
*/
package autoclick;
//Comment this out if you don't have the library
import com.sun.jna.platform.win32.WinDef;
//Used for exporting rect class as normal rectangle
import java.awt.Rectangle;
/**
*
* @author Jakub Mareda
*/
public class Rect {
public final int width;
public final int height;
public final int top;
public final int bottom;
public final int left;
public final int right;
/** The parameters go clockwise - top, right, bottom, left
* @param t top
* @param r right
* @param b bottom
* @param l left**/
public Rect(int t, int r, int b, int l) {
top = t;
bottom = b;
right = r;
left = l;
width = r-l;
height = b-t;
}
/** Can be used to define a point. In this case, width and height will be zero.
* @param x x offset of the point
* @param y y offset of the point**/
public Rect(int x, int y) {
top = bottom = y;
left = right = x;
width = height = 0;
}
/** Using WinDef.RECT from com.sun.jna.platform.win32.WinDef. Comment this out for
* if this library is not present. Java doesn't provide smarter approach.
* @param rect WinDef rect. Can be obtained for example by sirius.core.user32Ext.GetClientRect
**/
public Rect(WinDef.RECT rect) {
top = rect.top;
bottom = rect.bottom;
right = rect.right;
left = rect.left;
width = right-left;
height = bottom-top;
}
/** Define by starting offset (top, left) and width and height.
*
* @param t top offset (y axis)
* @param l left offset (x axis)
* @param w width (x axis)
* @param h height (y axis)
* @return
*/
public static Rect byWidthHeight(int t, int l, int w, int h) {
return new Rect(t, l+w, t+h, l);
}
/** Get the middle point of the rectangle.
* @return Rect of zero size that is in the middle of this rect.
*/
public Rect middle() {
return new Rect(width/2+left, height/2+top);
}
@Override
public String toString() {
return "autoclick.Rect["+top+", "+right+", "+bottom+", "+left+"] ("+width+" x "+height+")";
}
/** Get java.awt.Rectangle equal to this rectangle. Intelligent language
* would allow me to override (Rectangle). But Java really sucks.
* @return java.awt.Rectangle of the same size and position as this rectangle.
*/
public Rectangle toStdRectangle() {
return new Rectangle(top, left, width, height);
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package autoclick;
import java.awt.Color;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
/**
*
* @author Jakub
*/
/**All static. Set of methods to allow you finding locations on screen **/
public class ScreenWatcher {
public static Rect findByAvgColor(BufferedImage image, BufferedImage bigImage, float tolerance) {
return null;
}
public static Rect findByExactMatch(BufferedImage image, BufferedImage bigImage, float tolerance) {
image = (BufferedImage)image.getScaledInstance((int)(tolerance*image.getWidth()), (int)(tolerance*image.getHeight()), Image.SCALE_SMOOTH);
bigImage = (BufferedImage)bigImage.getScaledInstance((int)(tolerance*bigImage.getWidth()), (int)(tolerance*bigImage.getHeight()), Image.SCALE_SMOOTH);
return findByExactMatch(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;
}
/*int clr = image.getRGB(x, y);
int red = (clr & 0x00ff0000) >> 16;
int green = (clr & 0x0000ff00) >> 8;
int blue = clr & 0x000000ff;
image.setRGB(x, y, clr);*/
/** Utility methods **/
public static BufferedImage resampleImage(BufferedImage original, double xscale, double yscale) {
int w = original.getWidth();
int h = original.getHeight();
BufferedImage after = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
AffineTransform at = new AffineTransform();
at.scale(xscale, yscale);
AffineTransformOp scaleOp =
new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
after = scaleOp.filter(original, after);
return after;
}
/*
* Where bi is your image, (x0,y0) is your upper left coordinate, and (w,h)
* are your width and height respectively
*/
public static Color averageColor(BufferedImage bi, int x0, int y0, int w, int h) {
int x1 = x0 + w;
int y1 = y0 + h;
long sumr = 0, sumg = 0, sumb = 0;
for (int x = x0; x < x1; x++) {
for (int y = y0; y < y1; y++) {
Color pixel = new Color(bi.getRGB(x, y));
sumr += pixel.getRed();
sumg += pixel.getGreen();
sumb += pixel.getBlue();
}
}
int num = w * h;
return new Color(sumr / num, sumg / num, sumb / num);
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package autoclick;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
/**
*
* @author Jakub
*/
public class ScreenWatcherMain {
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);
}
}
}
}
public static BufferedImage loadFromPath(String path) {
File img = new File(path);
BufferedImage thing = null;
try {
thing = ImageIO.read(img);
}
catch(IOException e) {
System.err.println("Can't read '"+path+"': "+e);
}
return thing;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment