image.getRaster().getDataBuffer() returns 负值数组
image.getRaster().getDataBuffer() returns array of negative values
这 answer suggests that it's over 10 times faster to loop pixel array 而不是使用 BufferedImage.getRGB。这种差异太重要了,不能在我的计算机视觉程序中忽略。出于这个原因,O 重写了我的 IntegralImage
使用像素数组计算积分图像的方法:
/* Generate an integral image. Every pixel on such image contains sum of colors or all the
pixels before and itself.
*/
public static double[][][] integralImage(BufferedImage image) {
//Cache width and height in variables
int w = image.getWidth();
int h = image.getHeight();
//Create the 2D array as large as the image is
//Notice that I use [Y, X] coordinates to comply with the formula
double integral_image[][][] = new double[h][w][3];
//Variables for the image pixel array looping
final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
//final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
//If the image has alpha, there will be 4 elements per pixel
final boolean hasAlpha = image.getAlphaRaster() != null;
final int pixel_size = hasAlpha?4:3;
//If there's alpha it's the first of 4 values, so we skip it
final int pixel_offset = hasAlpha?1:0;
//Coordinates, will be iterated too
//It's faster than calculating them using % and multiplication
int x=0;
int y=0;
int pixel = 0;
//Tmp storage for color
int[] color = new int[3];
//Loop through pixel array
for(int i=0, l=pixels.length; i<l; i+=pixel_size) {
//Prepare all the colors in advance
color[2] = ((int) pixels[pixel + pixel_offset] & 0xff); // blue;
color[1] = ((int) pixels[pixel + pixel_offset + 1] & 0xff); // green;
color[0] = ((int) pixels[pixel + pixel_offset + 2] & 0xff); // red;
//For every color, calculate the integrals
for(int j=0; j<3; j++) {
//Calculate the integral image field
double A = (x > 0 && y > 0) ? integral_image[y-1][x-1][j] : 0;
double B = (x > 0) ? integral_image[y][x-1][j] : 0;
double C = (y > 0) ? integral_image[y-1][x][j] : 0;
integral_image[y][x][j] = - A + B + C + color[j];
}
//Iterate coordinates
x++;
if(x>=w) {
x=0;
y++;
}
}
//Return the array
return integral_image;
}
问题是,如果我在 for
循环中使用此调试输出:
if(x==0) {
System.out.println("rgb["+pixels[pixel+pixel_offset+2]+", "+pixels[pixel+pixel_offset+1]+", "+pixels[pixel+pixel_offset]+"]");
System.out.println("rgb["+color[0]+", "+color[1]+", "+color[2]+"]");
}
这是我得到的:
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
...
那么我应该如何正确检索 BufferedImage
图像的像素阵列?
上面代码中的一个很容易被忽略的错误是 for
循环没有像您预期的那样循环。 for
循环更新 i
,而循环体使用 pixel
作为其数组索引。因此,您只会看到像素 1、2 和 3 的值。
除此之外:
具有负像素值的 "problem" 很可能是代码假定 BufferedImage
以 "pixel interleaved" 形式存储其像素,但是,它们存储 "pixel packed".也就是说,一个像素的所有样本(R、G、B 和 A)都存储在一个样本中,即一个 int。所有 BufferedImage.TYPE_INT_*
类型都是这种情况(而 BufferedImage.TYPE_nBYTE_*
类型是交错存储的)。
光栅中有负值是完全正常的,任何透明度小于 50%(不透明大于或等于 50%)的像素都会发生这种情况,因为 4 个样本是如何打包到int
,因为 int
是 Java 中的有符号类型。
在这种情况下,使用:
int[] color = new int[3];
for (int i = 0; i < pixels.length; i++) {
// Assuming TYPE_INT_RGB, TYPE_INT_ARGB or TYPE_INT_ARGB_PRE
// For TYPE_INT_BGR, you need to reverse the colors.
// You seem to ignore alpha, is that correct?
color[0] = ((pixels[i] >> 16) & 0xff); // red;
color[1] = ((pixels[i] >> 8) & 0xff); // green;
color[2] = ( pixels[i] & 0xff); // blue;
// The rest of the computations...
}
另一种可能性是,您创建了一个自定义类型图像 (BufferedImage.TYPE_CUSTOM
),每个样本实际上使用 32 位 unsigned int。这是可能的,但是,int
仍然是 Java 中的签名实体,因此您需要屏蔽掉符号位。使这个稍微复杂一点,在 Java -1 & 0xFFFFFFFF == -1
中,因为对 int
的任何计算仍然是 int
,除非你明确说明(在 byte
或 short
值将具有 "scaled up" 到 int
)。要获得正值,您需要使用这样的 long
值:-1 & 0xFFFFFFFFL
(即 4294967295
)。
在这种情况下,使用:
long[] color = new long[3];
for(int i = 0; i < pixels.length / pixel_size; i += pixel_size) {
// Somehow assuming BGR order in input, and RGB output (color)
// Still ignoring alpha
color[0] = (pixels[i + pixel_offset + 2] & 0xFFFFFFFFL); // red;
color[1] = (pixels[i + pixel_offset + 1] & 0xFFFFFFFFL); // green;
color[2] = (pixels[i + pixel_offset ] & 0xFFFFFFFFL); // blue;
// The rest of the computations...
}
我不知道你的图像是什么类型,所以我不能确定哪一张有问题,但它是其中之一。 :-)
PS:BufferedImage.getAlphaRaster()
可能是一种昂贵且不准确的判断图像是否具有 alpha 的方法。最好只使用 image.getColorModel().hasAlpha()
。另见 hasAlpha vs getAlphaRaster.
这 answer suggests that it's over 10 times faster to loop pixel array 而不是使用 BufferedImage.getRGB。这种差异太重要了,不能在我的计算机视觉程序中忽略。出于这个原因,O 重写了我的 IntegralImage
使用像素数组计算积分图像的方法:
/* Generate an integral image. Every pixel on such image contains sum of colors or all the
pixels before and itself.
*/
public static double[][][] integralImage(BufferedImage image) {
//Cache width and height in variables
int w = image.getWidth();
int h = image.getHeight();
//Create the 2D array as large as the image is
//Notice that I use [Y, X] coordinates to comply with the formula
double integral_image[][][] = new double[h][w][3];
//Variables for the image pixel array looping
final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
//final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
//If the image has alpha, there will be 4 elements per pixel
final boolean hasAlpha = image.getAlphaRaster() != null;
final int pixel_size = hasAlpha?4:3;
//If there's alpha it's the first of 4 values, so we skip it
final int pixel_offset = hasAlpha?1:0;
//Coordinates, will be iterated too
//It's faster than calculating them using % and multiplication
int x=0;
int y=0;
int pixel = 0;
//Tmp storage for color
int[] color = new int[3];
//Loop through pixel array
for(int i=0, l=pixels.length; i<l; i+=pixel_size) {
//Prepare all the colors in advance
color[2] = ((int) pixels[pixel + pixel_offset] & 0xff); // blue;
color[1] = ((int) pixels[pixel + pixel_offset + 1] & 0xff); // green;
color[0] = ((int) pixels[pixel + pixel_offset + 2] & 0xff); // red;
//For every color, calculate the integrals
for(int j=0; j<3; j++) {
//Calculate the integral image field
double A = (x > 0 && y > 0) ? integral_image[y-1][x-1][j] : 0;
double B = (x > 0) ? integral_image[y][x-1][j] : 0;
double C = (y > 0) ? integral_image[y-1][x][j] : 0;
integral_image[y][x][j] = - A + B + C + color[j];
}
//Iterate coordinates
x++;
if(x>=w) {
x=0;
y++;
}
}
//Return the array
return integral_image;
}
问题是,如果我在 for
循环中使用此调试输出:
if(x==0) {
System.out.println("rgb["+pixels[pixel+pixel_offset+2]+", "+pixels[pixel+pixel_offset+1]+", "+pixels[pixel+pixel_offset]+"]");
System.out.println("rgb["+color[0]+", "+color[1]+", "+color[2]+"]");
}
这是我得到的:
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
rgb[-16777216, -16777216, -16777216]
rgb[0, 0, 0]
...
那么我应该如何正确检索 BufferedImage
图像的像素阵列?
上面代码中的一个很容易被忽略的错误是 for
循环没有像您预期的那样循环。 for
循环更新 i
,而循环体使用 pixel
作为其数组索引。因此,您只会看到像素 1、2 和 3 的值。
除此之外:
具有负像素值的 "problem" 很可能是代码假定 BufferedImage
以 "pixel interleaved" 形式存储其像素,但是,它们存储 "pixel packed".也就是说,一个像素的所有样本(R、G、B 和 A)都存储在一个样本中,即一个 int。所有 BufferedImage.TYPE_INT_*
类型都是这种情况(而 BufferedImage.TYPE_nBYTE_*
类型是交错存储的)。
光栅中有负值是完全正常的,任何透明度小于 50%(不透明大于或等于 50%)的像素都会发生这种情况,因为 4 个样本是如何打包到int
,因为 int
是 Java 中的有符号类型。
在这种情况下,使用:
int[] color = new int[3];
for (int i = 0; i < pixels.length; i++) {
// Assuming TYPE_INT_RGB, TYPE_INT_ARGB or TYPE_INT_ARGB_PRE
// For TYPE_INT_BGR, you need to reverse the colors.
// You seem to ignore alpha, is that correct?
color[0] = ((pixels[i] >> 16) & 0xff); // red;
color[1] = ((pixels[i] >> 8) & 0xff); // green;
color[2] = ( pixels[i] & 0xff); // blue;
// The rest of the computations...
}
另一种可能性是,您创建了一个自定义类型图像 (BufferedImage.TYPE_CUSTOM
),每个样本实际上使用 32 位 unsigned int。这是可能的,但是,int
仍然是 Java 中的签名实体,因此您需要屏蔽掉符号位。使这个稍微复杂一点,在 Java -1 & 0xFFFFFFFF == -1
中,因为对 int
的任何计算仍然是 int
,除非你明确说明(在 byte
或 short
值将具有 "scaled up" 到 int
)。要获得正值,您需要使用这样的 long
值:-1 & 0xFFFFFFFFL
(即 4294967295
)。
在这种情况下,使用:
long[] color = new long[3];
for(int i = 0; i < pixels.length / pixel_size; i += pixel_size) {
// Somehow assuming BGR order in input, and RGB output (color)
// Still ignoring alpha
color[0] = (pixels[i + pixel_offset + 2] & 0xFFFFFFFFL); // red;
color[1] = (pixels[i + pixel_offset + 1] & 0xFFFFFFFFL); // green;
color[2] = (pixels[i + pixel_offset ] & 0xFFFFFFFFL); // blue;
// The rest of the computations...
}
我不知道你的图像是什么类型,所以我不能确定哪一张有问题,但它是其中之一。 :-)
PS:BufferedImage.getAlphaRaster()
可能是一种昂贵且不准确的判断图像是否具有 alpha 的方法。最好只使用 image.getColorModel().hasAlpha()
。另见 hasAlpha vs getAlphaRaster.