RuntimeException: Buffer not large enough for pixels - java

I'm receiving a Bitmap in byte array through socket and I read it and then I want to set it os.toByteArray as ImageView in my application. The code I use is:
try {
//bmp = BitmapFactory.decodeByteArray(result, 0, result.length);
bitmap_tmp = Bitmap.createBitmap(540, 719, Bitmap.Config.ARGB_8888);
ByteBuffer buffer = ByteBuffer.wrap(os.toByteArray());
bitmap_tmp.copyPixelsFromBuffer(buffer);
Log.d("Server",result+"Length:"+result.length);
runOnUiThread(new Runnable() {
#Override
public void run() {
imageView.setImageBitmap(bitmap_tmp);
}
});
return bmp;
} finally {
}
When I run my application and start receiving Byte[] and expect that ImageView is changed, it's not.
LogCat says:
java.lang.RuntimeException: Buffer not large enough for pixels at
android.graphics.Bitmap.copyPixelsFromBuffer
I searched in similar questions but couldn't find a solution to my problem.

Take a look at the source (version 2.3.4_r1, last time Bitmap was updated on Grepcode prior to 4.4) for Bitmap::copyPixelsFromBuffer()
The wording of the error is a bit unclear, but the code clarifies-- it means that your buffer is calculated as not having enough data to fill the pixels of your bitmap.
This is (possibly) because they use the buffer's remaining() method to figure the capacity of the buffer, which takes into account the current value of its position attribute. If you call rewind() on your buffer before you invoke copyFromPixels(), you might see the runtime exception disappear. I say 'might' because the ByteBuffer::wrap() method should set the position attribute value to zero, removing the need to call rewind, but judging by similar questions and my own experience resetting the position explicitly may do the trick.
Try
ByteBuffer buffer = ByteBuffer.wrap(os.toByteArray());
buffer.rewind();
bitmap_tmp.copyPixelsFromBuffer(buffer);

