Android Bitmap bigger than heapSize - android

I'm getting the error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget(Heap Size=12295KB, Allocated=3007KB, Bitmap Size=15621KB)
The bitmap size is larger than my heapSize.. So how can I make the bitmap smaller? Here is the code where it crashes:
private Bitmap getPicture(int position) {
Bitmap bmpOriginal = BitmapFactory.decodeFile(mFileLocations.get(position));
Bitmap bmResult = Bitmap.createBitmap(bmpOriginal.getWidth(), bmpOriginal.getHeight(), Bitmap.Config.RGB_565);
Canvas tempCanvas = new Canvas(bmResult);
tempCanvas.rotate(90, bmpOriginal.getWidth()/2, bmpOriginal.getHeight()/2);
tempCanvas.drawBitmap(bmpOriginal, 0, 0, null);
bmpOriginal.recycle();
}
it crashes at line:
Bitmap bmResult = Bitmap.createBitmap(bmpOriginal.getWidth(), bmpOriginal.getHeight(), Bitmap.Config.RGB_565);
and my decodeFile:
private Bitmap decodeFile(String f){
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//The new size we want to scale to
final int REQUIRED_SIZE=70;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
scale*=2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {
Log.d(LOG_TAG, "Failed to decode file");
}
return null;
}

Scale down your image to be in VM Budget...
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile( filename, options );
options.inJustDecodeBounds = false;
options.inSampleSize = 4;
bitmap = BitmapFactory.decodeFile( filename, options );
if ( bitmap != null ) {
bitmap = Bitmap.createScaledBitmap( bitmap, width, height, false );
}
Change sample size to whatever you want like 2, 4, 6, 8 etc..
For full details refer to this from Android developers site which was posted rescently, it clearly states what you need to do to be in VM budget..

Two things:
It appears that you are using the BitmapFactory.decodeFile method instead of the method you have posted.
Also it looks like the only information you are keeping from bmpOriginal are its dimensions. So why not just use BitmapFactory.inJustDecodeBounds like you do in your decodeFile method. Or do just recycle the bitmap before you create the new one while retaining its dimensions, like this:
int width = bmpOriginal.getWidth();
int height = bmpOriginal.getHeight();
bmpOriginal.recycle();
Bitmap bmResult = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);

This was recently covered in AndroidDevelopers+ :
https://plus.google.com/103125970510649691204/posts/1oSFSyv3pRj

Related

Trying to rotate a bitmap without success

