Problem converting bytes from a Bing Map request into a BufferedImage - java

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.

Related

BufferedImage from 4-bit data buffer

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);
}

getting a bufferedImage from a bytearray

I am trying to get back a bufferedImage from array of bytes, but I am getting an error saying bufferedimage is null. I actually tried several ways, everything ended up in the same way. Here goes my code:
1)
byte[] arr = Base64.decode(base64String);
BufferedImage bImageFromConvert =ImageIO.read(new ByteArrayInputStream(arr));
2)
InputStream in = new ByteArrayInputStream(arr);
BufferedImage bImageFromConvert = ImageIO.read(in);
I am pretty sure my byte array contains data and I think ImageIO.read() is where my code goes wrong.
The error is in your BufferedImage to Base64 encode method as you have posted in the comments.
You are never writing the BufferedImage to the ByteArrayOutputStream. Therefore the Base64 string is empty, and reading the empty string produces a null BufferedImage.
You should use this code to encode your image:
BufferedImage originalImage = ImageIO.read(new File("G:\\a.jpg"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write( originalImage, "jpg", baos );
String base64String=Base64.encode(baos.toByteArray());
To decode the image use this code:
byte[] arr = Base64.decode(base64String);
BufferedImage bImageFromConvert =ImageIO.read(new ByteArrayInputStream(arr));
System.out.println(bImageFromConvert.getWidth());
Try this code.Maybe it works. It worked for me.
byte[] aByteArray = {};
int width = ;
int height = ;
DataBuffer buffer = new DataBufferByte(aByteArray, aByteArray.length);
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);
Just add the byte, width and height to code and customize it.

FFMPEG and JNI - pass AVFrame data to Java and Back

I have some C code that decodes a video frame by frame. I get to a point where i have an AVFrame in BGR32 and would like to send it back to Java for editing.
I have a ByteBuffer object in my C code that was created in Java using AllocateDirect but i struggle to write the content of the AVFrame->data[0] (of uint8_t type) to it and read it back. I have tried memcpy with no luck. Does anyone have an idea how to achieve this?
UPDATE
Followed Will's comment below and wrote this in C
char *buf = (*pEnv)->GetDirectBufferAddress(pEnv, byteBuffer);
memcpy(buf, rgb_frame->data[0], output_width*output_height*4);
The buffer does contain some data in Java but doing the following returns a null bitmap
BufferedImage frame = ImageIO.read(bitmapStream);
Where bitmapStream is a ByteBufferInputStream defined here:
https://code.google.com/p/kryo/source/browse/trunk/src/com/esotericsoftware/kryo/io/ByteBufferInputStream.java?r=205
Not sure if I am not writing things correctly in this buffer
UPDATE 2
Got pretty close now thanks to the latest snippet. I am using BGR32 format in my C code ie 4 bytes per pixel. So I modified things a bit in Java:
final byte[] dataArray = new byte[width*height*4];
imageData.get(dataArray);
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
final DataBuffer buffer = new DataBufferByte(dataArray, dataArray.length);
Raster raster = Raster.createRaster(sampleModel, buffer, null);
image.setData(raster);
I get the image correctly but there seems to be an issue with color channels
Tried different formats with no luck
From Oracle's JNI Functions Documentation at
https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetDirectBufferAddress
GetDirectBufferAddress
void* GetDirectBufferAddress(JNIEnv* env, jobject buf);
Fetches and returns the starting address of the memory region
referenced by the given direct java.nio.Buffer.
This function allows native code to access the same memory region that
is accessible to Java code via the buffer object. LINKAGE:
Index 230 in the JNIEnv interface function table. PARAMETERS:
env: the JNIEnv interface pointer
buf: a direct java.nio.Buffer object (must not be NULL) RETURNS:
Returns the starting address of the memory region referenced by the
buffer. Returns NULL if the memory region is undefined, if the given
object is not a direct java.nio.Buffer, or if JNI access to direct
buffers is not supported by this virtual machine. SINCE:
JDK/JRE 1.4
I tested with this C++ code:
static char framebuf[100];
JNIEXPORT void JNICALL Java_javaapplication45_UseByteBuffer_readBuf
(JNIEnv *env, jobject usebb, jobject bb) {
void *addr = env->GetDirectBufferAddress(bb);
framebuf[0] = 77;
memcpy(addr,framebuf,100);
}
and this Java Code:
public class UseByteBuffer {
public native void readBuf(ByteBuffer bb);
}
...
public static void main(String[] args) {
System.load("/home/shackle/NetBeansProjects/usebb/dist/Debug/GNU-Linux-x86/libusebb.so");
ByteBuffer bb = ByteBuffer.allocateDirect(100);
new UseByteBuffer().readBuf(bb);
byte first_byte = bb.get(0);
System.out.println("first_byte = " + first_byte);
}
And it printed the first_byte=77 indicating it got the data copied correctly.
Update
ImageIO.read() will not accept just any set of bytes it has to be in a format that one of the installed ImageReader's can recognize such as JPEG or PNG.
Here is an example getting raw for (3 byte r,g,b )bytes into an image
int width = 256;
int height = 256;
ByteBuffer bb = ByteBuffer.allocateDirect(height*width*3);
byte[] raw = new byte[width * height * 3];
bb.get(raw);
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
DataBuffer buffer = new DataBufferByte(raw, raw.length);
SampleModel sampleModel = new ComponentSampleModel(DataBuffer.TYPE_BYTE, width, height, 3, width * 3, new int[]{0,1,2});
Raster raster = Raster.createRaster(sampleModel, buffer, null);
image.setData(raster);
Update 2
For BGR32 I believe this would be closer:
ByteBuffer imageData = ByteBuffer.allocateDirect(height * width * 4);
byte[] raw = new byte[width * height * 4];
imageData.get(raw);
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
DataBuffer buffer = new DataBufferByte(raw, raw.length);
SampleModel sampleModel = new ComponentSampleModel(
DataBuffer.TYPE_BYTE, width, height, 4, width * 4,
new int[]{2,1,0} // Try {1,2,3}, {3,2,1}, {0,1,2}
);
Raster raster = Raster.createRaster(sampleModel, buffer, null);
image.setData(raster);
Notice where I have commented, where I suspect you may need to experiment with the array of bandOffsets in the third argument of the ComponentSampleModel constructor to fix the color model.
Update 3
One can reuse the sampleModel to get data out of the image by using BufferedImage.copyData() to a WritableRaster instead of using getRaster().
SampleModel sampleModel = new ComponentSampleModel(
DataBuffer.TYPE_BYTE, width, height, 4, width * 4,
new int[]{2, 1, 0}
);
...
BufferedImage newImage = ImageIO.read(new File("test.png"));
byte newRaw[] = new byte[height*width*4];
DataBuffer newBuffer = new DataBufferByte(newRaw, newRaw.length);
WritableRaster newRaster = Raster.createWritableRaster(sampleModel, newBuffer, null);
newImage.copyData(newRaster);

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);

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