The memory of Bitmap decode - android

I have some questions about Bitmap decode.
When I try to decode Bitmap from a byte[] array, using BitmapFactory.decodeByteArray, what is the difference between the param byte[] and the mBuffer byte[] in result bitmap. when the function return will the bitmap still hold reference to the byte[] param?
when I decode bitmap from a jpg file from sdcard using the following code:
File file = new File(getExternalCacheDir(), "large.jpg");
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
if(mImageView != null){
mImageView.setImageBitmap(bitmap);
}
This jpg file has 10800*5400 resolution and 13.82M size, the result bitmap is not null, and there is no OOM error , but the bitmap not showing. How could that be ? I think in this case android should throw an OOM error so that I can catch it to try down scale the bitmap again. But it just shows nothing. It seems unreasonable. Does any know the reason?

Luckily, I found that when I set the layerType to LAYER_TYPE_SOFTWARE, the bitmap will show. So I believe there is a image size limit for hardware renderer.

Related

Converting byte[] to Bitmap not working - Null pointer exception

When I execute bitmap.getHeight(); I got a nullPointerException. This is how I try to get my bitmap:
In my json:"pic":"iVBORw0KGgoAAAANSUhEUgAAAgAAAAGACAIAAABUQk3......."
I retrieve from json the following:
byte[] decode = Base64.decode(jsonObj.getString("pic"), Base64.DEFAULT);
Log.i("size",decode.length+""); //65535
Bitmap pic = getImage(decode);
public static Bitmap getImage(byte[] image) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
return BitmapFactory.decodeByteArray(image, 0, image.length,options);
}
But I can't display the image.
Any ideas?
The error must be inside BitmapFactory.decodeByteArray and most probably the source of the problem is unsupported image format. Check that you pass base64 encoded jpeg, png, gif or bmp.
Also if you get 65535 as decode's size - it is very suspicious. Maybe the data is trimmed in database (if you have limit of 65535 for blob size) or somewhere else.

Android: Attempting to Pass Bitmap as a Byte Array from One Activity to Another

I have been trying to pass a single byte array (compressed bitmap) from one activity to another. When I attempt to decode the byte array back to a bitmap, and show the bitmap, it appears to be a completely transparent bitmap.
I use this code on the first activity to compress the bitmap and send the byte array:
try {
InputStream selectedImage = getContentResolver().openInputStream(Uri.parse(photoPath));
bitmap = BitmapFactory.decodeStream(selectedImage);
} catch (FileNotFoundException exception) {
Log.e(this.toString(), exception.toString());
}
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte [] byteArray = stream.toByteArray();
Log.e("PANTHERIT_BYTEARRAY", byteArray.toString());
Intent stickerActivity = new Intent (this, StickerActivity.class);
stickerActivity.putExtra("byteArray", byteArray);
startActivity(stickerActivity);
And I use this code in the accepting activity to decompress and store the bitmap:
byte [] byteArray = getIntent().getByteArrayExtra("byteArray");
Log.e("STICKER_BYTEARRAY", byteArray.toString());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length, options);
ImageView bitmapview = (ImageView) findViewById(R.id.bitmap_view);
bitmapview.setImageBitmap(bitmap);
I copied the code to the first activity, compressed the bitmap and immediately decompressed it. I took the decompressed bitmap and set it to an ImageView in that activity and it worked fine. So the error seems to be in the passing of the byte array from the first activity to the second, but I cannot figure out why that would be.
I found an answer. I do not really know why the byte array was getting messed up, maybe it was too large? Even so, both the input and output byte arrays were the same length, so not a very good explanation.
Anyway, I found my answer here: Send Bitmap Using Intent Android. The first answer by Zaid Daghestani is the same as what I initially tried, but his edit is what worked. Instead of passing a byte array, you save the compressed bitmap to a temporary file, and pass the filename through the intent. Definitely works for passing a bitmap, now I just have to figure out how to display the bitmap on a SurfaceView instead of an ImageView...
Also, just in case you need a mutable bitmap (in order to pass it to an ImageView) you can use:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inMutable = true;
bitmap = BitmapFactory.decodeStream(input, null, options);

Error:Bitmap size Exceed VM budget, when converting image to ARGB_8888

