Link to home
Start Free TrialLog in
Avatar of sendres
sendres

asked on

How to convert a boolean[][] array to a GIF image file (and vice versa)?

I have an application where I need to do the following:

I have a two-dimensional array containing boolean values. I would like to convert these data into an image such that 'true' array elements become 'white' pixels and 'false' array elements become 'black' pixels. Then I want to save the image to a file in GIF or PNG format.

Conversely, I want to import a GIF or PNG image into a boolean[][] array, such that white pixels become true elements and non-white pixels become false elements.

My array of boolean values uses standard [row][column] ordering of the subscripts, i.e. myArray[0][0] is the top left corner of the image and myArray[height-1][width-1] is the bottom right corner.

I'm using J2SE v1.4.2.
SOLUTION
Avatar of Tommy Braas
Tommy Braas
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
Avatar of sendres
sendres

ASKER

Okay, I've got the export working. In the end I didn't nned to get a Graphics for the BufferedImage, I just manually set the pixels using setRGB. Here's my code:

    public static void export (boolean[][] data) {
        int height = data.length;
        int width = data[0].length;
        BufferedImage outImage = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                if (data[i][j]) {
                    outImage.setRGB (j, i, 0x00000000);
                } else {
                    outImage.setRGB (j, i, 0xffffffff);
                }
            }
        }
        try {
            // Save as PNG
            File file = new File("outimage.png");
            ImageIO.write(outImage, "png", file);
        } catch (IOException e) {
        }

I'm going to try the reverse of this process to import the data. I think the only trick will be to deal with the different types of color models and rasters in the input files.

Hi Ho, Hi Ho,
It's off to code I go...
ASKER CERTIFIED SOLUTION
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
Good luck!
Already gave the answer orangehead911, I only left out the Image I/O since I'm sure sendres can deal with it :)
By the way, the code to convert the Image back to the boolean array should've been able to use:

            Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(image.getSource(), bca));

instead of having to use the PixelGrabber:
            try {
                  if (pg.grabPixels(10000)) {
                        pixels = (int[])pg.getPixels();
                        for (int y = 0; y < height; y++) {
                              for (int x = 0; x < width; x++) {
                                    bca.filterRGB(x, y, pixels[x + y * width]);
                              }
                        }
                  }
            } catch (InterruptedException ex) {
            }

but for some obscure reason, it doesn't work that way!!! :O

Java - or AWT - works in mysterious ways ;)
>>  Already gave the answer orangehead911,
What do you mean?
The way this thread reads on my computer it shows my answer (complete code of BooleanColourArray class) before your comment "Good luck", so I said that I already gave the answer, meaning I've already put the code on display, I figured you probably posted at the same time as I did :)
You post is 10 minutes after mine.

My 'Good luck' was in response to "I'm going to try the reverse of this process to import the data. I think the only trick will be to deal with the different types of color models and rasters in the input files." from sendres.
Actually, I was right, my post (listing of my BooleanColourArray class) and your post of "Good luck" were exactly at the same time, 09:55AM, sendres posted his code 10 minutes earlier.
Anyway, what do you think of my code though? ;)
>>  You post is 10 minutes after mine.
Was referring to the initial post I made.
Ok, now I'm confused, wasn't your initial post made at 05:32AM GMT-12:00?
Pardon me, misread the time...*blushing*. It's been a hard week... ;-)
LOL, that's ok, what do you think of my code btw? :)
Avatar of sendres

ASKER

Okay, here's what I came up with. It's more of a brute force approach than Doron's code. I basically reverse-engineered my export method, and used a few ideas from the Java almanac to get the file into a BufferedImage. Then I use a bitmask to ignore the alpha channel.

Let me know if you see room for improvement.

import java.awt.image.*;
import java.awt.*;
import java.io.*;
import javax.imageio.ImageIO;
import javax.swing.*;

/** Converts boolean[][] array to GIF or PNG file and vice versa.
 */
public abstract class ImageImportExport {
    private static final int BLACK = 0xFF000000;
    private static final int WHITE = 0xFFFFFFFF;
    private static final int MASK  = 0xFF000000;
   
    private static boolean
            hasAlpha;     // flag whether image supports alpha channels
       
    private static boolean[][]
            data;         // image data
   
    private static BufferedImage
            inImage,      // input Image file converted to BufferedImage
            bImage,       // intermediary to convert Image to BufferedImage
            outImage;     // output data represented as BufferedImage
       
    private static ColorModel
            colorModel;   // color model of the image

    private static File
            file;         // handle used for file I/O
   
    private static Graphics
            g;            // Graphics used to convert Image to BufferedImage
   
    private static GraphicsConfiguration
            gc;           // cuurent graphics configuration
       
    private static GraphicsDevice
            gd;           // current graphics device
       
    private static GraphicsEnvironment
            ge;           // current graphics environment
       
    private static Image
            image;        // input image read from file
   
