Link to home
Start Free TrialLog in
Avatar of adamretter
adamretter

asked on

Convert Java Image Object to Byte Array

I need to take a java Image object (java.awt.Image) and convert it to a byte array. I need to preserve all of the image information not just the pixels!

Basically I have a database, and I have read the image from the database into a byte array, I have then constructed an Image object from this byte array - this all works fine, I can render the image, manipulate it etc.
But now I need to go the other way and convert the Image object back to a byte array - this doesnt appear to be so easy!

The solution MUST work for both Sun JDK 1.4+ and 1.5+ and MUST NOT rely on any 3rd party libraries unless they are absolutely tiny and open source, although 3rd party libraries are not the prefered method!

The code I had originally found to do this (below) throws an exception at g,drawimage(...), and to be honset I dont understand enough about Java to fix it myself (ths solution does not have to include this code)-

private byte[] convertImageToByteArray(Image img, String MIMEType)
{
            try
              {
                  Iterator iter = ImageIO.getImageWritersByMIMEType(MIMEType);
                  ImageWriter writer = iter.hasNext() ? (ImageWriter) iter.next() : null;
                  ByteArrayOutputStream baos = new ByteArrayOutputStream();
                  ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
                  writer.setOutput(ios);
                  BufferedImage rendImage = null;
                  if(img instanceof BufferedImage)
                  {
                      rendImage = (BufferedImage) img;
                  }
                  else
                  {
                      MediaTracker tracker = new MediaTracker(this);
                      tracker.addImage(img, 0);
                      tracker.waitForAll();
                      rendImage = new BufferedImage(img.getWidth(null), img.getHeight(null), 1);
                      Graphics g = rendImage.createGraphics();
                      g.drawImage(img, 0, 0, null);
                  }
                  writer.write(new IIOImage(rendImage, null, null));
                  writer.dispose();
                  return baos.toByteArray();
              }
            catch(Exception e)
            {
                  e.printStackTrace();//temp
                  return null;
            }
}

Avatar of CEHJ
CEHJ
Flag of United Kingdom of Great Britain and Northern Ireland image

Please post the exception stack trace
> I need to preserve all of the image information not just the pixels!

what other information specifically do u need?
try:

                     MediaTracker tracker = new MediaTracker(this);
                     tracker.addImage(img, 0);
                     tracker.waitForAll();
                     rendImage = new BufferedImage(img.getWidth(null), img.getHeight(null), 1);
                     Graphics g = rendImage.createGraphics();
                     g.drawImage(img, 0, 0, null);
                     tracker.addImage(rendImage, 1);
                     tracker.waitForAll();
You needn't worry about that. It *will* be preserved (otherwise it wouldn't be possible to reconstitute the image from a file)
And close baos.
Avatar of adamretter
adamretter

ASKER

FAO CEHJ, here is the exception -

java.lang.ClassCastException: [I
      at java.awt.image.ColorModel.getAlpha(ColorModel.java:814)
      at java.awt.image.ColorModel.getRGB(ColorModel.java:859)
      at sun.awt.image.ImageRepresentation.convertToRGB(ImageRepresentation.java:271)
      at sun.awt.image.ImageRepresentation.setPixels(ImageRepresentation.java:485)
      at java.awt.image.AreaAveragingScaleFilter.accumPixels(AreaAveragingScaleFilter.java:196)
      at java.awt.image.AreaAveragingScaleFilter.setPixels(AreaAveragingScaleFilter.java:235)
      at sun.awt.image.OffScreenImageSource.sendPixels(OffScreenImageSource.java:84)
      at sun.awt.image.OffScreenImageSource.produce(OffScreenImageSource.java:169)
      at sun.awt.image.OffScreenImageSource.addConsumer(OffScreenImageSource.java:48)
      at sun.awt.image.OffScreenImageSource.startProduction(OffScreenImageSource.java:62)
      at java.awt.image.FilteredImageSource.startProduction(FilteredImageSource.java:166)
      at sun.awt.image.ImageRepresentation.startProduction(ImageRepresentation.java:647)
      at sun.awt.image.ImageRepresentation.drawToBufImage(ImageRepresentation.java:722)
      at sun.java2d.pipe.DrawImage.copyImage(DrawImage.java:883)
      at sun.java2d.pipe.ValidatePipe.copyImage(ValidatePipe.java:168)
      at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:2847)
      at sun.java2d.SunGraphics2D.drawImage(SunGraphics2D.java:2832)
      at org.exist.xquery.modules.image.ScaleFunction$ImageConversion.convertImageToByteArray(ScaleFunction.java:266)
