36

I'm posting this thread because I have some difficulties to deal with pictures in Java. I would like to be able to convert a picture into a byte[] array, and then to be able to do the reverse operation, so I can change the RGB of each pixel, then make a new picture. I want to use this solution because setRGB() and getRGB() of BufferedImage may be too slow for huge pictures (correct me if I'm wrong).

I read some posts here to obtain a byte[] array (such as here) so that each pixel is represented by 3 or 4 cells of the array containing the red, the green and the blue values (with the additional alpha value, when there are 4 cells), which is quite useful and easy to use for me. Here's the code I use to obtain this array (stored in a PixelArray class I've created) :

public PixelArray(BufferedImage image)
{
    width = image.getWidth();
    height = image.getHeight();
    DataBuffer toArray = image.getRaster().getDataBuffer();
    array = ((DataBufferByte) toArray).getData();
    hasAlphaChannel = image.getAlphaRaster() != null;
}

My big trouble is that I haven't found any efficient method to convert this byte[] array to a new image, if I wanted to transform the picture (for example, remove the blue/green values and only keeping the red one). I tried those solutions :

1) Making a DataBuffer object, then make a SampleModel, to finally create a WritableRaster and then BufferedImage (with additional ColorModel and Hashtable objects). It didn't work because I apparently don't have all the information I need (I have no idea what's the Hashtable for BufferedImage() constructor).

2) Using a ByteArrayInputStream. This didn't work because the byte[] array expected with ByteArrayInputStream has nothing to do with mine : it represents each byte of the file, and not each component of each pixel (with 3-4 bytes for each pixel)...

Could someone help me?

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
Jef Grailet
  • 547
  • 1
  • 5
  • 7

5 Answers5

47

Try this:

private BufferedImage createImageFromBytes(byte[] imageData) {
    ByteArrayInputStream bais = new ByteArrayInputStream(imageData);
    try {
        return ImageIO.read(bais);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
TeWu
  • 5,928
  • 2
  • 22
  • 36
heejong
  • 754
  • 7
  • 7
  • Is that really the most efficient way? I just create a new `BufferedImage`, obtain the `byte[]` the same way as in the first post, and use `System.ArrayCopy`. It does tend to get out of sync when you both try to write to the byte[] and to the Graphics object of the image. – Mark Jeronimus Jun 24 '14 at 07:09
  • 24
    In a quick test, your method returns `null` because my `ImageIO` doesn't have an `ImageInputStreamSPI` understanding the raw stream. – Mark Jeronimus Jun 24 '14 at 07:40
16

I have tried the approaches mentioned here but for some reason neither of them worked. Using ByteArrayInputStream and ImageIO.read(...) returns null, whereas byte[] array = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); returns a copy of the image data, not a direct reference to them (see also here).

However, the following worked for me. Let's suppose that the dimensions and the type of the image data are known. Let also byte[] srcbuf be the buffer of the data to be converted into BufferedImage. Then,

  1. Create a blank image, for example

    img=new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
    
  2. Convert the data array to Raster and use setData to fill the image, i.e.

    img.setData(Raster.createRaster(img.getSampleModel(), new DataBufferByte(srcbuf, srcbuf.length), new Point() ) );
    
Community
  • 1
  • 1
fyts
  • 493
  • 5
  • 12
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);

This method does tend to get out of sync when you try to use the Graphics object of the resulting image. If you need to draw on top of your image, construct a second image (which can be persistant, i.e. not constructed every time but re-used) and drawImage the first one onto it.

Johann
  • 27,536
  • 39
  • 165
  • 279
Mark Jeronimus
  • 9,278
  • 3
  • 37
  • 50
  • Similarly, if the image is of type TYPE_INT_ARGB, you access the array as `int[] array = ((DataBufferInt) image.getRaster().getDataBuffer()).getData()`. Each integer represents one pixel in the image: Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are blue. – Kay Nov 24 '16 at 13:01
6

Several people upvoted the comment that the accepted answer is wrong.

If the accepted answer isn't working, it may be because Image.IO doesn't have support for the type of image you're trying, for example tiff images.

To make it work, you need to add an extra jar to handle the image type.

You can add jai-imageio-core-1.3.1.jar to your classpath with:

    <!-- 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>

To add support for:

  • wbmp
  • bmp
  • pcx
  • pnm
  • raw
  • tiff
  • gif (write)

You can check the list of supported formats with:

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

Note that you only have to drop the jar (jai-imageio-core-1.3.1.jar for example) into your classpath to make it work.

Other projects that add additional support for image types include:

Jeffrey Knight
  • 5,888
  • 7
  • 39
  • 49
  • having to supply image dimensions is not very sensible - there are other jai-imageio implementations that work, too, but some of them need (platform dependent) binaries and most are not in maven central (if that's an issue like for me). – Remigius Stalder Nov 09 '17 at 17:42
  • 1
    Thanks for pointing out. I needed JP2 format to be converted. I actually needed this dependency: ` com.github.jai-imageio jai-imageio-jpeg2000 1.3.0 ` – Azim Feb 26 '18 at 17:39
0

The approach by using ImageIO.read directly is not right in some cases. In my case, the raw byte[] doesn't contain any information about the width and height and format of the image. By only using ImageIO.read, It is impossible for the program to construct a valid image.

It is necessary to pass the basic information of the image to BufferedImage object:

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

Then set the data for the BufferedImage object by using setRGB or setData. (When using setRGB, it seems we must convert byte[] to int[] first. As a result, it may cause performance issues if the source image data is big. Maybe setData is a better idea for big byte[] typed source data.)

Wagner
  • 323
  • 2
  • 8