Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert short[] into a grayscale image

I am writing a Buddhabrot fractal generator using aparapi. I got the OpenCL part of it to work, resulting in a single-dimension array that represents each pixel. I have the dimensions of the final image as final ints, and have written code to get the index of arbitrary points in that array. I want to save this as an image and I'm trying to use BufferedImage with TYPE_USHORT_GRAY. Here's what I have so far:

    BufferedImage image=new BufferedImage(VERTICAL_PIXELS, HORIZONTAL_PIXELS, BufferedImage.TYPE_USHORT_GRAY);
    for(int i=0; i<VERTICAL_PIXELS; i++)
        for(int k=0; k<HORIZONTAL_PIXELS; k++)
            image.setRGB(k, i, normalized[getArrayIndex(k,i,HORIZONTAL_PIXELS)]);

The problem is, I don't know what to set the RGB as. What do I need to do?

like image 663
Shawn Walton Avatar asked Jan 06 '12 21:01

Shawn Walton


1 Answers

The problem here is that setRGB() wants an 0xRRGGBB color value. BufferedImage likes to pretend that the image is RGB, no matter what the data is stored as. You can actually get at the internal DataBufferShort (with getTile(0, 0).getDataBuffer()), but it can be tricky to figure out how it is laid out.

If you already have your pixels in a short[], a simpler solution might be to copy them into an int[] instead an jam it into a MemoryImageSource:

int[] buffer = /* pixels */;

ColorModel model = new ComponentColorModel(
   ColorSpace.getInstance(ColorSpace.CS_GRAY), new int[] { 16 }, 
   false, true, Transparency.OPAQUE, DataBuffer.TYPE_USHORT);

Image image = Toolkit.getDefaultToolkit().createImage(
   new MemoryImageSource(VERTICAL_PIXELS, HORIZONTAL_PIXELS, 
                         model, buffer, 0, VERTICAL_PIXELS));

The advantage of this approach is that you control the underlying pixel array. You could make changes to that array and call newPixels() on your MemoryImageSource, and it would update live. It also gives you complete power to define your own palette other than grayscale:

int[] cmap = new int[65536];
for(int i = 0; i < 65536; ++i) {

    cmap[i] = (((i % 10000) * 256 / 10000) << 16) 
            | (((i % 20000) * 256 / 20000) << 8)
            | (((i % 40000) * 256 / 40000) << 0);
}
ColorModel model = new IndexColorModel(16, 65536, cmap, 0, false, -1, DataBuffer.TYPE_USHORT);

This approach works fine if you just want to display the image on the screen:

JFrame frame = new JFrame();
frame.getContentPane().add(new JLabel(new ImageIcon(image)));
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);

However, if you wanted to write it out to a file and preserve the one-short-per-pixel format (say, to load into Matlab) then you're out of luck. The best you can do is to paint it into a BufferedImage and save that with ImageIO, which will save as RGB.

If you definitely need a BufferedImage at the end, another approach is to apply the color palette yourself, calculate the RGB values, and then copy them into the image:

short[] data = /* your data */;
int[] cmap = /* as above */;
int[] rgb = new int[data.length];

for(int i = i; i < rgb.length; ++i) {
   rgb[i] = cmap[data[i]];
}

BufferedImage image = new BufferedImage(
   VERTICAL_PIXELS, HORIZONTAL_PIXELS, 
   BufferedImage.TYPE_INT_RGB);

image.setRGB(0, 0, VERTICAL_PIXELS, HORIZONTAL_PIXELS,
   pixels, 0, VERTICAL_PIXELS);
like image 175
Russell Zahniser Avatar answered Sep 28 '22 08:09

Russell Zahniser