    private static int
            height,       // image height in pixels
            i,            // loop counter
            j,            // loop counter
            pixelColor,   // color of a pixel read from input image
            transparency, // Transparency BITMASK or OPAQUE
            type,         // BufferedImage TYPE_INT_RGB or TYPE_INT_ARGB
            width;        // image width in pixels
   
    private static PixelGrabber
            pixelGrabber; // pixel grabber to sample the image
   
    /** Empty constructor--this class is <CODE><B>abstract</B></CODE> and may
     * not be instantiated.
     */    
    ImageImportExport() {}
   
    /** Method to export a boolean[][] array as an image file.
     *  'true' vales are exported as 'black' pixels; 'false' values are
     *  exported as 'white pixels.
     * @param data array containing image data. <CODE>data[0][0]</CODE> is the top left corner of
     * the image; <CODE>data[height-1][width-1]</CODE> is the bottom right corner.
     */    
    public static void imageExport(boolean[][] data) {
        height = data.length;
        width = data[0].length;
        outImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
       
        for (i = 0; i < height; i++) {
            for (j = 0; j < width; j++) {
                if (data[i][j]) {
                    outImage.setRGB (j, i, BLACK);
                } else {
                    outImage.setRGB (j, i, WHITE);
                }
            }
        }
       
        try {
            file = new File("outimage.png");
            ImageIO.write(outImage, "png", file);
        } catch (IOException e) {
        }
    }
   
    /** Method to import an image file as a boolean[][] array.
     *  'black' pixels are imported as 'true' values; 'white' values are
     *  imported as 'false' values.
     * @param imageName the name of the <CODE>File</CODE> to be opened.
     * @return the image file as a <CODE>bolean[][</CODE>] array.
     */    
    public static boolean[][] imageImport(String imageName) {
        image = null;
        try {
            file = new File(imageName);
            image = ImageIO.read(file);
        } catch (IOException e) {
        }
        inImage = toBufferedImage(image);
        height =  inImage.getHeight();
        width = inImage.getWidth();
        data = new boolean[height][width];

        for (j = 0; j < width; j++) {
            for (i = 0; i < height; i++) {
                pixelColor = inImage.getRGB(j, i) | MASK;
                if (pixelColor == WHITE) {
                    data[i][j] = false;
                } else {
                    data[i][j] = true;
                }
            }
        }
        return data;
    }

    // method to convert an Image to a BufferedImage
    private static BufferedImage toBufferedImage(Image image) {
        if (image instanceof BufferedImage) {
            return (BufferedImage)image;
       
        } else {
            image = new ImageIcon(image).getImage();
            hasAlpha = hasAlpha(image);
            bImage = null;
            ge = GraphicsEnvironment.getLocalGraphicsEnvironment();

            try {
                transparency = Transparency.OPAQUE;
                if (hasAlpha) {
                    transparency = Transparency.BITMASK;
                }
                gd = ge.getDefaultScreenDevice();
                gc = gd.getDefaultConfiguration();
                bImage = gc.createCompatibleImage(image.getWidth(null),
                        image.getHeight(null), transparency);
            } catch (HeadlessException e) {
            }
   
            if (bImage == null) {
                type = BufferedImage.TYPE_INT_RGB;
                if (hasAlpha) {
                    type = BufferedImage.TYPE_INT_ARGB;
                }
                bImage = new BufferedImage(image.getWidth(null),
                        image.getHeight(null), type);
            }
           
            g = bImage.createGraphics();
            g.drawImage(image, 0, 0, null);
            g.dispose();
            return bImage;
        }
    }
   
    // Method to determine if the Image supports alpha channels
    private static boolean hasAlpha(Image image) {
        if (image instanceof BufferedImage) {
            bImage = (BufferedImage)image;
            return bImage.getColorModel().hasAlpha();
        } else {
            pixelGrabber = new PixelGrabber(image, 0, 0, 1, 1, false);

            try {
                pixelGrabber.grabPixels();
            } catch (InterruptedException e) {
            }
   
            colorModel = pixelGrabber.getColorModel();
            return colorModel.hasAlpha();
        }
    }
}
Well, what are we supposed to do now then? :)
Avatar of sendres

ASKER

As I said, let me know if you see any room for improvement! Other than that, and divying up some points, I think we're about done!
Thanks for the point and I'm still going to try and find out why importing the Image back into a boolean array didn't work with the Toolkit.createImage method which led me to use a PixelGrabber.  Basically, I always try to use code already present in the JDK (usually written by Sun of course) that accomplishes tasks that could help my goals.
Avatar of sendres

ASKER

Okay, let me know if you do. I'm all in favor of reusing code, particularly from the JDK whenever possible. This particular piece of code is a small portion of a project I'm doing as a school assignment. As such it's important that I be able to explain every detail of how it works. In this instance, I had already written the export method and was already cracking on the import when I received your solution.
Will do :)

In my writing this code I simply took advantage of the RGBImageFilter which Java has to do for me the Pixel processing, like going through all the pixels in the picture and basically leaving me the job of either returning the new pixel colour (black/white according to the true/false in the array) or setting the array data according to the pixel in the source Image.