将 BufferedImage 转换为 ByteBuffer

Posted

技术标签:

【中文标题】将 BufferedImage 转换为 ByteBuffer【英文标题】:Converting BufferedImage to ByteBuffer 【发布时间】:2015-03-27 13:28:49 【问题描述】:

我正在尝试将缓冲图像转换为 ByteBuffer,但我得到了这个异常

java.awt.image.DataBufferInt cannot be cast to java.awt.image.DataBufferByte

谁能帮助我并提出一个好的转换方法。

来源:

public static ByteBuffer convertImageData(BufferedImage bi) 

    byte[] pixelData = ((DataBufferByte) bi.getRaster().getDataBuffer()).getData();
    //        return ByteBuffer.wrap(pixelData);
    ByteBuffer buf = ByteBuffer.allocateDirect(pixelData.length);
    buf.order(ByteOrder.nativeOrder());
    buf.put(pixelData);
    buf.flip();
    return buf;

这是我的目标

 ByteBuffer buf = convertImageData(image);

【问题讨论】:

当你创建一个缓冲图像时,你可以给它一个常数来指示图像的色彩空间。看起来您正在混合 int 和 byte 类型的图像。例如,docs.oracle.com/javase/7/docs/api/java/awt/image/…。您也可以使用docs.oracle.com/javase/7/docs/api/java/awt/image/… 来查找现有图像的类型 【参考方案1】:

您不能只是将任意数据缓冲区强制转换为 DataBufferByte,您需要确保它实际上是正确的类型:

ByteBuffer byteBuffer;
DataBuffer dataBuffer = bi.getRaster().getDataBuffer();

if (dataBuffer instanceof DataBufferByte) 
    byte[] pixelData = ((DataBufferByte) dataBuffer).getData();
    byteBuffer = ByteBuffer.wrap(pixelData);

else if (dataBuffer instanceof DataBufferUShort) 
    short[] pixelData = ((DataBufferUShort) dataBuffer).getData();
    byteBuffer = ByteBuffer.allocate(pixelData.length * 2);
    byteBuffer.asShortBuffer().put(ShortBuffer.wrap(pixelData));

else if (dataBuffer instanceof DataBufferShort) 
    short[] pixelData = ((DataBufferShort) dataBuffer).getData();
    byteBuffer = ByteBuffer.allocate(pixelData.length * 2);
    byteBuffer.asShortBuffer().put(ShortBuffer.wrap(pixelData));

else if (dataBuffer instanceof DataBufferInt) 
    int[] pixelData = ((DataBufferInt) dataBuffer).getData();
    byteBuffer = ByteBuffer.allocate(pixelData.length * 4);
    byteBuffer.asIntBuffer().put(IntBuffer.wrap(pixelData));

else 
    throw new IllegalArgumentException("Not implemented for data buffer type: " + dataBuffer.getClass());

如果您的 BufferedImage 是标准类型之一(BufferedImage.TYPE_* 不是 TYPE_CUSTOM),则上述应该可以工作。

请注意,可能存在特殊的DatBuffer 子类,并且可能将像素存储在多个存储区中,具有不同的字节顺序,可能是通道交错(而不是标准像素交错)等。所以上面的代码仍然不完全通用。

如果您要将这些ByteBuffers 传递给本机代码,使用allocateDirect(..) 并复制像素可能会更快,否则我认为使用wrap(..) 将使代码更简单,效率更高。

【讨论】:

是否可以避免大量字节从一个地方复制到另一个地方? 因为我们显然无法避免复制,所以我使用了ImageIO 解决方案,因为它是图像原生的。 @Dims 如果它适用于您的用例,当然可以。不过,使用ImageIO 将数据编码为文件格式总是比仅复制字节要慢。【参考方案2】:

如果您想对图像数据进行编码,您可能需要在此处使用 ImageIO。像这样的:

public static ByteBuffer convertImageData(BufferedImage bi) 
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    try 
        ImageIO.write(bi, "png", out);
        return ByteBuffer.wrap(out.toByteArray());
     catch (IOException ex) 
        //TODO
    
    return null;

Here is the list 支持的格式。

【讨论】:

虽然您的代码将编译和运行,但它确实做的事情与 OP 在他的代码块中试图实现的完全不同。代码似乎期望一个包含“原始”像素的字节缓冲区,他将在这里得到一个包含 PNG 文件的缓冲区。

以上是关于将 BufferedImage 转换为 ByteBuffer的主要内容,如果未能解决你的问题,请参考以下文章

将每个动画 GIF 帧转换为单独的 BufferedImage

将 JOGL 纹理转换为 BufferedImage

关于JAVA的问题:如何将BufferedImage转换为Image

如何将数字转换为字节?

在java中,如何将byte转为string

BufferedImage.getRGB 中的坐标超出范围