Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 704
  • Last Modified:

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.
0
sendres
Asked:
sendres
  • 11
  • 7
  • 4
2 Solutions
 
Tommy BraasCommented:
Steps to solve your problem:
Create a BufferedImage the size of the dimensions of your array
Get a Graphics from your BufferedImage
Iterate through your loops and paint the pixels onto the Graphics object
Use ImageIO to save the image as a GIF or a PNG

See the following for more information:
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/image/BufferedImage.html
http://java.sun.com/j2se/1.4.2/docs/api/java/awt/Graphics2D.html
http://java.sun.com/j2se/1.4.2/docs/api/javax/imageio/ImageIO.html
0
 
sendresAuthor Commented:
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...
0
 
doronbCommented:
import java.awt.*;
import java.awt.image.*;

public class BooleanColourArray extends RGBImageFilter {
      private final static int CONVERT_IMAGE = 100;
      private final static int CONVERT_ARRAY = CONVERT_IMAGE + 1;
      private final static int black = Color.black.getRGB();
      private final static int white = Color.white.getRGB();
      private int mode;
      private boolean[][] booleanArray;

      private BooleanColourArray() {
            super();
            // Next line is important, don't leave it out!
            canFilterIndexColorModel = true;
      }

      public static Image convertBooleanArray(boolean[][] booleanArray) {
            Object[] y = booleanArray;
            boolean[] x = booleanArray[0];
            BooleanColourArray bca = new BooleanColourArray();
            bca.booleanArray = booleanArray;
            bca.mode = CONVERT_ARRAY;
            BufferedImage result = new BufferedImage(x.length, y.length, BufferedImage.TYPE_INT_RGB);
            Graphics2D g2d = result.createGraphics();
            g2d.setBackground(Color.black);
            g2d.clearRect(0, 0, x.length, y.length);
            Image image = Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(result.getSource(), bca));
            g2d.drawImage(image, 0, 0, null);
            return result;
      }

      public static boolean[][] convertBooleanImage(Image image) {
            int width = image.getWidth(null);
            int height = image.getHeight(null);
            int[] pixels = new int[width * height];
            PixelGrabber pg = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width);
            BooleanColourArray bca = new BooleanColourArray();
            bca.booleanArray = new boolean[height][width];
            bca.mode = CONVERT_IMAGE;
            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) {
            }
            return bca.booleanArray;
      }

      public int filterRGB(int x, int y, int rgb) {
            // Assume we should return the current pixel value..
            int result = rgb;
            if (mode == CONVERT_IMAGE) {
                  booleanArray[y][x] = (rgb == white);
            } else if (mode == CONVERT_ARRAY) {
                  result = booleanArray[y][x] ? white : black;
            }
            // Always return a colour pixel..
            return result;
      }
}

Use the BooleanColourArray class as follows:

            boolean[][] data = {
                  {false, false, false}, // 0
                  {false, false, true},  // 1
                  {false, true, false},  // 2
                  {false, true, true},   // 3
                  {true, false, false},  // 4
                  {true, false, true},   // 5
                  {true, true, false},   // 6
                  {true, true, true}     // 7
            };
            Image xImage = BooleanColourArray.convertBooleanArray(data);

And to get a boolean array out of an Image:

            boolean[][] testData = BooleanColourArray.convertBooleanImage(xImage);

This class performs complete two-way conversion :)

Hope this helps,
Doron
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
Tommy BraasCommented:
Good luck!
0
 
doronbCommented:
Already gave the answer orangehead911, I only left out the Image I/O since I'm sure sendres can deal with it :)
0
 
doronbCommented:
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 ;)
0
 
Tommy BraasCommented:
>>  Already gave the answer orangehead911,
What do you mean?
0
 
doronbCommented:
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 :)
0
 
Tommy BraasCommented:
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.
0
 
doronbCommented:
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.
0
 
doronbCommented:
Anyway, what do you think of my code though? ;)
0
 
Tommy BraasCommented:
>>  You post is 10 minutes after mine.
Was referring to the initial post I made.
0
 
doronbCommented:
Ok, now I'm confused, wasn't your initial post made at 05:32AM GMT-12:00?
0
 
Tommy BraasCommented:
Pardon me, misread the time...*blushing*. It's been a hard week... ;-)
0
 
doronbCommented:
LOL, that's ok, what do you think of my code btw? :)
0
 
sendresAuthor Commented:
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();
        }
    }
}
0
 
doronbCommented:
Well, what are we supposed to do now then? :)
0
 
sendresAuthor Commented:
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!
0
 
doronbCommented:
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.
0
 
sendresAuthor Commented:
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.
0
 
doronbCommented:
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.
0
 
Tommy BraasCommented:
=-)
0

Featured Post

Hire Technology Freelancers with Gigs

Work with freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely, and get projects done right.

  • 11
  • 7
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now