BufferedImage from 4-bit data buffer - java

Hello I have a problem with converting my 4-bit data buffer to WritableRaster.
Image resolution: 1024x768 (786432)
Here is description what I'm doing.
1) Create 4-bit BufferedImage
bit4Image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY, MY_BIT_4_COLOR_MODEL);
Graphics graphics = bit4Image.getGraphics();
graphics.drawImage(originalImage, 0, 0, null);
graphics.dispose();
//4-bit BufferedImage created. 4-bit BufferedImage is properly made cause it can be saved to hdd and looks good
2) Get byte array from DataBuffer from 4-bit
byte[] pixelData = ((DataBufferByte) bit4Image.getRaster().getDataBuffer()).getData();
// pixelData length is 393216
3) Now I want to create BufferedImage from this byte array pixelData
BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY, MY_BIT_4_COLOR_MODEL);
DataBufferByte buffer = new DataBufferByte(pixelData, pixelData.length);
WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, width, 1, new int[]{0}, new Point(0, 0));
dest.setData(raster);
Problem is when I call Raster.createInterleavedRaster. Exception:
java.awt.image.RasterFormatException: Data array too small (should be > 786431 )
I also tried something like this
BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY, MY_BIT_4_COLOR_MODEL);
dest.getRaster().setDataElements(0, 0, width, height, pixelData);
But this one gives me similar failure:
java.lang.ArrayIndexOutOfBoundsException: 393216
Could someone give me a hint or show the proper way of setting this 4-bit pixelData to WritableRaster?

Resolved.
I just had to create WritableRaster with giving it SampleModel:
SampleModel sm = MY_BIT_4_COLOR_MODEL.createCompatibleSampleModel(width, height);
WritableRaster wr = Raster.createWritableRaster(sm, buffer, new Point(0,0));

The initial code you had almost worked, the only problem is you tried to create an "interleaved" raster. For palette images (IndexColorModel) you typically have only one sample (the palette index) per pixel, so there's no samples to interleave.
Instead, your pixel data is 4 bit/pixel, stored as two pixels per byte. Storing more samples per storage unit, is often referred to as "packed". This means you need to create a "packed" raster, using one of the Raster.createPackedRaster methods.
Here's a full, runnable sample:
public static void main(String[] args) {
int width = 100;
int height = 100;
// Create initial 4 bit image
IndexColorModel icm = new IndexColorModel(4, 16, new int[16], 0, false, -1, DataBuffer.TYPE_BYTE);
BufferedImage bit4Image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY, icm);
// ...you probably do something in between here
// Get the pixel data
byte[] pixelData = ((DataBufferByte) bit4Image.getRaster().getDataBuffer()).getData();
// ...you probably do something in between here
// Create a data buffer around the array, wrap in raster
DataBufferByte buffer = new DataBufferByte(pixelData, pixelData.length);
WritableRaster raster = Raster.createPackedRaster(buffer, width, height, 4, null);
// Finally create a copy of the image, sharing pixel data
BufferedImage copy = new BufferedImage(icm, raster, icm.isAlphaPremultiplied(), null);
System.out.println("copy: " + copy);
}

Related

How to obtain a resized BufferedImage

Say I have 2 TIF images and I read one of them into a BufferedImage instance:
ImageReader reader = ImageIO.getImageReadersByFormatName("tif").next();
reader.setInput(inputStream, false); // inputStream is the first image.
BufferedImage bufferedImage = reader.read(0);
Now I want to create a new BufferedImage without reading the other image. It should be the same as the previous one, but only different in size. imageType seems to be 0 for TIF images, but the following doesn't work.
BufferedImage largeBufferedImage = new BufferedImage(newWidth, newHeight, 0);
Is there any way to clone the existing BufferedImage and only change its size?
BTW I want to be able to do it for any image format. I don't want to deal with details like imageType if possible.
BufferedImage deepCopy(BufferedImage bi)/*method to clone BufferedImage*/ {
ColorModel cm = bi.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = bi.copyData(null);
return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}
BufferedImage newImg = deepCopy(oldImg);//clone it
Graphics2D g = newImg.createGraphics();
g.drawImage(newImg, 0, 0, width, height, null);//newImg will be resized
When you draw in your paint method, you can add more parameters to stretch and scale image, see g.drawImage at this link.
After some trial and error, I found a working solution for my problem.
private BufferedImage copyAndResize(BufferedImage source, int width, int height)
{
ColorModel cm = source.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = source.copyData(null);
SampleModel sm = raster.getSampleModel().createCompatibleSampleModel(width, height);
WritableRaster newRaster = WritableRaster.createWritableRaster(sm, null);
BufferedImage newBi = new BufferedImage(cm, newRaster, isAlphaPremultiplied, null);
return newBi;
}