The buffer size should be exactly 1553040B (assuming bitmap's height, width and 32bit to encode each color).

Related

Capture a picture in Android, and work with it using the ImageAnalyzer

I am trying to implement a solution that would be able to capture a picture from the phone's camera, then operate the picture (do something with it) and repeat this process N times, quickly.
I have achieved this using the imageCapture.takePicture method, but when trying to implement the same process for N pictures, the onCaptureSuccess method is being called every ~500ms (on a solid Samsung device). The process of capturing and saving the picture lasts too long for me. I need it to be quicker than 500ms.
I was looking to implement it using the imageAnalyzer class, and used code similar to this:
private class CameraAnalyzer implements ImageAnalysis.Analyzer {
#Override
public void analyze(#NonNull ImageProxy image) {
ByteBuffer bb = image.getPlanes()[0].getBuffer();
byte[] buf = new byte[bb.remaining()];
bb.get(buf);
//raw - data container
raw = buf;
//runnable - operate the picture
runnable.run();
image.close();
}
}
But I am receiving NULL for buf and the picture is always empty. bb.rewind() did not help as well.
After being advised that the picture is coming in RAW format, and thus need to convert it to a Bitmap, I have done it with this code:
ImageProxy.PlaneProxy[] planes = image.getPlanes();
ByteBuffer buffer = planes[0].getBuffer();
int pixelStride = planes[0].getPixelStride();
int rowStride = planes[0].getRowStride();
int rowPadding = rowStride - pixelStride * image.getWidth();
Bitmap bitmap = Bitmap.createBitmap(image.getWidth()+rowPadding/pixelStride,
image.getHeight(), Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
But while executing the copyPixelsFromBuffer I am encountering this issue:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.strujomeri, PID: 24466
java.lang.RuntimeException: Buffer not large enough for pixels
How can I get the picture I want in imageAnalyzer, and also have it's content
in byte[] format to do with it what I want ?
The default image format is YUV_420_888 and as your buffer is only one plane (the Luminance(Y) plane ) the buffer is of pixels that are only a single byte in size.
copyPixelsFromBuffer assumes that the buffer is in the same colour space as the bitmap, so as you set the bitmap format to ARGB_8888 then it is looking for 4 bytes per pixel not 1 byte per pixel you are giving it.
I've not used this but this page has a ImageProxy.toBitmap example to convert a YUV to Bitmap via a Jpeg (if you just wanted a Jpeg you could skip the last step)
I've also seen another method not via Jpeg but to directly change the colorspace of YUV to a bitmap.
You can of course change to the only other colour space ImageAnalysis supports of ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888 which takes longer to be captured because it needs converting and is still not the right format for a Bitmap

I don't understand a code to load picture in java

I am working with OpenCV in java, but I don't understand part of a class that loads pictures in java:
public class ImageProcessor {
public BufferedImage toBufferedImage(Mat matrix){
int type = BufferedImage.TYPE_BYTE_GRAY;
if ( matrix.channels() > 1 ) {
type = BufferedImage.TYPE_3BYTE_BGR;
}
int bufferSize = matrix.channels()*matrix.cols()*matrix.rows();
byte [] buffer = new byte[bufferSize];
matrix.get(0,0,buffer); // get all the pixels
BufferedImage image = new BufferedImage(matrix.cols(),matrix.rows(),type);
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);
return image;
}
Main class sends a Mat object to this class.
The result sends BufferedImage but I don't understand targetPixels because this class doesn't use it somewhere else. But whenever I comment targetPixels and System.arraycopy, result shows black picture.
I want to know what's targetPixels - and what does it do?
The point is less about that array, but the methods that get you there.
You start here: getRaster(). That is supposed to return a WritableRaster ... and so on.
That class is using from getDataBuffer() from the Raster class; and there we find:
A class representing a rectangular array of pixels. A Raster encapsulates a DataBuffer that stores the sample values and a SampleModel that describes how to locate a given sample value in a DataBuffer.
What happens in essence here: that Image object, in the end has an array of bytes that are supposed to contain certain information.
That assignment:
final byte[] targetPixels = ...
retrieves a reference to that internal data; and then arrayCopy() is used to copy data into that array.
For the record: that doesn't look like a good approach - as it only works when this copy action really affects the internals of that Image object. But what if that last call getData() would create a copy of the internal data?
In other words: this code tries to gain direct access to internal data of some object; and then manipulate that internal data.
Even if that works today, it is not robust; and might break easily in the future. The other thing: note that this code does a unconditional cast (DataBufferByte). That code throws a RuntimeException if the the buffer doesn't have exactly that type.
Probably that is "all fine"; since it is all related to "AWT" classes which probably exist for ages; and will not change at all any more. But as said; this code has various potential issues.
targetPixels is the main image data (i.e. the pixels) of your new image. The actual image is created when the pixeldata is copied from buffer into targetPixels.
targetPixels is the array of bytes from your newly created BufferedImage, those bytes are empty thus you need to copy the bytes from the buffer to it with System.arraycopy.. :)

Possible memory leak when caching BufferedImage

We have an application which serve images, to speed up the response time, we cache the BufferedImage directly in memory.
class Provider {
#Override
public IData render(String... layers,String coordinate) {
int rwidth = 256 , rheight = 256 ;
ArrayList<BufferedImage> result = new ArrayList<BufferedImage>();
for (String layer : layers) {
String lkey = layer + "-" + coordinate;
BufferedImage imageData = cacher.get(lkey);
if (imageData == null) {
try {
imageData = generateImage(layer, coordinate,rwidth, rheight, bbox);
cacher.put(lkey, imageData);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
if (imageData != null) {
result.add(imageData);
}
}
return new Data(rheight, rheight, width, result);
}
private BufferedImage generateImage(String layer, String coordinate,int rwidth, int rheight) throws IOException {
BufferedImage image = new BufferedImage(rwidth, rheight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.setColor(Color.RED);
g.drawString(layer+"-"+coordinate, new Random().nextInt(rwidth), new Random().nextInt(rheight));
g.dispose();
return image;
}
}
class Data implements IData {
public Data(int imageWidth, int imageHeight, int originalWidth, ArrayList<BufferedImage> images) {
this.imageResult = new BufferedImage(this.imageWidth, this.imageHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = imageResult.createGraphics();
for (BufferedImage imgData : images) {
g.drawImage(imgData, 0, 0, null);
imgData = null;
}
imageResult.flush();
g.dispose();
images.clear();
}
#Override
public void save(OutputStream out, String format) throws IOException {
ImageIO.write(this.imageResult, format, out);
out.flush();
this.imageResult = null;
}
}
usage:
class ImageServlet extends HttpServlet {
void doGet(req,res){
IData data= provider.render(req.getParameter("layers").split(","));
OutputStream out=res.getOutputStream();
data.save(out,"png")
out.flush();
}
}
Note:the provider filed is a single instance.
However it seems that there is a possible memory leak because I will get Out Of Memory exception when the application keep running for about 2 minutes.
Then I use visualvm to check the memory usage:
Even I Perform GC manually, the memory can not be released.
And Though there are only 300+ BufferedImage cached, and 20M+ memory are used, 1.3G+ memory are retained. In fact, through "firebug" I can make sure that a generate image is less than 1Kb. So I think the memory usage is not healthy.
Once I do not use the cache (comment the following line):
//cacher.put(lkey, imageData);
The memory usage looks good:
So it seem that the cached BufferedImage cause the memory leak.
Then I tried to transform the BufferedImage to byte[] and cache the byte[] instead of the object itself. And the memory usage is still normal. However I found the Serialization and Deserialization for the BufferedImage will cost too much time.
So I wonder if you guys have any experience of image caching?
update:
Since there are so many people said that there is no memory leak but my cacher use too many memory, I am not sure but I have tried to cache byte[] instead of BufferedImage directly, and the memory use looks good. And I can not imagine 322 image will take up 1.5G+ memory,event as #BrettOkken said, the total size should be (256 * 256 * 4byte) * 322 / 1024 / 1024 = 80M, far less than 1Gb.
And just now,I change to cache the byte and monitor the memory again, codes change like this:
BufferedImage ig = generateImage(layer,coordinate rwidth, rheight);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(ig, "png", bos);
imageData = bos.toByteArray();
tileCacher.put(lkey, imageData);
And the memory usage:
Same codes, same operation.
Note from both VisualVM screenshots that 97.5% memory consumed by 4,313 instances of int[] (Which I assume is by cached buffered image) is not consumed in non-cached version.
Although you have a less than 1K PNG image (which is compressed as per PNG format), this single image is being generated out of multiple instances of buffered image (which is not compressed). Hence you cannot directly co-relate image size from browser to memory occupied on server. So issue here is not memory leak but amount of memory required to cache this uncompressed layers of buffered images.
Strategy to resolve this is to tweak your caching mechanism:
If possible use compressed version of layers cached instead of raw
images
Ensure that you will never run out of memory by limiting cache size
by instances or by amount of memory utilized. Use either LRU or LIRS
cache eviction policy
Use custom key object with coordinate and layer as two separate
variables overriding with equals/hashcode to use as key.
Observe the behavior and if you have too many cache misses then you
will need better caching strategy or cache may be unnecessary
overhead.
I believe you are caching layers as you expect combinations of layer
and coordinates and hence cannot cache final images but depending on kind of
pattern of requests you expect you may want to consider that option if possible
Not sure what caching API you are using or what are actual values in your request. However based of visualvm it looks to me that String objects are leaking. Also as you mentioned if you turn off caching, problem is resolved.
Consider extract of below snippet of your code.
String lkey = layer + "-" + coordinate;
BufferedImage imageData = cacher.get(lkey);
Now here are few things for you to consider for this code.
You possibly getting new string objects each time for lkey
Your cache has no upper limit with and no eviction policy (e.g. LRU)
Cacher instead of doing String.equals() is doing == and since this
are new string objects they never match causing new entry each time
VisualVM is a start but it doesn't give the complete picture.
You need to trigger a heap dump while the application is using a high amount of memory.
You can trigger a heap dump from VisualVM. It can also be done automatically on an OOME if you add this vmarg to the java process:
-XX:+HeapDumpOnOutOfMemoryError
Use Memory Analyzer Tool to open and inspect the heap dump.
The tool is quite capable and can help you walk the object references to discover:
What is actually using your memory.
Why the objects from #1 aren't being garbage collected.

How to read colors from an image

This is where I called the program to read the SpriteSheet, which was in another class.
private SpriteSheet spriteSheet = new SpriteSheet("/sprite_sheet.png");
This is where I tried to read the pixel colors from the sprite sheet which was colored with Black, Dark grey, Light grey, and White. This is meant to print out color details for the first row of pixels.
public SpriteSheet(String path) {
BufferedImage image = null;
try {
image = ImageIO.read(SpriteSheet.class.getResourceAsStream(path));
} catch (IOException e) {
e.printStackTrace();
}
if(image==null){
return;
}
this.path = path;
this.width = image.getWidth();
this.height = image.getHeight();
pixels = image.getRGB(0, 0, width, height, null, 0, width);
for(int i = 0; i<pixels.length;i++){
pixels[i] = (pixels[i] & 0xff)/64;
}
for(int i=0; i<8;i++){
System.out.println(pixels[i]);
}
When I run this it does not print the numbers like I coded. How could I fix this? And how does the reading of colors work?
If you aren't getting any output then either your println lines aren't getting reached or (far less likely) there's something wrong with your console configuration that is hiding output. We will assume, hopefully correctly, that all methods called here always either return or throw (i.e. nothing is hanging). Presuming the former, the only opportunity I see for that to happen is if the image fails to load and you return.
Since you print the stack trace of any exceptions but do not report seeing a stack trace, that means that ImageIO.read() must be returning null. For that to happen, according to the documentation:
The InputStream is wrapped in an ImageInputStream. If no registered ImageReader claims to be able to read the resulting stream, null is returned.
I do know that PNG is supported generally, but perhaps your specific flavor of PNG is not (e.g. a compressed, 4-color palette or something; I don't know what the limitations of ImageIO are here). I suggest you try saving the image in a different format, or testing with a more baseline PNG format (e.g. uncompressed 24-bit, no transparency, no interlacing).
If my analysis here is correct, the moral of this story is: Improve your error handling. Don't silently fail; especially when performing tasks that are critical to the correct functioning of your program.
If I am not correct, I suggest you either drop some debugging printouts in your method or step through in a debugger to see why those println lines aren't getting executed.

Get buffered image from byte array of raw data

I am using JNA. and i am getting byte array of raw data from my c++ method.
Now i am stuck how to get buffered image in java using this raw data byte array.
I had tried few things to get it as tiff image but i dint get success.
this are the code i tried so far.
here my byte array contains data for 16 bit gray scale image. i get this data from x-sensor device. and now i need to get image from this byte array.
FIRST TRY
byte[] byteArray = myVar1.getByteArray(0, 3318000);//array of raw data
ImageInputStream stream1=ImageIO.createImageInputStream(newByteArrayInputStream(byteArray));
ByteArraySeekableStream stream=new ByteArraySeekableStream(byteArray,0,3318000);
BufferedImage bi = ImageIO.read(stream);
SECOND TRY
SeekableStream stream = new ByteArraySeekableStream(byteArray);
String[] names = ImageCodec.getDecoderNames(stream);
ImageDecoder dec = ImageCodec.createImageDecoder(names[0], stream, null);
//at this line get the error ArrayIndexOutOfBoundsException: 0
RenderedImage im = dec.decodeAsRenderedImage();
I think i am missing here.
As my array is containing raw data ,it does not containthen header for tiff image.
m i right?
if yes then how to provide this header in byte array. and eventually how to get image from this byte array?
to test that i am getting proper byte array from my native method i stored this byte array as .raw file and after opening this raw file in ImageJ software it sows me correct image so my raw data is correct.
The only thing i need is that how to convert my raw byte array in image byte array?
Here is what I am using to convert raw pixel data to a BufferedImage. My pixels are signed 16-bit:
public static BufferedImage short2Buffered(short[] pixels, int width, int height) throws IllegalArgumentException {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY);
short[] imgData = ((DataBufferShort)image.getRaster().getDataBuffer()).getData();
System.arraycopy(pixels, 0, imgData, 0, pixels.length);
return image;
}
I'm then using JAI to encode the resulting image. Tell me if you need the code as well.
EDIT: I have greatly improved the speed thanks to #Brent Nash answer on a similar question.
EDIT: For the sake of completeness, here is the code for unsigned 8 bits:
public static BufferedImage byte2Buffered(byte[] pixels, int width, int height) throws IllegalArgumentException {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
byte[] imgData = ((DataBufferByte)image.getRaster().getDataBuffer()).getData();
System.arraycopy(pixels, 0, imgData, 0, pixels.length);
return image;
}
Whether or not the byte array contains literally just pixel data or a structured image file such as TIFF etc really depends on where you got it from. It's impossible to answer that from the information provided.
However, if it does contain a structured image file, then you can generally:
wrap a ByteArrayInputStream around it
pass that stream to ImageIO.read()
If you just have literally raw pixel data, then you have a couple of main options:
'manually' get that pixel data so that it is in an int array with one int per pixel in ARGB format (the ByteBuffer and IntBuffer classes can help you with twiddling about with bytes)
create a blank BufferedImage, then call its setRGB() method to set the actual pixel contents from your previously prepared int array
I think the above is easiest if you know what you're doing with the bits and bytes. However, in principle, you should be able to do the following:
find a suitable WritableRaster.create... method method that will create a WritableRaster object wrapped around your data
pass that WritableRaster into the relevant BufferedImage constructor to create your image.

Categories