4

I'm trying to write 16 bit grayscale imagedata to a png using BufferedImage.TYPE_USHORT_GRAY. Normally I write to an image like so:

BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);

then:

image.setRGB(x,y,Color.getRGB);

to set the pixels, and finally:

ImageIO.write(image, "png", new File(path + ".png"));

to write to a png image.

But now I have this as image:

BufferedImage imageGray = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY);

How do I go about saving pixels to that format? Using setRGB() with a 16 bit integer doesn't seem to work, when I open the saved png file I see a lot of banding happening.

I tried saving a simple gradient from 0 to 65535 and then using the setRGB() on the grayscale image, and checked the results in Photoshop. I can see the image consists of smaller gradients every 256 rows. I'm guessing either setRGB() or imageIO doesn't work as I would like it to.

Are there workarounds for this? Does imageIO even support the BufferedImage.TYPE_USHORT_GRAY format? Or can it only save 8 bit data? And if it can save 16bit data, how would I go about saving pixel data, preferably in a way like setRGB() works (so for a certain x,y coordinate)?

Erik Stens
  • 1,779
  • 6
  • 25
  • 40

4 Answers4

2

As pst already commented below my question:

Try using the Raster directly?

Accessing the Raster directly solved the problem :

BufferedImage bi = BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY)
for (int x = 0; x < width; x++) {
    for (int y = 0; y < height; y++) {
        Short s = shortData[x][y]
        bi.getRaster().setDataElements(x, y, Short[] {s})
    }
}
avianey
  • 5,545
  • 3
  • 37
  • 60
Erik Stens
  • 1,779
  • 6
  • 25
  • 40
0

From BufferedImage you can read

public static final int TYPE_USHORT_GRAY

Represents an unsigned short grayscale image, non-indexed). This image has a ComponentColorModel with a CS_GRAY ColorSpace.

So try instantiating your own ColorSpace with the CS_GRAY type (ColorSpace.getInstance(ColorSpace.CS_GRAY) should do it I suppose). This object has a method called fromRGB which you should be able to use...

aioobe
  • 413,195
  • 112
  • 811
  • 826
  • 1
    Doesn't this just convert sRGB data to grayscale data? I already have grayscale data, I just need to save it pixel by pixel to a BufferedImage with TYPE_USHORT_GRAY so I can save the 16bit data. At the moment I like the idea of setRGB() since you can specify the coordinate you want to save to, but there is no saveGray() equivalent. I can just set 16bit integers using setRGB(), but it turns out the color model doesn't interpret this correctly. Is there any way of converting a 16bit integer to a value that can be used with setRGB which works with the CS_GRAY colorspace? – Erik Stens Nov 07 '10 at 09:12
0

You probably need to widen the signed 16bit shorts to ints and remove the sign:

int ushort = (int)(shortData[x][y]) & 0xFFFF;
deorst
  • 761
  • 3
  • 13
  • I already have unsigned 16 bit ints, I'm just using the BufferedImage.TYPE_USHORT_GRAY as a convenient way to save stuff to a 16 bit grayscale png – Erik Stens Nov 07 '10 at 10:50
0
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY);
short[] dataArray = ((DataBufferUShort)image.getRaster().getDataBuffer()).getData();
dataArray[y*width+x] = color;
ImageIO.write(image, "png", new File(path + ".png"));
comonad
  • 5,134
  • 2
  • 33
  • 31