JAVA : How to create .PNG image from a byte[]?

I have seen some code source, but I do not understand...
I use Java 7
Please, how to convert a RGB (Red,Green,Blue) Byte Array (or something similar) to a .PNG file format ?
Example from an array that could represent "a RGB pixel" :
byte[] aByteArray={0xa,0x2,0xf};
Important Aspect :
I try to generate a .PNG file only from a byte[] "not from a previous existing file"
is it possible with an existing API? ;)
Here my first code :
byte[] aByteArray={0xa,0x2,0xf};
ByteArrayInputStream bais = new ByteArrayInputStream(aByteArray);
File outputfile = new File("image.png");
ImageIO.write(bais, "png", outputfile);
....Error : No suitable Method Found
Here the other version modified from Jeremy but look similar :
byte[] aByteArray={0xa,0x2,0xf};
ByteArrayInputStream bais = new ByteArrayInputStream(aByteArray);
final BufferedImage bufferedImage = ImageIO.read(newByteArrayInputStream(aByteArray));
ImageIO.write(bufferedImage, "png", new File("image.png"));
....multiple Errors : image == null! ...... Sure ? Note : I do not search to use a source file
The Image I/O API deals with images, so you need to make an image from your byte array first before you write it out.
byte[] aByteArray = {0xa,0x2,0xf,(byte)0xff,(byte)0xff,(byte)0xff};
int width = 1;
int height = 2;
DataBuffer buffer = new DataBufferByte(aByteArray, aByteArray.length);
//3 bytes per pixel: red, green, blue
WritableRaster raster = Raster.createInterleavedRaster(buffer, width, height, 3 * width, 3, new int[] {0, 1, 2}, (Point)null);
ColorModel cm = new ComponentColorModel(ColorModel.getRGBdefault().getColorSpace(), false, true, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
BufferedImage image = new BufferedImage(cm, raster, true, null);
ImageIO.write(image, "png", new File("image.png"));
This assumes the byte array has three bytes per pixel (red, green then blue) and the range of values is 0-255.

Creating 8 bit image from byte array

The byte array is obtained this way -
BufferedImage image = new Robot().createScreenCapture(new Rectangle(screenDimension));
byte[] array = ((DataBufferByte)getGraycaleImage(image).getRaster().getDataBuffer()).getData();
// Method getGraycaleImage returns a grayscaled BufferedImage, it works fine
now how do i reconstruct this grayscale image from the byte array?
I don't know much about ARGB, RGB or grayscale images. I tried this -
private Image getGrayscaleImageFromArray(byte[] pixels, int width, int height)
{
int[] pixels2=getIntArrayFromByteArray(pixels);
MemoryImageSource mis = new MemoryImageSource(width, height, pixels2, 0, width);
Toolkit tk = Toolkit.getDefaultToolkit();
return tk.createImage(mis);
}
private int[] getIntArrayFromByteArray(byte[] pixelsByte)
{
int[] pixelsInt=new int[pixelsByte.length];
int i;
for(i=0;i<pixelsByte.length;i++)
pixelsInt[i]=pixelsByte[i]<<24 | pixelsByte[i]<<16
| pixelsByte[i]<<8 | pixelsByte[i]; // I think this line creates the problem
return pixelsInt;
}
When I draw this image it's not black and white, rather something like orange and gray.
You have to specify the correct ColorSpace corresponding to a grayscale image.
Here's an example, as found on http://technojeeves.com/joomla/index.php/free/89-create-grayscale-image-on-the-fly-in-java:
public static BufferedImage getGrayscale(int width, byte[] buffer) {
int height = buffer.length / width;
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
int[] nBits = { 8 };
ColorModel cm = new ComponentColorModel(cs, nBits, false, true,
Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
SampleModel sm = cm.createCompatibleSampleModel(width, height);
DataBufferByte db = new DataBufferByte(buffer, width * height);
WritableRaster raster = Raster.createWritableRaster(sm, db, null);
BufferedImage result = new BufferedImage(cm, raster, false, null);
return result;
}
I hope it'll help you if I explain to you how to convert from ARGB/RGB 2 gray
cause there are too many unknown functions and classes :P
ARGB is 32 bit/pixel so 8 bit for every channel. the alpha channel is the opacity so the opposite of transparency so 0 is transparent.
RGB is 24 bit/pixel. to convert from ARGB to RGB you have to dismiss the alpha channel.
to convert from RGB to grayscale u have to use this formula:
0.2989 * R + 0.5870 * G + 0.1140 * B
so you have to figure out which byte belongs to which channel ;)
This will work. Just make sure you tweak the image type the way you need:
Image img = new ImageIcon(array).getImage();
BufferedImage image = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB);
image.createGraphics().drawImage(img, 0, 0, null);

