如何在Java中将byte[]转换为BufferedImage?

36
我发帖是因为在Java中处理图片时遇到了一些困难。我想将一张图片转换成一个byte[]数组,然后再反向操作,在改变每个像素的RGB值后生成新的图片。我想使用这种方法是因为BufferedImage的setRGB()和getRGB()在处理大型图片时可能会过慢(如果我错了,请纠正我)。我阅读了一些帖子(例如这里)以获取包含每个像素的红色、绿色和蓝色值(当有4个单元格时还包括额外的alpha值)的byte[]数组,这对我来说非常有用且易于使用。以下是我用于获取此数组的代码(存储在我创建的PixelArray类中):
public PixelArray(BufferedImage image)
{
    width = image.getWidth();
    height = image.getHeight();
    DataBuffer toArray = image.getRaster().getDataBuffer();
    array = ((DataBufferByte) toArray).getData();
    hasAlphaChannel = image.getAlphaRaster() != null;
}

我很苦恼,因为我没找到任何有效的方法将这个byte[]数组转换成一个新的图像,如果我想要转换图片(例如删除蓝色/绿色值仅保留红色值)。我尝试了以下两种解决方案:

1)创建DataBuffer对象,然后创建SampleModel,最后创建WritableRaster和BufferedImage(还需要额外的ColorModel和Hashtable对象)。由于显然我没有所需的所有信息(我不知道BufferedImage()构造函数的Hashtable是什么),所以它并没有起作用。

2)使用ByteArrayInputStream。这种方法不起作用,因为ByteArrayInputStream期望的byte[]数组与我的数组完全不同:它表示文件的每个字节,而不是每个像素组件(每个像素有3-4个字节)…

有人可以帮帮我吗?

5个回答

47

试试这个:

private BufferedImage createImageFromBytes(byte[] imageData) {
    ByteArrayInputStream bais = new ByteArrayInputStream(imageData);
    try {
        return ImageIO.read(bais);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

这真的是最有效的方法吗?我只是创建一个新的BufferedImage,以与第一篇帖子中相同的方式获取byte[],然后使用System.ArrayCopy。当您尝试同时写入byte[]和图像的Graphics对象时,它确实会失去同步。 - Mark Jeronimus
24
在一个快速测试中,你的方法返回了 null,因为我的 ImageIO 没有一个理解原始流的 ImageInputStreamSPI - Mark Jeronimus

16

我尝试了这里提到的方法,但由于某种原因它们都不起作用。使用ByteArrayInputStreamImageIO.read(...)返回null,而byte[] array = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();返回图像数据的副本,而不是对它们的直接引用(也可以参见这里)。

然而,下面的方法对我有效。假设图像数据的维度和类型已知。让byte[] srcbuf成为要转换为BufferedImage的数据缓冲区。那么,

  1. 创建一个空白图像,例如

  2. img=new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
    
  3. 将数据数组转换为Raster,并使用setData填充图像,即:

  4. img.setData(Raster.createRaster(img.getSampleModel(), new DataBufferByte(srcbuf, srcbuf.length), new Point() ) );
    

9
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
byte[] array = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(pixelArray, 0, array, 0, array.length);

当你尝试使用结果图片的Graphics对象时,这种方法往往会失去同步。如果你需要在图片上绘制,可以构造第二个图片(可以是持久的,即不是每次都构建,而是可重复利用),然后使用drawImage将第一个图片绘制到它上面。


同样地,如果图像的类型为TYPE_INT_ARGB,则可以通过以下方式访问数组:int[] array = ((DataBufferInt) image.getRaster().getDataBuffer()).getData()。每个整数表示图像中的一个像素:位24-31是alpha通道,16-23是红色通道,8-15是绿色通道,0-7是蓝色通道。 - Kay

6

有几个人点赞了评论,认为被接受的答案是错误的。

如果被接受的答案不起作用,可能是因为Image.IO不支持您正在尝试的图像类型,例如tiff图像。

要使其正常工作,您需要添加一个额外的jar来处理图像类型。

您可以通过以下方式将jai-imageio-core-1.3.1.jar添加到类路径中:

    <!-- https://mvnrepository.com/artifact/com.github.jai-imageio/jai-imageio-core -->
    <dependency>
        <groupId>com.github.jai-imageio</groupId>
        <artifactId>jai-imageio-core</artifactId>
        <version>1.3.1</version>
    </dependency>

为了添加对以下格式的支持:

  • wbmp
  • bmp
  • pcx
  • pnm
  • raw
  • tiff
  • gif(写入)

您可以使用以下命令检查支持的格式列表:

     for(String format : ImageIO.getReaderFormatNames())
        System.out.println(format);

请注意,您只需要将jar文件(例如jai-imageio-core-1.3.1.jar)放入类路径中即可使其正常工作。
其他添加额外图像类型支持的项目包括:

需要提供图像尺寸并不是很明智 - 还有其他可用的JAI-ImageIO实现,但其中一些需要(依赖于平台的)二进制文件,并且大多数都不在Maven中央库中(如果这对我来说是个问题的话)。 - Remigius Stalder
1
感谢指出。我需要将JP2格式转换。实际上,我需要这个依赖项: com.github.jai-imageio jai-imageio-jpeg2000 1.3.0 - Azim

0

直接使用ImageIO.read的方法在某些情况下是不正确的。在我的情况下,原始的byte[]数组中没有包含有关图像宽度、高度和格式的任何信息。仅使用ImageIO.read是无法构造有效图像的。

有必要将图像的基本信息传递给BufferedImage对象:

    BufferedImage outBufImg = new BufferedImage(width, height, bufferedImage.TYPE_3BYTE_BGR);

然后通过使用setRGB或setData为BufferedImage对象设置数据。(当使用setRGB时,似乎我们必须先将byte[]转换为int[]。因此,如果源图像数据很大,可能会导致性能问题。也许对于大的byte[]类型源数据来说,使用setData是一个更好的选择。)


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