将BufferedImage转换为ByteBuffer

6
我试图将一个缓冲图像转换为 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);

1
当您创建缓冲图像时,可以给它一个常量来指定图像的颜色空间。看起来您正在混合使用int和byte类型的图像。例如,http://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferedImage.html#TYPE_BYTE_INDEXED。您还可以使用http://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferedImage.html#getType%28%29来查找现有图像的类型。 - BretC
2个回答

5

你不能将任意的数据缓冲区强制转换为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是标准类型之一(TYPE_CUSTOM除外的BufferedImage.TYPE_*),上述代码应该可以工作。
请注意,特殊的DataBuffer子类可能存在,并且可能在多个存储器中存储像素,具有不同的字节顺序,可能是通道交错(而不是标准像素交错)等。因此,上述代码仍然不完全通用。
如果您要将这些ByteBuffer传递给本机代码,使用allocateDirect(..)并复制像素可能会更快,否则我认为使用wrap(..)将使代码更简单并且更有效率。

有没有可能避免从一个地方复制大量字节到另一个地方? - Dims
由于我们显然无法避免复制,我使用了ImageIO解决方案,因为它是图像的本地解决方案。 - Dims
如果适用于您的用例,当然可以。但是,使用ImageIO将数据编码为文件格式始终比仅复制字节要慢。 - Harald K

3

如果您想对图像数据进行编码,可以使用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;
}

这里是支持的格式列表


4
尽管您的代码可以编译并运行,但它实际上与OP在代码块中尝试实现的完全不同。该代码似乎期望一个包含“原始”像素的字节缓冲区,但在这里得到的是一个包含PNG文件的缓冲区。 - Harald K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接