尽快获得缓冲图像和缓冲图像部分的平均颜色

Get average color on bufferedimage and bufferedimage portion as fast as possible

我正在尝试在图像中查找图像。我这样做是为了桌面自动化。此时此刻,我力求快,而不是精确。因此,我决定仅根据相同的平均颜色来匹配相似图像。

如果我在桌面上选择几个图标,例如:

我会搜索最后一个(我还在想这个文件是什么):

您可以清楚地看到最有可能匹配的内容:

在不同的情况下,这可能不起作用。但是,当给定图像大小时,它应该非常可靠且速度快如闪电。

我可以截图为BufferedImage对象:

MSWindow window = MSWindow.windowFromName("Firefox", false);
BufferedImage img = window.screenshot();
//Or, if I can estimate smaller region for searching:
BufferedImage img2 = window.screenshotCrop(20,20,50,50);

当然,要搜索的图片将从保存在文件中的模板中加载:

BufferedImage img = ImageIO.read(...whatever goes in there, I'm still confused...);

我解释了我所知道的,以便我们可以专注于唯一的问题:

这里速度取胜。在这种特殊情况下,我认为它比代码可读性更有价值。

我认为无论你做什么,你都会有一个O(wh)操作,其中w是你的宽度,h是你的高度。

因此,我将post这个(幼稚的)解决方案来完成你问题的第一部分,因为我认为没有更快的解决方案。

/*
 * 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);
}

有一种恒定时间方法可用于查找图像矩形截面的平均颜色,但它需要线性预处理。这对你来说应该没问题。此方法还可用于查找 3 维数组或问题的任何更高维模拟中的直角棱柱的平均值。我将使用灰度示例,但只需重复该过程即可轻松扩展到 3 个或更多通道。

假设我们有一个二维数组,我们称之为“img”。

第一步是生成一个相同维度的新数组,其中每个元素包含原始图像中所有值的总和,这些值位于边界该元素和图像左上角元素的矩形内。

您可以使用以下方法在线性时间内构建这样的图像:

int width = 1920;
int height = 1080;

//source data
int[] img = GrayScaleScreenCapture();
int[] helperImg = int[width * height]

for(int y = 0; y < height; ++y)
{
    for(int x = 0; x < width; ++x)
    {
        int total = img[y * width + x];

        if(x > 0)
        {
            //Add value from the pixel to the left in helperImg
            total += helperImg[y * width + (x - 1)];
        }

        if(y > 0)
        {
            //Add value from the pixel above in helperImg
            total += helperImg[(y - 1) * width + x];
        }

        if(x > 0 && y > 0)
        {
            //Subtract value from the pixel above and to the left in helperImg
            total -= helperImg[(y - 1) * width + (x - 1)];
        }

        helperImg[y * width + x] = total;
    }
}

现在我们可以使用 helperImg 在常数时间内求 img 给定矩形内所有值的总和:

//Some Rectangle with corners (x0, y0), (x1, y0) , (x0, y1), (x1, y1)
int x0 = 50;
int x1 = 150;
int y0 = 25;
int y1 = 200;

int totalOfRect = helperImg[y1 * width + x1];

if(x0 > 0)
{
    totalOfRect -= helperImg[y1 * width + (x0 - 1)];
}

if(y0 > 0)
{
    totalOfRect -= helperImg[(y0 - 1) * width + x1];
}

if(x0 > 0 && y0 > 0)
{
    totalOfRect += helperImg[(y0 - 1) * width + (x0 - 1)];
}

最后,我们简单地用totalOfRect除以矩形的面积求平均值:

int rWidth = x1 - x0 + 1;
int rheight = y1 - y0 + 1;

int meanOfRect = totalOfRect / (rWidth * rHeight);

这是一个基于 的完整 BufferedImage 版本,具有可调采样精度(步长)。

public static Color getAverageColor(BufferedImage bi) {
    int step = 5;

    int sampled = 0;
    long sumr = 0, sumg = 0, sumb = 0;
    for (int x = 0; x < bi.getWidth(); x++) {
        for (int y = 0; y < bi.getHeight(); y++) {
            if (x % step == 0 && y % step == 0) {
                Color pixel = new Color(bi.getRGB(x, y));
                sumr += pixel.getRed();
                sumg += pixel.getGreen();
                sumb += pixel.getBlue();
                sampled++;
            }
        }
    }
    int dim = bi.getWidth()*bi.getHeight();
    // Log.info("step=" + step + " sampled " + sampled + " out of " + dim + " pixels (" + String.format("%.1f", (float)(100*sampled/dim)) + " %)");
    return new Color(Math.round(sumr / sampled), Math.round(sumg / sampled), Math.round(sumb / sampled));
}