FAO objects,

The code that you propose wont make any difference, because as I mentioned the exception is thrown at g.drawImage() which happends before your suggested changes...
>>FAO CEHJ, here is the exception -

Is that *all* of it?
It goes lower but there is no point posting that, as it is just the code in existing modules of a collaborative open source project that are known to work. All the code I have written for this is in org.exist.xquery.modules.image.ScaleFunction, everything below can be safely ignored I believe.
Are there any exceptions lower down?
Check the color model of your original image, and set the color model of your BufferedImage to match it.
for example if it's rgb with transparency (which it looks like) use:

 rendImage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_ARGB);
FAO CEHJ - no there are no exceptions further down
FAO objects -

There is no BufferedImage.TYPE_ARGB, so I have tried -

TYPE_INT_ARGB
TYPE_INT_ARGB_PRE
TYPE_4BYTE_ABGR
TYPE_4BYTE_ABGR_PRE

They still seem to cause the same exception as before.

This function has to work on any image type, be it PNG, JPEG, GIF or BMP etc...
So hardcoding something like that probably wont work for me.

Whats next?
Problem you're going to have is that you cannot determine things like the ColorModel from Image class.
So preserviing all the details of the original image will be difficult. In fact how you are converting the image you're going to lose details of the original image aren't you.

to get the original images color model try:

PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
pg.grabPixels();
ColorModel cm = pg.getColorModel();

you can then use that when creating the new image
or use it to determine whether the image uhas transparency

boolean transparent = cm.hasAlpha();
FAO Objects -

I think you are going to hold my hand a bit more,

The constructor of BufferedImage will take a ColorModel but it also wants an ImageType, where do I get this imageType from?

Rather than these piecemeal suggestions, What I would really like is a full tested solution here for converting an java.awt.Image to a byte array without loosing any of the image information. Basically I am trying to store a java.awt.Image in a db.

Do I need to increase the points for this? I can offer double the points (1000) for the correct fully working solution...
What exactly is it you don't want to lose, other than the array of pixel values?
Why not just use PixelGrabber to grab the image pixel values as an array.

http://java.sun.com/j2se/1.5.0/docs/api/java/awt/image/PixelGrabber.html

Providing complete solutions is beyond the scope of this site sorry.
I dont want to loose any data about the image.

I need to be able to store the image in a db, so later I can get it out the db and reconstruct it from a byte array. I have sucessfully written and tested the code for getting an image from a byte array stored in the db, I can retreive the image from the db into an java.awt.Image object and view the Image, no problem.

Now I need the other end of the process, to take a java.awt.Image and convert it in its entirety to a byte array so i can then store if back to the db.

"Providing complete solutions is beyond the scope of this site sorry" - Why? This site is about asking questions and gettings answers,  I think I have had solutions given for previous questions I have asked on this site in the past. All you need do is post the java code in one of these comment boxes that will do the job.

Im offering 1000 points.
Then it sounds like PixelGrabber will meet your needs

int[] pixels = new int[w * h];
PixelGrabber pg = new PixelGrabber(image, 0, 0, w, h, pixels, 0, w);
pg.grabPixels();

pixels will now contain the value of all pixels in the image.
What about the metadata of the image, I know that at least JPEG's can contain metadata?
> What about the metadata of the image, I know that at least JPEG's can contain metadata?

You've already lost that when u converted to Image class.
If you don't want to lose it then you'll need to store the original image.
Okay so I have the original image in its entirely stored in the db, I can retreive this from the db as a byte array.

Now the bigger picture is this, I am retreiving this image so that i can Scale it, which is why I was using the java.awt.Image Object, I then want to store the scaled Image back to the db, which is why I was asking how to convert an Image to a byte array. I did not realise that converting from the byte array to a java.awt.Image meant that I would loose additional image information, and if this is the case, how come its still possible to call Image.getProperty("comment"...) ?

Whats my best bet then?
ASKER CERTIFIED SOLUTION
Avatar of Mick Barry
Mick Barry
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I diagree, this issue has not yet been solved. The answers have been very piecemeal and not really what I asked for. I would like this question to remain open.