Here is my code:
File file = new File(Path to Jpeg File size is 700kb);
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(file));
}
catch (Exception e) {
// TODO: handle exception
}
bitmap =BitmapFactory.decodeStream(in);
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
Please help i get error in this copy line i want to make its ARGB_8888 image.Need Help :(
You need to reduce the memory usage.
From you code, you first decode stream to one bitmap, and then copy it, which means you create two large bitmap objects.
You don't need to decode and then copy it, you can try
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888
// You can try value larger than 1
options.inSampleSize = 2 // If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
// Decode bitmap
bitmap = BitmapFactory.decodeStream(in, null, options)
In this case, there's only one bitmap created. And you set inSampleSize to large values to reduce the loaded bitmap size.

How to convert bitmap image to binary image in android?

I need to convert bitmap to binary image for my hw.Do u know anything about that?
Are you looking for an algorithm to perform the conversion?
The easiest way is to compare each pixel value with a fixed threshold: if the pixel value is less than the threshold, the corresponding output pixel is black (0), else it is white (1).
If you wish to determine the threshold automatically, you may want to implement Otsu's method. That method does a correct job overall when you can't make too many assumptions about the pixels distribution in your image.
http://en.wikipedia.org/wiki/Otsu%27s_Method
As a reference, that's how it looks like in Mathematica:
Binarize[image, threshold], and Binarize[img] for Otsu's method.
You can use the bitmap convert function and write it to an output stream and then use the output stream to get for yourself the byte array
I hope that helps
you can look this link converting Java bitmap to byte array, it can convert bitmap to binary, can then you should look at display image from byteArray
Hope this helps...
Bitmap bitmapObtained =//get your bitmap
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmapObtained.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArray = stream.toByteArray();
perhaps this is your code
imageID = cursor.getString(columnIndex);
// uri = Uri.withAppendedPath(Media.EXTERNAL_CONTENT_URI, "" + imageID);
Log.v("dklfdlk",imageID);
bitmap = BitmapFactory.decodeFile(imageID);
if (bitmap != null) {
newBitmap = Bitmap.createScaledBitmap(bitmap, 78, 78, true);
bitmap.recycle();
if (newBitmap != null) {
publishProgress(new LoadedImage(newBitmap));
}
try this

OutOfMemory exception when loading bitmap from external storage

In my application I load a couple of images from JPEG and PNG files. When I place all those files into assets directory and load it in this way, everything is ok:
InputStream stream = getAssets().open(path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
return new BitmapDrawable(bitmap);
But when I try to load the exact same images from sd card, I get an OutOfMemory exception!
InputStream stream = new FileInputStream("/mnt/sdcard/mydata/" + path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
return new BitmapDrawable(bitmap);
This is what I get in the log:
11-05 00:53:31.003: ERROR/dalvikvm-heap(13183): 827200-byte external allocation too large for this process.
11-05 00:53:31.003: ERROR/GraphicsJNI(13183): VM won't let us allocate 827200 bytes
...
11-05 00:53:31.053: ERROR/AndroidRuntime(13183): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
11-05 00:53:31.053: ERROR/AndroidRuntime(13183): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
...
Why can this happen?
UPDATE: Tried both of these on real device - it seems that I can't load more than 12MB of bitmaps into whatever is called "external memory" (this is not an sd card).
I tried all the approaches mentioned here & at other resources but I came to the inference that setting ImageView's reference to null will solve the issue:
public Bitmap getimage(String path ,ImageView iv)
{
//iv is passed to set it null to remove it from external memory
iv=null;
InputStream stream = new FileInputStream("/mnt/sdcard/mydata/" + path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
stream=null;
return bitmap;
}
& you are done!
Note:Though it may solve above problem but I would suggest you to check Tom van Zummeren 's optimized image loading.
And also check SoftReference: All SoftReferences pointing to softly reachable objects are guaranteed to be cleared before the VM will throw an OutOfMemoryError.
When doing a lot with bitmaps, don't debug the app - just run it. The debugger will leave memory leaks.
Bitmaps are very expensive. If possible, scale them down on load by creating BitmapFactory.Options and setting inSampleSize to >1.
EDIT: Also, be sure to check your app for memory leaks. Leaking a Bitmap (having static Bitmaps is an excellent way to do that) will quickly exhaust your available memory.
Probably nothing wrong with your API usage, I guess all we can do is infer that using the AssetManager involves less behind-the-scenes heap allocation than opening a random file from the SD card.
800KB is a serious allocation in anybody's book... this will doubtless be for the decompressed image pixels. Given that you know the size of the image, what depth is it? If it's 32bpp then try overriding that using inPreferredConfig.
This is a fairly common issue which all of us face while loading images from the sdcard.
The solution as I found was to use inJustDecodeBounds first while loading the image using decodeFileDescriptor . That would not actually decode the image, but give the image size. Now I can scale it appropriately(using the options) so as to resize the image for the display area. Its needed because low memory on the phone can be easily taken over by your 5MP image. This I believe is the most elegant solution.
There are two issues here....
Bitmap memory isn't in the VM heap but rather in the native heap - see BitmapFactory OOM driving me nuts
Garbage collection for the native heap is lazier than the VM heap - so you need to be quite aggressive about doing bitmap.recycle and bitmap =null every time you go through an Activity's onPause or onDestroy
Instead of loading it from the SD Card directly, why not move the image to the cache in the phone's internal storage using getCacheDir() or use a temp directory to store the images in?
See this, this on external memory usage. Also, this article may be of relevance to you.
Use the below code and you will never get the following error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
BitmapFactory.Options bounds = new BitmapFactory.Options();
bounds.inSampleSize = 4;
myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bounds);
picturesView.setImageBitmap(myBitmap);
The best solution i found and edited according to my need
public static Bitmap getImageBitmap(String path) throws IOException{
// Allocate files and objects outside of timingoops
File file = new File(thumbpath);
RandomAccessFile in = new RandomAccessFile(file, "rws");
final FileChannel channel = in.getChannel();
final int fileSize = (int)channel.size();
final byte[] testBytes = new byte[fileSize];
final ByteBuffer buff = ByteBuffer.allocate(fileSize);
final byte[] buffArray = buff.array();
#SuppressWarnings("unused")
final int buffBase = buff.arrayOffset();
// Read from channel into buffer, and batch read from buffer to byte array;
long time1 = System.currentTimeMillis();
channel.position(0);
channel.read(buff);
buff.flip();
buff.get(testBytes);
long time1 = System.currentTimeMillis();
Bitmap bmp = Bitmap_process(buffArray);
long time2 = System.currentTimeMillis();
System.out.println("Time taken to load: " + (time2 - time1) + "ms");
return bmp;
}
public static Bitmap Bitmap_process(byte[] buffArray){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inDither=false; //Disable Dithering mode
options.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
options.inInputShareable=true; //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
options.inTempStorage=new byte[32 * 1024]; //Allocate some temporal memory for decoding
options.inSampleSize=1;
Bitmap imageBitmap = BitmapFactory.decodeByteArray(buffArray, 0, buffArray.length, options);
return imageBitmap;
}
Thanks to all the threads, I've found a solution that works for me on a real device.
The tricks are all about using
BitmapFactory.Options opts=new BitmapFactory.Options();
opts.inSampleSize=(int)(target_size/bitmap_size); //if original bitmap is bigger
But for me this was not enough. My original image (taken from the Camera app) was 3264x2448. The correct ratio for me was 3, since i wanted a plain VGA image of 1024x768.
But setting inSampleSize to 3 was not enough: still out of memory exception.
So in the end I opted for a iterative approach: I start from the computed correct size, and increase it until I stop having a OOM exception.
For me it was at sample of 4.
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
// o2.inSampleSize = scale;
float trueScale = o.outWidth / 1024;
o2.inPurgeable = true;
o2.inDither = false;
Bitmap b = null;
do {
o2.inSampleSize = (int) trueScale;
Log.d(TAG, "Scale is " + trueScale);
try {
b = BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (OutOfMemoryError e) {
Log.e(TAG,"Error decoding image at sampling "+trueScale+", resampling.."+e);
System.gc();
try {
Thread.sleep(50);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
trueScale += 1;
} while (b==null && trueScale < 10);
return b;
You must not depends on the GC to recycle your bitmap memory.
You must clearly recycle the bitmap when it is not needed.
See the Bitmap method:
void recycle()
Free up the memory associated with this bitmap's pixels, and mark the bitmap as "dead", meaning it will throw an exception if getPixels() or setPixels() is called, and will draw nothing.
Try this another way...
Bitmap bmpOrignal = BitmapFactory.decodeFile("/sdcard/mydata/" + path");
Allows inSampleSize resize the final read image.
getLength() of AssetFileDescriptor allows get size of file.
You can vary inSampleSize according to getLength() to prevent OutOfMemory like this :
private final int MAX_SIZE = 500000;
public Bitmap readBitmap(Uri selectedImage)
{
Bitmap bm = null;
AssetFileDescriptor fileDescriptor = null;
try
{
fileDescriptor = this.getContentResolver().openAssetFileDescriptor(selectedImage,"r");
long size = fileDescriptor.getLength();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = (int) (size / MAX_SIZE);
bm = BitmapFactory.decodeFileDescriptor(fileDescriptor.getFileDescriptor(), null, options);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try {
if(fileDescriptor != null) fileDescriptor.close();
} catch (IOException e) {}
}
return bm;
}

Categories

Resources