I`m editing a bitmap to optimize it for an OCR scan. One of the things I need to do is to rotate the image 270 degrees. I'm using the following code:
Matrix matrix = new Matrix();
matrix.PostRotate (270);
canvas.DrawBitmap(alteredBitmap, matrix, paint);
Apparently, this does not work for me. Can someone point out where I am wrong?
The bitmap comes from a byte[].
This is the snippet of code I use to rotate a byte[] in my projects. It receives and returns a byte[], but by removing the last 5 lines of code it'll return a Bitmap instead. It works wonders:
public async Task<byte[]> RotateImage(byte[] source, int rotation)
{
//This is optional, use it to reduce your images a little
var options = new BitmapFactory.Options();
options.InJustDecodeBounds = false;
options.InSampleSize = 2;
var bitmap = await BitmapFactory.DecodeByteArrayAsync(source, 0, source.Length, options);
Matrix matrix = new Matrix();
matrix.PostRotate(rotation);
var rotated = Bitmap.CreateBitmap(bitmap, 0, 0, bitmap.Width, bitmap.Height, matrix, true);
var stream = new MemoryStream();
await rotated.CompressAsync(Bitmap.CompressFormat.Jpeg, 100, stream);
var result = stream.ToArray();
await stream.FlushAsync();
return result;
}
All awaitable calls have non-async counterparts, so this can be converted to run in a blocking way without major issues. Just beware that removing the options variable might cause OutOfMemoryException, so make sure you know what you're doing before removing.
This method has always worked for me
Matrix matrix = new Matrix();
//myBitmap is the bitmap which is to be rotated
matrix.postRotate(rotateDegree);
Bitmap bitmap = Bitmap.createBitmap(myBitmap, 0, 0, myBitmap.getWidth(), myBitmap.getHeight(), matrix, true);//Rotated Bitmap
Well if you have this bitmap generated from a url , go ahead and use this function
public Bitmap decodeBitmap(File f) {
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
//The new size we want to scale to
final int REQUIRED_SIZE = 490;
//Find the correct scale value. It should be the power of 2.
int scale = 1;
while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE)
scale *= 2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {
}
return null;
}

OutOfMemoryError in BitmapFactory.decodeFile()

When I pick image from gallery if image size bigger than 3 Mb android the OutOfMemoryError.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bmp = BitmapFactory.decodeFile(imageFilePath, options);
This text from logs. Please help me, becouse "deadline")
E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:623)
at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:378)
at com.DriverNotes.AndroidMobileClientTest.ProfileActivity.onActivityResult(ProfileActivity.java:104)
at android.app.Activity.dispatchActivityResult(Activity.java:5456)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3402)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3449)
at android.app.ActivityThread.access$1200(ActivityThread.java:150)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1328)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
OutofMemory occurs when your app exceeds memory allocated in heap. The bitmap is too large to fit in memory ie heap. In such a case you run out of memory. You need to scale down the bitmap and then use the same.
For that check this link
try this code may help you,
public static Bitmap decodeFile(File f,int WIDTH,int HIGHT){
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//The new size we want to scale to
final int REQUIRED_WIDTH=WIDTH;
final int REQUIRED_HIGHT=HIGHT;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT)
scale*=2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap bm = BitmapFactory.decodeFile(path,options);
Try this.
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inDither=false; //Disable Dithering mode
bmOptions.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
bmOptions.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
bmOptions.inTempStorage=new byte[32 * 1024];
Bitmap mainBitmap = BitmapFactory.decodeFile(filePath, bmOptions);

Loading Multiple Image From server in android Give VM Buget out of memory?

I am using lazy loader example but my application give following error in Logcat.
12-14 15:27:55.987: E/dalvikvm-heap(27566): 2457600-byte external allocation too large for this process.
12-14 15:27:55.992: E/(27566): VM won't let us allocate 2457600 bytes
12-14 15:27:55.992: D/skia(27566): --- decoder->decode returned false
I am use Imageloader class in that write
private Bitmap decodeFile(File f){
try {
//decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
o.inPurgeable = true; // Tell to garbage collector that whether it needs free memory, the Bitmap can be cleared
o.inTempStorage = new byte[32 * 1024];
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE=70;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true){
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
//decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inPurgeable = true; // Tell to garbage collector that whether it needs free memory, the Bitmap can be cleared
o2.inTempStorage = new byte[32 * 1024];
o2.inSampleSize=scale;
Bitmap bitmap1=BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
// System.out.println("width : "+bitmap1.getWidth()+ " height : "+bitmap1.getHeight());
/* if(bitmap1.getHeight()>=bitmap1.getWidth())
{
bitmap1 = Bitmap.createScaledBitmap(bitmap1, bitmap1.getHeight(),bitmap1.getWidth(), true);
}else{
//bmp = Bitmap.createScaledBitmap(bmp, (int) height2,width, true);
Matrix matrix = new Matrix();
matrix.postRotate(270);
bitmap1 = Bitmap.createBitmap(bitmap1 , 0, 0, bitmap1 .getWidth(), bitmap1 .getHeight(), matrix, true);
}*/
return bitmap1;
} catch (FileNotFoundException e) {}
return null;
}
but i use sacle =scale*1 because i want HD image in that use scale-scale*2 then my image like blur..
then what i do?
I recommend you use the library Picasso It is simpler than Android-Universal-Image-Loader but just as powerful.
you dont need to do all this . try this library . this is awesome .
Android-Universal-Image-Loader

OutOfMemoryError when using 2 bitmaps

I have 2 ImageView on my home layout and their content comes from images placed on SD card as shown in below code snippet:
try {
String tempPath1 = Environment.getExternalStorageDirectory()
+ File.separator + "Clipping_Pictures" + File.separator
+ "06-05-2013_02-06-09pm.png";
File f = new File(tempPath1);
Bitmap b = null, b2 = null;
b = BitmapFactory.decodeFile(f.getPath());
if (f.exists()) {
ivClip1.setImageBitmap(b);//ivClip1 is ImageView
}
tempPath1 = Environment.getExternalStorageDirectory()
+ File.separator + "Clipping_Pictures" + File.separator
+ "06-05-2013_02-06-33pm.png";
f = new File(tempPath1);
b2 = BitmapFactory.decodeFile(f.getPath());
if (f.exists()) {
ivClip2.setImageBitmap(b2);
}
} catch (Exception e) {
e.printStackTrace();
}
When I load the app for the 1st time, it displays both the images on respective imageviews. But 2nd launch on-wards, app crashes with following exception:
OutOfMemoryError: bitmap size exceeds VM budget
Note that two resource images are .png and of size ~850kb each which should be fine I guess.
There are similar threads on SO and on internet and I tried some of their suggested solutions, but none seems to work.
Any help appreciated.
If you are building your app for Android 3.0 on wards, then you can use android:largeHeap="true" attribute in your application tag of manifest file.
Doing this, hope your app won't crash due to Out of Memory.
Here is example:
application
android:allowBackup="true"
android:icon="#drawable/icon_96x96"
android:label="#string/app_name"
android:largeHeap="true"
android:theme="#android:style/Theme.NoTitleBar"
Thanks!
its because of large size of your bitmaps. compress your bitmap using following code:
Bitmap ShrinkBitmap(byte[] file, int width, int height){
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeByteArray(file, 0, file.length, bmpFactoryOptions);
int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight/(float)height);
int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth/(float)width);
if (heightRatio > 1 || widthRatio > 1)
{
if (heightRatio > widthRatio)
{
bmpFactoryOptions.inSampleSize = heightRatio;
} else {
bmpFactoryOptions.inSampleSize = widthRatio;
}
}
bmpFactoryOptions.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeByteArray(file, 0, file.length, bmpFactoryOptions);
return bitmap;
}
Are you executing all this code from onCreate() or from onResume()?
You may try to clean the views before you try to load the images again (ivClip1.setImageBitmap(null) or a lightweight one), because while you are decoding both bitmaps you are still having the previous instances in memory while showing.
You can add this lines to resize bitmap and then use it
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file),null,options);
Method to calculate samplesize and reduce the bitmap size
private Bitmap decodeFile(File f){
try {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
final int REQUIRED_SIZE=70;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
scale*=2;
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}

Android: Get thumbnail of image on SD card, given Uri of original image

So i've got a Uri of an image the user chooses out of images off his SD card. And i'd like to display a thumbnail of that image, because obviously, the image could be huge and take up the whole screen. Anyone know how?
You can simply create thumbnail video and image using ThumnailUtil class of java
Bitmap resized = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(file.getPath()), width, height);
public static Bitmap createVideoThumbnail (String filePath, int kind)
Added in API level 8
Create a video thumbnail for a video. May return null if the video is corrupt or the format is not supported.
Parameters
filePath the path of video file
kind could be MINI_KIND or MICRO_KIND
For more Source code of Thumbnail Util class
Developer.android.com
This code will do the job:
Bitmap getPreview(URI uri) {
File image = new File(uri);
BitmapFactory.Options bounds = new BitmapFactory.Options();
bounds.inJustDecodeBounds = true;
BitmapFactory.decodeFile(image.getPath(), bounds);
if ((bounds.outWidth == -1) || (bounds.outHeight == -1))
return null;
int originalSize = (bounds.outHeight > bounds.outWidth) ? bounds.outHeight
: bounds.outWidth;
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = originalSize / THUMBNAIL_SIZE;
return BitmapFactory.decodeFile(image.getPath(), opts);
}
You may want to calculate the nearest power of 2 to use for inSampleSize, because it's said to be faster.
I believe this code is fastest way to generate thumbnail from file on SD card:
public static Bitmap decodeFile(String file, int size) {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file, o);
//Find the correct scale value. It should be the power of 2.
int width_tmp = o.outWidth, height_tmp = o.outHeight;
int scale = (int)Maths.pow(2, (double)(scale-1));
while (true) {
if (width_tmp / 2 < size || height_tmp / 2 < size) {
break;
}
width_tmp /= 2;
height_tmp /= 2;
scale++;
}
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeFile(file, o2);
}
A few trying I could not get the thumbnail path of image from SD.
i am resolved this problem getting an android image bitmap, before I create an image view in adapter for gridview (or where you need). So i call method imageView.setImageBitmap(someMethod(Context context, imageID))
Bitmap someMethod(Context context, long imageId){
Bitmap bitmap = Media.Images.Thumbnails.getThumbnail(context.getAplicationContext.getContentResolver(), imageid, MediaStore.Images.Thumbnails.MINI_KIND, null);
return bitmap;
}
You can get image ID from your SD using this guide (Get list of photo galleries on Android)
If you like HQ thumbnails, so use [RapidDecoder][1] library. It is simple as follow:
import rapid.decoder.BitmapDecoder;
...
Bitmap bitmap = BitmapDecoder.from(getResources(), R.drawable.image)
.scale(width, height)
.useBuiltInDecoder(true)
.decode();
Don't forget to use builtin decoder if you want to scale down less than 50% and a HQ result.
I tested it in API Level 8 :)
This package will let you access image URI to receive image size, large Bitmap data, sampling image to any smaller size for saving memory and maximize performance.
It uses InputStream and BitmapFactory:
public int[] getImageSize(Uri uri){
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
InputStream input = this.getContentResolver().openInputStream(uri);
BitmapFactory.decodeStream(input, null, options); input.close();
return new int[]{options.outWidth, options.outHeight};
}
catch (Exception e){}
return new int[]{0,0};
}
public Bitmap BitmapImage(Uri uri){return BitmapImage(uri,-1,-1);}
public Bitmap BitmapImage(Uri uri, int maxSize){return BitmapImage(uri,maxSize,maxSize);}
public Bitmap BitmapImage(Uri uri, int Wmax, int Hmax){
try {
InputStream input = this.getContentResolver().openInputStream(uri);
double ratio=1;
if ((Wmax>-1)&&(Hmax>-1)){
int[] wh=getImageSize(uri); double w=wh[0], h=wh[1];
if (w/Wmax>1){ratio=Wmax/w; if (h*ratio>Hmax){ratio=Hmax/h;}}
else if (h/Hmax>1){ratio=Hmax/h;}
}
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inSampleSize = (int)Math.ceil(1/ratio);
bitmapOptions.inPreferredConfig=Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
input.close();
return bitmap;
}
catch (Exception e){}
return null;
}
Four functions for different use case:
/*
getImageSize(Uri uri): return int[]{ width, height}
BitmapImage(Uri uri): return Bitmap in full size
BitmapImage(Uri uri, int maxSize): return sampled Bitmap which is limited in square size
BitmapImage(Uri uri, int Wmax, int Hmax): return sampled Bitmap which is limited width and height
*/

Categories

Resources