尽快获得缓冲图像和缓冲图像部分的平均颜色
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));
}
我正在尝试在图像中查找图像。我这样做是为了桌面自动化。此时此刻,我力求快,而不是精确。因此,我决定仅根据相同的平均颜色来匹配相似图像。
如果我在桌面上选择几个图标,例如:
我会搜索最后一个(我还在想这个文件是什么):
您可以清楚地看到最有可能匹配的内容:
在不同的情况下,这可能不起作用。但是,当给定图像大小时,它应该非常可靠且速度快如闪电。
我可以截图为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);
这是一个基于
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));
}