从 BufferedImage 获取 RGBA 字节数组 Java
Getting a RGBA byte array from a BufferedImage Java
我有一个 BufferedImage 并且想要获得格式为 R G B A(每个字节一个通道)的字节数组。我该怎么做?
从 BufferedImage#getRGB
开始,其中 returns ARGB
值与图像的 ColorModel
无关 - 检查其 documentation.
然后使用 ColorModel
的 default 实例获取组件(getRed()
、getGreen()
、...)或获取组件的 int
数组(例如 getComponents()
)。或者直接拆分 getRGB()
返回的值,格式在 ColorModel#getRGBdefault
.
中描述
最终图像(或其栅格数据)可以转换为TYPE_4BYTE_ABGR,所以栅格数据可以直接使用(只是猜测,我没有做过)
最简单的方法是使用 BufferedImage.getRGB
(尽管它的名字给了你 ARGB 值),并将打包的 int[]
转换为 byte[]
四倍长。输入可以是任何 ImageIO
可以读取的文件,PNG 就可以了。
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(new File(args[0]));
int[] argb = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
byte[] rgba = intARGBtoByteRGBA(argb);
}
private static byte[] intARGBtoByteRGBA(int[] argb) {
byte[] rgba = new byte[argb.length * 4];
for (int i = 0; i < argb.length; i++) {
rgba[4 * i ] = (byte) ((argb[i] >> 16) & 0xff); // R
rgba[4 * i + 1] = (byte) ((argb[i] >> 8) & 0xff); // G
rgba[4 * i + 2] = (byte) ((argb[i] ) & 0xff); // B
rgba[4 * i + 3] = (byte) ((argb[i] >> 24) & 0xff); // A
}
return rgba;
}
一种更有趣的方法是创建一个 BufferedImage
,它由已经采用 RGBA 格式的 byte[]
支持,如下所示:
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(new File(args[0]));
ComponentColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, image.getWidth(), image.getHeight(), image.getWidth() * 4, 4, new int[] {0, 1, 2, 3}, null); // R, G, B, A order
BufferedImage imageToo = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
// This array will be in the same R, G, B, A order
byte[] rgbaToo = ((DataBufferByte) raster.getDataBuffer()).getData();
// Draw the image onto the RGBA buffer, which will be updated immediately
Graphics2D g = imageToo.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.drawImage(image, 0, 0, null);
}
finally {
g.dispose();
}
}
以上示例中的哪一个更好用,取决于用例。
如果您只需要一次性转换,第一个可能更容易推理并且工作得很好。
如果您需要多次更新缓冲区,第二种方法可能会产生更好的性能。
PS:我对所有测试输入使用两种替代方法得到完全相同的结果, 除了 原始灰度(使用 ColorSpace.CS_GRAY
).我相信这是一个困扰 Java2D 用户多年的已知问题...
最终我只是使用 java 库 PNGJ 加载它,并以 RGBA 格式相应地加载每一行:
for(int j = 0; j < reader.imgInfo.rows;j++) {
IImageLine row = reader.readRow();
for(int b = 0; b < reader.imgInfo.cols; b++) {
int argb = ch == 3 ? ImageLineHelper.getPixelRGB8(row, b) : ch == 4 ? ImageLineHelper.getPixelARGB8(row, b) : 0xFF000000;
然后我将其转换为 RGBA:
pixels.write((argb & 0x00FF0000) >> 16);
pixels.write((argb & 0x0000FF00) >> 8 );
pixels.write((argb & 0x000000FF) >> 0 );//neatness lol
pixels.write((argb & 0xFF000000) >> 24);
我有一个 BufferedImage 并且想要获得格式为 R G B A(每个字节一个通道)的字节数组。我该怎么做?
从 BufferedImage#getRGB
开始,其中 returns ARGB
值与图像的 ColorModel
无关 - 检查其 documentation.
然后使用 ColorModel
的 default 实例获取组件(getRed()
、getGreen()
、...)或获取组件的 int
数组(例如 getComponents()
)。或者直接拆分 getRGB()
返回的值,格式在 ColorModel#getRGBdefault
.
最终图像(或其栅格数据)可以转换为TYPE_4BYTE_ABGR,所以栅格数据可以直接使用(只是猜测,我没有做过)
最简单的方法是使用 BufferedImage.getRGB
(尽管它的名字给了你 ARGB 值),并将打包的 int[]
转换为 byte[]
四倍长。输入可以是任何 ImageIO
可以读取的文件,PNG 就可以了。
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(new File(args[0]));
int[] argb = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
byte[] rgba = intARGBtoByteRGBA(argb);
}
private static byte[] intARGBtoByteRGBA(int[] argb) {
byte[] rgba = new byte[argb.length * 4];
for (int i = 0; i < argb.length; i++) {
rgba[4 * i ] = (byte) ((argb[i] >> 16) & 0xff); // R
rgba[4 * i + 1] = (byte) ((argb[i] >> 8) & 0xff); // G
rgba[4 * i + 2] = (byte) ((argb[i] ) & 0xff); // B
rgba[4 * i + 3] = (byte) ((argb[i] >> 24) & 0xff); // A
}
return rgba;
}
一种更有趣的方法是创建一个 BufferedImage
,它由已经采用 RGBA 格式的 byte[]
支持,如下所示:
public static void main(String[] args) throws IOException {
BufferedImage image = ImageIO.read(new File(args[0]));
ComponentColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
WritableRaster raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, image.getWidth(), image.getHeight(), image.getWidth() * 4, 4, new int[] {0, 1, 2, 3}, null); // R, G, B, A order
BufferedImage imageToo = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
// This array will be in the same R, G, B, A order
byte[] rgbaToo = ((DataBufferByte) raster.getDataBuffer()).getData();
// Draw the image onto the RGBA buffer, which will be updated immediately
Graphics2D g = imageToo.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.drawImage(image, 0, 0, null);
}
finally {
g.dispose();
}
}
以上示例中的哪一个更好用,取决于用例。
如果您只需要一次性转换,第一个可能更容易推理并且工作得很好。
如果您需要多次更新缓冲区,第二种方法可能会产生更好的性能。
PS:我对所有测试输入使用两种替代方法得到完全相同的结果, 除了 原始灰度(使用 ColorSpace.CS_GRAY
).我相信这是一个困扰 Java2D 用户多年的已知问题...
最终我只是使用 java 库 PNGJ 加载它,并以 RGBA 格式相应地加载每一行:
for(int j = 0; j < reader.imgInfo.rows;j++) {
IImageLine row = reader.readRow();
for(int b = 0; b < reader.imgInfo.cols; b++) {
int argb = ch == 3 ? ImageLineHelper.getPixelRGB8(row, b) : ch == 4 ? ImageLineHelper.getPixelARGB8(row, b) : 0xFF000000;
然后我将其转换为 RGBA:
pixels.write((argb & 0x00FF0000) >> 16);
pixels.write((argb & 0x0000FF00) >> 8 );
pixels.write((argb & 0x000000FF) >> 0 );//neatness lol
pixels.write((argb & 0xFF000000) >> 24);