I'm looking to send over an Object that has a BufferedImage through a socket. BufferedImage is not serializable, so it needs to be converted to a serializable data type, and then back again. I have looked a lot online, and byte[] seems to be the go to send just a BuffereImage, but I'm trying to send an entire object so I'm leaning more towards int[]. I thought I ran across an answer that explained this on here a few weeks ago, but after 2.5 hours of searching I could not find it. I have tried the Java Oracle, but quickly got lost.
If there is a better way please excuse my ignorance, as I have not really worked a lot with sockets and BufferedImage manipulation.
Basically a BufferedImage is an array. The pixels are stored into the DataBuffer, which is an array.
BufferedImage source = //...
switch ( source.getType() )
{
case BufferedImage.TYPE_BYTE_GRAY :
case BufferedImage.TYPE_3BYTE_BGR :
case BufferedImage.TYPE_4BYTE_ABGR :
final byte[] bb = ((DataBufferByte)source.getRaster().getDataBuffer()).getData() ;
//...
break ;
case BufferedImage.TYPE_USHORT_GRAY :
final short[] sb = ((DataBufferUShort)source.getRaster().getDataBuffer()).getData() ;
//...
break ;
case BufferedImage.TYPE_INT_RGB :
case BufferedImage.TYPE_INT_BGR :
case BufferedImage.TYPE_INT_ARGB :
final int[] ib = ((DataBufferInt)source.getRaster().getDataBuffer()).getData() ;
break ;
// etc.
}
You will also need to send the image dimensions, plus the number of channels.
Send the image across as a PNG:
// BufferedImage -> byte sequence
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "PNG", baos);
byte[] imageData = baos.toByteArray();
// byte sequence -> BufferedImage
ByteArrayInputStream bais = new ByteArrayInputStream(imageData);
BufferedImage img = ImageIO.read(bais);
Related
I have a Spring MVC Web Service that returns an image as a byte array. The output is in json format. The format is png
Here's the code snippet for the image.
BufferedImage img = ImageIO.read(new File(caminho.replace("/", "//")));
imagem = ((DataBufferByte) img.getRaster().getDataBuffer
()).getData();
When I run the server, this is the output:
[{"id":0,"caminhoMDPI":null,"caminhoHDPI":"C:/Users/Marcos/Pictures/postos/drawable-
mdpi/esso_logo.png","caminhoXHDPI":null,"caminhoXXHDPI":null,"imagem":"AAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAA....]
Eventually, other symbols appear on the "imagem" field. I suppose this is right, but I'm not
sure.
On my Android app, I have a routine to download the image and store it as a blob in the database. I receive the json format and transform it into a class with jackson. I've logged the "imagem" field and it looks the same.
My problem is that I can't transform it into an image. Here's the code snippet:
byte[] img_bandeira = cursor.getBlob(cursor.getColumnIndex("img_bandeira"));
Bitmap bmpBandeira = BitmapFactory.decodeByteArray(img_bandeira, 0, img_bandeira.length);
ImageView ivBandeira = (ImageView) view.findViewById(R.id.ivBandeira);
ivBandeira.setImageBitmap(bmpBandeira);
All I get is a message: skImageDecoder::Factory returned null.
I've looked at other similar posts, tried to change some lines, but nothing happened. I don't know what's going on here. Thanks in advance.
I solved it. In the server, the image should be sent like this:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedImage img = ImageIO.read(new File(caminho.replace("/", "//")));
ImageIO.write(img, "png", baos);
baos.flush();
String base64String = Base64.encode(baos.toByteArray());
baos.close();
In my app, it should be read like this:
public static Bitmap getImage(byte[] imageArray) {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(imageArray);
Bitmap bitmap = BitmapFactory.decodeStream(byteArrayInputStream);
return bitmap;
}
Thank you, everybody ;)
I am not new to bitmaps nor new to java. I am trying to convert High resolution bitmaps to byte array in a loop. Please find code here:
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, stream);
imageByteArray = stream.toByteArray();
When i am using the above approach I cam able to convert 5 images in 1 second. But I need it to be even faster. I tried ByteBuffer approach also like this:
Bitmap bmp = intent.getExtras().get("data");
int size = bmp.getRowBytes() * bmp.getHeight();
ByteBuffer b = ByteBuffer.allocate(size);
bmp.copyPixelsToBuffer(b);
byte[] bytes = new byte[size];
try {
b.get(bytes, 0, bytes.length);
} catch (BufferUnderflowException e) {
// always happens
}
But this is very slow (Slower then previous) :(
Please, can somebody give a faster method? Guide Me...
The first solution is the right one.
But two things can happen here:
The image is maybe not of JPEG type, so conversion occurs, which takes time
The image is compressed 50%, which takes time
That aside, if it's taking some time, I doubt it could go faster (being the right solution).
You must consider the fact that the speed of processing is tightly tied to the speed of the device you are testing on( since this is tagged android I'm presuming you're using a mobile device ).
You should take a look at android developer on how to handle large bitmaps effectively Android developers . Since processing 5 high resolution images per second is slow to you I can presume you are having some kind of gallery or previews? If that's the case you shouldn't handle the high resolution images and should indeed take a look at the link above.
Also as a side-note your second code can be optimised this way:
int bytes = bmp.getByteCount();
ByteBuffer buffer = ByteBuffer.allocate(bytes);
bmp.copyPixelsToBuffer(buffer);
byte[] array = buffer.array();
Otherwise the most efficient way of copying bytes that I know is copy() taken from Commons-IO:
public static int copy(InputStream input, OutputStream output) throws IOException {
int n, count = 0;
byte[] buffer = new byte[4 * 1024];
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
you can try as follows
Bitmap bitmap = intent.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap .compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArray = stream.toByteArray();
hope it may work good for you!!
check for line bitmap.compress(Bitmap.CompressFormat.JPEG, 50, stream); it may cause problem..as you are using JPEG format with rate 50.
I'm trying to send a BufferedImage over socket, I do this by converting the image to byte[] and then send it over after encoding it in Base64. I'm sending over 2 BufferedImages, one of them is "full", the other one is about 50% transparent. The problem I'm having, is that when they arrive, the second image is still visually transparent, but when I get the data array via Raster, it has been changed.
I made a small test code to demonstrate the problem;
BufferedImage levelBufferedOriginal = ...
BufferedImage backgroundBufferedOriginal = ...
byte[] levelDataOriginal = ((DataBufferByte) levelBufferedOriginal.getRaster().getDataBuffer()).getData();
byte[] backgroundDataOriginal = ((DataBufferByte) backgroundBufferedOriginal.getRaster().getDataBuffer()).getData();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] temp = null, temp2=null;
try {
ImageIO.write(levelBufferedOriginal, "png", baos);
baos.flush();
temp = baos.toByteArray();
baos.close();
baos=new ByteArrayOutputStream();
ImageIO.write(backgroundBufferedOriginal, "png", baos);
baos.flush();
temp2 = baos.toByteArray();
baos.close();
} catch (IOException e1) {
e1.printStackTrace();
}
BufferedImage levelBufferedNew = null;
BufferedImage backgroundBufferedNew = null;
try {
levelBufferedNew = ImageIO.read(new ByteArrayInputStream(temp));
backgroundBufferedNew = ImageIO.read(new ByteArrayInputStream(temp2));
} catch (IOException e) {
e.printStackTrace();
}
byte[] levelDataNew = ((DataBufferByte) levelBufferedNew.getRaster().getDataBuffer()).getData();
byte[] backgroundDataNew = ((DataBufferByte) backgroundBufferedNew.getRaster().getDataBuffer()).getData();
System.out.println("LEVEL: " + Arrays.equals(levelDataOriginal, levelDataNew));
System.out.println("BACKGROUND: " + Arrays.equals(backgroundDataOriginal, backgroundDataNew));
All I do here, is simply transform the BufferedImage to byte[], then back, and compare the data I get from DataBufferByte. The output is
LEVEL: false
BACKGROUND: true
Background is the "full" image, and Level is the one with some transparent pixels.
If the general idea is wrong, I would like to hear another, all I want is to be able to exactly recreate 2 bufferedImages.
edit: What we have established so far:
The images (both before and after) are TYPE_BYTE_INDEXED (13) with IndexColorModel (color map)
The before image has a transparent color in the color map, at index 255 (which is the value -1 in the byte array, as Java uses signed bytes). The after image has a different value at this index, that is not transparent.
The images are serialized/deserialized in PNG format, using ImageIO
The images are visually equal, but the raw pixel data (the byte array) differs
Which leads to the conclusion that the ImageIO PNGImageWriter re-arranges the entries in the color map when writing, resulting in different pixel data/color map.
This basically leaves us with two options:
Serialize the image data in a different way, to assure the color map/pixel data is not modified. It is possible to send the pixel data array, along with the color map array and the height/width of the image, and then re-create the image exactly at the client. This is quite a bit of code, and is probably covered by other questions on SO.
Don't rely on the pixel data/color maps being the same. Use the value of ((IndexColorModel) levelBufferedNew.getColorModel()).getTransparentPixel() to test for/set transparency instead of the hardcoded value -1. This requires pretty much no other change in your code.
Note: These solutions will only work for TYPE_BYTE_INDEXED (13) images.
For a more generic (but possibly slower) approach, use the code in the original answer to set transparent parts, and use (levelBufferedNew.getRGB(x, y) >> 24) == 0 to test for transparency. This should work even for TYPE_INT_ARGB or TYPE_4BYTE_ABGR.
original answer:
Instead of fiddling with the image at byte array level, why not try using normal Java2D? ;-)
Something like:
Graphics2D g = levelBufferedNew.createGraphics();
try {
g.setComposite(AlphaComposite.Clear);
g.fillOval(x, y, w, h); // The area you want to make transparent
}
finally {
g.dispose();
}
...should work.
PS: As the images use IndexColorModel, you can use the getTransparentPixel() to get the transparent pixel index, instead of relying on it being at a certain index (-1/255). Then you can still manipulate at byte array level. ;-)
I see that a number of people have had a similar problem, however I'm yet to try find exactly what I'm looking for.
So, I have a method which reads an input image and converts it to a byte array:
File imgPath = new File(ImageName);
BufferedImage bufferedImage = ImageIO.read(imgPath);
WritableRaster raster = bufferedImage .getRaster();
DataBufferByte data = (DataBufferByte) raster.getDataBuffer();
What I now want to do is convert it back into a BufferedImage (I have an application for which I need this functionality). Note that "test" is the byte array.
BufferedImage img = ImageIO.read(new ByteArrayInputStream(test));
File outputfile = new File("src/image.jpg");
ImageIO.write(img,"jpg",outputfile);
However, this returns the following exception:
Exception in thread "main" java.lang.IllegalArgumentException: im == null!
This is because the BufferedImage img is null. I think this has something to do with the fact that in my original conversion from BufferedImage to byte array, information is changed/lost so that the data can no longer be recognised as a jpg.
Does anyone have any suggestions on how to solve this? Would be greatly appreciated.
This is recommended to convert to a byte array
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "jpg", baos);
byte[] bytes = baos.toByteArray();
Note that calling close or flush will do nothing, you can see this for yourself by looking at their source/doc:
Closing a ByteArrayOutputStream has no effect.
The flush method of OutputStream does nothing.
Thus use something like this:
ByteArrayOutputStream baos = new ByteArrayOutputStream(THINK_ABOUT_SIZE_HINT);
boolean foundWriter = ImageIO.write(bufferedImage, "jpg", baos);
assert foundWriter; // Not sure about this... with jpg it may work but other formats ?
byte[] bytes = baos.toByteArray();
Here are a few links concerning the size hint:
Java: Memory efficient ByteArrayOutputStream
jpg bits per pixel
Of course always read the source code and docs of the version you are using, do not rely blindly on SO answers.
I am trying to convert byte[] array to buffered image so than i can resize the image..but problem is conversion always turned into null.here is my code..
ByteArrayInputStream bais = new ByteArrayInputStream(user.getUser_image());
//Here user.getUser_image() returns byte[] returned from server..
try {
BufferedImage image = ImageIO.read(bais);
System.out.println("============><================"+image);//Here it prints null
BufferedImage scaledImage = Scalr.resize(image,48);
}
.....and so on
It means that the ImageIO class is not able to select an appropriate ImageReader. The purpose of this could be corrupted byte array or unsupported image type. Try to debug it.