Solved

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

Posted on 2004-04-15
22
657 Views
Last Modified: 2007-12-19
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
Comment
Question by:sendres
  • 11
  • 7
  • 4
22 Comments
 
LVL 14

Assisted Solution

by:Tommy Braas
Tommy Braas earned 85 total points
ID: 10835231
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
 

Author Comment

by:sendres
ID: 10837396
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
 
LVL 9

Accepted Solution

by:
doronb earned 85 total points
ID: 10837479
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
 
LVL 14

Expert Comment

by:Tommy Braas
ID: 10837481
Good luck!
0
 
LVL 9

Expert Comment

by:doronb
ID: 10837488
Already gave the answer orangehead911, I only left out the Image I/O since I'm sure sendres can deal with it :)
0
 
LVL 9

Expert Comment

by:doronb
ID: 10837525
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
 
LVL 14

Expert Comment

by:Tommy Braas
ID: 10837560
>>  Already gave the answer orangehead911,
What do you mean?
0
 
LVL 9

Expert Comment

by:doronb
ID: 10837704
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
 
LVL 14

Expert Comment

by:Tommy Braas
ID: 10837721
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
 
LVL 9

Expert Comment

by:doronb
ID: 10837754
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
 
LVL 9

Expert Comment

by:doronb
ID: 10837756
Anyway, what do you think of my code though? ;)
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 14

Expert Comment

by:Tommy Braas
ID: 10837829
>>  You post is 10 minutes after mine.
Was referring to the initial post I made.
0
 
LVL 9

Expert Comment

by:doronb
ID: 10838928
Ok, now I'm confused, wasn't your initial post made at 05:32AM GMT-12:00?
0
 
LVL 14

Expert Comment

by:Tommy Braas
ID: 10839230
Pardon me, misread the time...*blushing*. It's been a hard week... ;-)
0
 
LVL 9

Expert Comment

by:doronb
ID: 10840036
LOL, that's ok, what do you think of my code btw? :)
0
 

Author Comment

by:sendres
ID: 10849607
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
 
LVL 9

Expert Comment

by:doronb
ID: 10849717
Well, what are we supposed to do now then? :)
0
 

Author Comment

by:sendres
ID: 10849785
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
 
LVL 9

Expert Comment

by:doronb
ID: 10851157
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
 

Author Comment

by:sendres
ID: 10853595
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
 
LVL 9

Expert Comment

by:doronb
ID: 10855314
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
 
LVL 14

Expert Comment

by:Tommy Braas
ID: 10855392
=-)
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Are you developing a Java application and want to create Excel Spreadsheets? You have come to the right place, this article will describe how you can create Excel Spreadsheets from a Java Application. For the purposes of this article, I will be u…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
This video teaches viewers about errors in exception handling.
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now