Problem converting bytes from a Bing Map request into a BufferedImage

I have very little experience with Java IO and images, and I've been unsuccessful at converting an aerial image saved as a byte array into a BufferedImage.
Here's my code:
int width = scaledImage.getWidth();
int height = scaledImage.getHeight();
DataBuffer buffer = new DataBufferByte(scaledImage.getImage(), scaledImage.getImage().length, 0);
SampleModel sampleModel = new SinglePixelPackedSampleModel(DataBuffer.TYPE_BYTE, width, height, new int[]{(byte)0xf});
WritableRaster raster = Raster.createWritableRaster(sampleModel, buffer, null);
ColorModel colorModel = imageManager.generateColorModel();
BufferedImage image = new BufferedImage(colorModel, raster, false, null);
Most of this code is borrowed from http://www.exampledepot.com/egs/java.awt.image/Mandelbrot2.html.
This code throws the following exception
java.awt.image.RasterFormatException: Data array too small (should be 122499 )
the actual length of the data array is 52341.
The dimensions are 350px X 350px
Here is the line that is killing you:
DataBuffer buffer = new DataBufferByte(scaledImage.getImage(), scaledImage.getImage().length, 0);
The example you show does width * height instead of scaledImage.getImage().length. In the model you've chosen, you need a byte per pixel, which is 350x350 or 122500.

Flipping vertically a raw image in Java

I need to flip in Java a raw image that has her rows inverted. By inverted I mean, the first row of the image is stored at the end of a file.
I managed to achive what I want by reordering the image rows using an auxiliar buffer. I included my code below.
I think this can be optimized by translating the coordinates, avoiding the memory copy. I tried to implement a DataBuffer that would invert the rows, but the raster I'm using requires a DataBufferByte (a final class).
Does anyone knows a more optimized way of doing what I want?
Thank you
...
int w = 640;
int h = 480;
byte[] flippedData = new byte[640*480*4];
int scanLineLength = w*4;
for(int i=0;i!=h; ++i) {
System.arraycopy(originalData, scanLineLength*i, flippedData, scanLineLength*(h-i)-scanLineLength, scanLineLength);
}
DataBuffer db = new DataBufferByte(flippedData,flippedData.length);
WritableRaster raster = Raster.createInterleavedRaster(db, w, h, scanLineLength, 4, new int[]{2,1,0}, new Point(0,0));
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorModel cm = new ComponentColorModel(cs, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
BufferedImage img = new BufferedImage(cm, raster, false, null);
ImageIO.write(img, "JPEG", new File("out.jpg"));
Use java.awt.AffineTransform:
Affine transformations can be constructed using sequences of translations, scales, flips, rotations, and shears.
See this and this to see how is flipping implemented using AffineTransform

Categories