javacolorsawtrgbawtrobot

Find color with tolerance in bufferedimage


I'm writing a method that will attempt to find a color in a bufferedImage. At the moment, the method works by taking a screencap, and then scanning the image for a specific color. Now I'd like to add some RGB tolerence, so if the user is trying to find color (1, 3, 5) with tolerance 1, any color +-1 R, B or G will return true.

I could solve this by first generating a arrayList of RGB values that work, and then for each pixel I could go through the array and check with each value. The problem is that would probably get VERY slow for high tolerances on large images.

Is there a more efficient or possibly a built in way I can do this? Here is my method as it stands right now. Thank you!

public static Point findColor(Box searchArea, int color){
    System.out.println("Test");
    BufferedImage image = generateScreenCap(searchArea);
    for (int i = 0; i < image.getWidth(); i++) {
        for (int j = 0; j < image.getHeight(); j++) {
            if((image.getRGB(i, j)*-1)==color){
                return new Point(i + searchArea.x1, j + searchArea.y1);
            }
        }
    }
    return new Point(-1, -1);
}

Edit: I'm using the int RGB values for all comparisons, so instead of Color[1, 1, 1], I use Color.getRGB() which returns a negative int which I convert to positive for end user simplicity.


Solution

  • You need to compare RGB values and not the "whole" color if you want to have a custom tolerance. Here is the code, it is not tested, but you get the idea :

    public static Point findColor(Box searchArea, int r, int g, int b, int tolerance) {
        // Pre-calc RGB "tolerance" values out of the loop (min is 0 and max is 255)
        int minR = Math.max(r - tolerance, 0);
        int minG = Math.max(g - tolerance, 0);
        int minB = Math.max(b - tolerance, 0);
        int maxR = Math.min(r + tolerance, 255);
        int maxG = Math.min(g + tolerance, 255);
        int maxB = Math.min(b + tolerance, 255);
    
        BufferedImage image = generateScreenCap(searchArea);
        for (int i = 0; i < image.getWidth(); i++) {
            for (int j = 0; j < image.getHeight(); j++) {
                // get single RGB pixel
                int color = image.getRGB(i, j);
    
                // get individual RGB values of that pixel
                // (could use Java's Color class but this is probably a little faster)
                int red = (color >> 16) & 0x000000FF;
                int green = (color >> 8) & 0x000000FF;
                int blue = (color) & 0x000000FF;  
    
                if ( (red >= minR && red <= maxR) &&
                     (green >= minG && green <= maxG) &&
                     (blue >= minB && blue <= maxB) ) 
                    return new Point(i + searchArea.x1, j + searchArea.y1);
            }
        }
        return new Point(-1, -1);
    }