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.
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]
I'm using J2SE v1.4.2.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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( ).createIm age(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 ;)
Toolkit.getDefaultToolkit(
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?
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.
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.
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? :)
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></COD E> 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.getLoc alGraphics Environmen t();
try {
transparency = Transparency.OPAQUE;
if (hasAlpha) {
transparency = Transparency.BITMASK;
}
gd = ge.getDefaultScreenDevice( );
gc = gd.getDefaultConfiguration ();
bImage = gc.createCompatibleImage(i mage.getWi dth(null),
image.getHeight(null), transparency);
} catch (HeadlessException e) {
}
if (bImage == null) {
type = BufferedImage.TYPE_INT_RGB ;
if (hasAlpha) {
type = BufferedImage.TYPE_INT_ARG B;
}
bImage = new BufferedImage(image.getWid th(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().has Alpha();
} else {
pixelGrabber = new PixelGrabber(image, 0, 0, 1, 1, false);
try {
pixelGrabber.grabPixels();
} catch (InterruptedException e) {
}
colorModel = pixelGrabber.getColorModel ();
return colorModel.hasAlpha();
}
}
}
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></COD
* 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
*/
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.getLoc
try {
transparency = Transparency.OPAQUE;
if (hasAlpha) {
transparency = Transparency.BITMASK;
}
gd = ge.getDefaultScreenDevice(
gc = gd.getDefaultConfiguration
bImage = gc.createCompatibleImage(i
image.getHeight(null), transparency);
} catch (HeadlessException e) {
}
if (bImage == null) {
type = BufferedImage.TYPE_INT_RGB
if (hasAlpha) {
type = BufferedImage.TYPE_INT_ARG
}
bImage = new BufferedImage(image.getWid
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().has
} 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? :)
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.
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.
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.
=-)
ASKER
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...