TLDR:使用较小的图像并阅读this 论文。
您应该尝试消除@Piglet 提到的任何不必要的函数调用,但您绝对应该将颜色保留在一个直方图中,而不是 R、G 和 B 的单独直方图。除了摆脱额外的函数调用之外,我认为你可以做四件事来加速你的算法——创建和比较直方图——并减少内存使用(因为更少的页面缓存意味着更少的磁盘抖动和更快的速度)。
使用较小的图片
颜色直方图索引的优点之一是它相对独立于分辨率。物体的颜色不会随着图像的大小而改变。显然,这是有局限性的——想象一下尝试使用 1×1 图像匹配对象。但是,如果您的图像有数百万像素(就像现在大多数智能手机中的图像),您绝对应该调整它的大小。这些作者发现,只有 16×11 的图像分辨率仍能产生非常好的结果 [see page 17],但即使将尺寸缩小到 ~100×100 像素,仍应提供显着的加速效果。
BufferedImage 从Image 继承方法getScaledInstance,您可以使用它来获得更小的图像。
double scalingFactor = 0.25; //You need to choose this value to work with your images
int aSmallHeight = myBigImage.getHeight() * scalingFactor;
int aSmallWidth = myBigImage.getWidth() * scalingFactor;
Image smallerImage = myBigImage.getScaledInstance(aSmallWidth, aSmallHeight, SCALE_FAST);
减小图像大小是加快算法速度的最有效的方法。如果你什么都不做,至少要这样做。
使用每个颜色通道的更少信息
这不会对生成直方图产生太大影响,因为它实际上需要更多的计算,但它会显着加快比较直方图的速度。总体思路称为quantization。基本上,如果您有 0..255 范围内的红色值,它们可以表示为一个字节。在该字节中,某些位比其他位更重要。
考虑这个彩色示例图像。我在左上角放置了一个几乎任意的红色阴影,在其他每个角落,我忽略了红色通道中的一个或多个位(由颜色字节中的下划线表示)。我故意选择了一种颜色,其中包含很多位,以便我可以显示忽略位的“最坏”情况。 (“最佳”情况,当我们忽略零位时,对颜色没有影响。)
即使我们忽略了一点,右上角和左上角也没有太大区别。即使我们忽略了 3 位,左上角和左下角也有明显的差异,但差异很小。左上角和右下角非常不同,尽管我们只忽略了一位,因为它是最重要的位。通过战略性地忽略不太重要的位,您可以减小直方图的大小,这意味着 JVM 移动的次数更少,并且在比较它们时也更少的 bin。
这里有一些可靠的数字。目前,您有 28×28×28 = 16777216 个 bin。如果您忽略每个颜色通道中的 3 个最低有效位,您将得到
25×25×25 = 32768 个 bin,是您当前使用的 bin 数量的 1/512。您可能需要对您的一组图像进行试验,以了解何种程度的量化仍能产生可接受的结果。
量化实现起来非常简单。您可以通过执行位移操作来忽略最右边的位。
int numBits = 3;
int quantizedRed = pixelColor.getRed() >> numBits;
int quantizedGreen = pixelColor.getGreen() >> numBits;
int quantizedBlue = pixelColor.getBlue() >> numBits;
使用不同的色彩空间
虽然灰度可能更快,但您不应该使用灰度,因为这样会丢失所有颜色信息。当您使用颜色直方图匹配对象时,实际的色调或色度比某物的亮度或暗度更重要。 (其中一个原因是,照明强度可能在图像之间甚至在图像之间发生变化。)您可以使用其他颜色表示,而不需要使用 3 个颜色通道。
例如,L*a*b* (see also this) 使用一个通道 (L) 对亮度进行编码,并使用两个通道 (a, b) 对颜色进行编码。 a 和 b 通道的范围从 -100 到 100,因此如果您只使用 a 和 b 创建直方图,则只需要 40000 个 bin。只有 a 和 b 的直方图的缺点是您失去了记录黑白像素的能力。对于您的算法,其他颜色空间各有优缺点。
颜色空间之间的转换通常不是很困难,因为在 Internet 上可以免费获得许多现有的颜色空间转换功能实现。例如,这里是Java conversion from RGB to L*a*b*。
如果您确实选择使用不同的色彩空间,也要小心使用量化。您应该在进行色彩空间转换后应用任何量化,并且您将需要测试不同的量化级别,因为新的色彩空间可能比 RGB 对量化更敏感或更不敏感。我的偏好是将图像保留为 RGB,因为量化在减少 bin 数量方面已经非常有效。
使用不同的数据类型
我做了一些调查,发现BufferedImage 将图像存储为Raster,它使用SampleModel 来描述像素如何存储在数据缓冲区中。这意味着仅检索一个像素的值就有很多开销。如果您的图像存储为byte[] 或int[],您将获得更快的结果。您可以使用
获取
byte 数组
byte[] pixels = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData();
请参阅the answer to this previous question 了解更多信息和一些将其转换为二维数组的示例代码。
最后这件事可能没有太大区别,但我注意到您使用double 来存储您的直方图。您应该考虑 int 是否可以代替。在 Java 中,int 的最大值大于 20 亿,因此溢出应该不是问题(除非您正在制作超过 20 亿像素的图像的直方图,在这种情况下,请参阅我的第一点)。 int 使用的内存只有 double 的一半(当您有数千或数百万个直方图箱时,这很重要),并且对于许多数学运算,它们可以更快(尽管这取决于您的硬件)。
如果您想了解更多关于用于对象匹配的颜色直方图的信息,请直接访问源代码并阅读 1991 年以来的 Swain and Ballard's Color Indexing paper。