I am using BitmapFactory.decodeStream(inputStream) to create a bitmap. But the size of the bitmap returned by bitmap.getByteCount() is coming out be very huge compared to the actual image size. The actual image size when I download(on disk) is around 36kb, but the bitmap generated has a size of around 800kb. Since it is the same image, how come there is such a huge difference ? I am using the following piece of code.
Bitmap.Config.RGB_565 is helping me to reduce the size of the image
public Bitmap downloadImage(String imageUrl) {
Bitmap bitmap = null;
InputStream inputStream = null;
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
inputStream = (new URL(imageUrl)).openStream();
bitmap = BitmapFactory.decodeStream(inputStream, null, options);
Log.d("downloadImage","size of downloaded image " + bitmap.getByteCount()/1024);
return bitmap;
} catch (Exception e) {
}
finally {
try {
if(inputStream != null) {
inputStream.close();
}
} catch(Exception e) {
}
}
return bitmap;
}
After using the device camera I get a 52MB bitmap and I decode it this way:
try {
bitmapLarge = BitmapFactory.decodeFile(pathName, bitmapOptions);
} catch (OutOfMemoryError ex) {
ex.printStackTrace();
try {
bitmapOptions.inSampleSize = 2;
bitmapOptions.inJustDecodeBounds = false;
bitmapLarge = BitmapFactory.decodeFile(pathName, bitmapOptions);
bitmapOptions.inSampleSize = 1;
} catch (OutOfMemoryError ex2) {
ex2.printStackTrace();
try {
bitmapOptions.inSampleSize = 4;
bitmapOptions.inJustDecodeBounds = false;
bitmapLarge = BitmapFactory.decodeFile(pathName, bitmapOptions);
} catch (OutOfMemoryError ex3) {
ex3.printStackTrace();
bitmapOptions.inSampleSize = 8;
bitmapOptions.inJustDecodeBounds = false;
bitmapLarge = BitmapFactory.decodeFile(pathName, bitmapOptions);
bitmapOptions.inSampleSize = 1;
}
bitmapOptions.inSampleSize = 1;
}
} catch (Exception ex) {
ex.printStackTrace();
Log.e("resizing bitmap", "#### NULL");
bitmapLarge = null;
return null;
}
I get a huge OutOfMemoryError at the first attempt to decodeFile:
12-15 18:24:24.393: E/dalvikvm-heap(12890): Out of memory on a 51916816-byte allocation.
How do I know what the size of the bitmap would be BEFORE trying to do anything with it?
And even if this is possible, how do I decode it downsampled somehow?
Instead of doing bitmapOptions.inJustDecodeBounds = false; change that to true and you will get back the bounds of the image
you should probably read this too http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
with inJustDecodeBounds = true. You will get back a null bitmap, but the bitmapOptions object will be filled with Bitmap's width and height. Then it is just Mathematic.. You need width * height * 4 bytes to keep your bitmap in memory
I am working on a code sample where I have to choose an image from gallery the code is working but after selection of image from gallery I get OutOfMemoryError in my OnActivityResult
I am able to get small images but large images are creating problem.
Here is my code:
try{
Uri selectedImageUri = data.getData();
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(selectedImageUri, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String filePath = cursor.getString(columnIndex);
cursor.close();
bitmap = BitmapFactory.decodeFile(filePath);
_profileImage.setImageBitmap(bitmap);
_profileImage.setScaleType(ScaleType.FIT_XY);
Constant._addPhotoBitmap=bitmap;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Bitmap resizedbitmap = Bitmap.createScaledBitmap(bitmap, 200, 200, true);
resizedbitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte [] _byteArray = baos.toByteArray();
String base64 = Base64.encodeToString(_byteArray,Base64.DEFAULT);
Constant._addPhotoBase64 = base64;
}catch (OutOfMemoryError e) {
e.printStackTrace();
Constant.showAlertDialog(Constant.errorTitle,
"Image size is too large.Please upload small image.",
DriverProfileScreen.this, false);
} catch (Exception e) {
e.printStackTrace();
}
You are deirectly decoding the file based on its uri path..thats why it is throwing exception..before loading image set some options..this will reduce the memory for the image loading..Use this method for loading image whatever size you want..
/**
* returns the thumbnail image bitmap from the given url
*
* #param path
* #param thumbnailSize
* #return
*/
private Bitmap getThumbnailBitmap(final String path, final int thumbnailSize) {
Bitmap bitmap;
BitmapFactory.Options bounds = new BitmapFactory.Options();
bounds.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, bounds);
if ((bounds.outWidth == -1) || (bounds.outHeight == -1)) {
bitmap = null;
}
int originalSize = (bounds.outHeight > bounds.outWidth) ? bounds.outHeight
: bounds.outWidth;
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = originalSize / thumbnailSize;
bitmap = BitmapFactory.decodeFile(path, opts);
return bitmap;
}
In Android Developer document there is Topic called
Displaying Bitmaps Efficiently
So please go through it.
http://developer.android.com/training/displaying-bitmaps/index.html
Hope this will help you.
Generally android device heap size is only 16MB (varies from device/OS see post Heap Sizes), if you are loading the images and it crosses the size of 16MB , it will throw out of memory exception, instead of using the Bitmap for , loading images from SD card or from resources or even from network try to using getImageUri , loading bitmap require more memory , or you can set bitmap to null if your work done with that bitmap.
So,You need to downscale you image using below code:
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;
}
Scale the bitmap first , then load it. It will solve the problem.
You can use the following method to do that.
private Bitmap getScaledBitmap(Uri uri){
Bitmap thumb = null ;
try {
ContentResolver cr = getContentResolver();
InputStream in = cr.openInputStream(uri);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize=8;
thumb = BitmapFactory.decodeStream(in,null,options);
} catch (FileNotFoundException e) {
Toast.makeText(PhotoTake.this , "File not found" , Toast.LENGTH_SHORT).show();
}
return thumb ;
}
Hope it helps.
try this code:
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.ActivityManager;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.widget.ImageView;
public class UserImageLoaderWithCache implements ComponentCallbacks {
private KCLruCache cache;
public UserImageLoaderWithCache(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass() * 1024 * 1024;
cache = new KCLruCache(memoryClass);
}
public void display(String url, ImageView imageview, int defaultresource) {
imageview.setImageResource(defaultresource);
Bitmap image = cache.get(url);
if (image != null) {
imageview.setImageBitmap(image);
}
else {
new SetImageTask(imageview).execute(url);
}
}
private class KCLruCache extends LruCache<String, Bitmap> {
public KCLruCache(int maxSize) {
super(maxSize);
}
}
private class SetImageTask extends AsyncTask<String, Void, Integer> {
private ImageView imageview;
private Bitmap bmp;
public SetImageTask(ImageView imageview) {
this.imageview = imageview;
}
#Override
protected Integer doInBackground(String... params) {
String url = params[0];
try {
bmp = getBitmapFromURL(url);
if (bmp != null) {
cache.put(url, bmp);
} else {
return 0;
}
} catch (Exception e) {
e.printStackTrace();
return 0;
} catch (OutOfMemoryError o) {
o.printStackTrace();
return 0;
}
return 1;
}
#Override
protected void onPostExecute(Integer result) {
if (result == 1) {
imageview.setImageBitmap(bmp);
}
super.onPostExecute(result);
}
private Bitmap getBitmapFromURL(String src) {
try {
URL url = new URL(src);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap myBitmap = BitmapFactory.decodeStream(input);
return myBitmap;
} catch (Exception e) {
e.printStackTrace();
return null;
}catch (OutOfMemoryError o) {
o.printStackTrace();
return null;
}
}
}
public void onLowMemory() {
}
/*public void onTrimMemory(int level) {
if (level >= TRIM_MEMORY_MODERATE) {
cache.evictAll();
}
else if (level >= TRIM_MEMORY_BACKGROUND) {
cache.trimToSize(cache.size() / 2);
}
}*/
public void onConfigurationChanged(Configuration arg0) {
// TODO Auto-generated method stub
}
}
I have used below code and used bitmap to store resized Image in local storage and It worked like charm
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap b = BitmapFactory.decodeFile(path, options);
here path is image Uri path in String
I develop a App with ffmpeg to decode a media frame. I filled a Bitmap object with the decode result and use ImageView.setImageBitmap to display the bitmap.
In Android 2.3 it works well, but in Android 4.0 or up it doesn't work.
The code is simply :
imgVedio.setImageBitmap(bitmapCache);//FIXME:in 4.0 it displays nothing
Then I tried write the Bitmap to a file and reload the file to display.
String fileName = "/mnt/sdcard/myImage/video.jpg";
FileOutputStream b = null;
try
{
b = new FileOutputStream(fileName);
bitmapCache.compress(Bitmap.CompressFormat.JPEG, 100, b);// write data to file
}
catch (FileNotFoundException e)
{
e.printStackTrace();
} finally
{
try
{
if(b != null)
{
b.flush();
b.close();
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
Bitmap bitmap = BitmapFactory.decodeFile(fileName);
imgVedio.setImageBitmap(bitmap);
It works, but the performance is too poor.
So can someone help me resolve the problem?
I think it's an out of memory problem, you can fix it using this method :
private Bitmap loadImage(String imgPath) {
BitmapFactory.Options options;
try {
options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeFile(imgPath, options);
return bitmap;
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
The "inSampleSize" option will return a smaller image and save memory. You can just call this in the ImageView.setImageBitmap :
imgVedio.setImageBitmap(loadImage(IMAGE_PATH));
actually i'm opening png files from assets folder with this code:
public static Bitmap loadImage( String imageName ){
if( imageName.charAt(0) == '/' ) {
imageName = imageName.substring(1);
}
imageName = imageName + ".png";
Bitmap image = BitmapFactory.decodeStream(getResourceAsStream(imageName));
return image;
}
public static InputStream getResourceAsStream( String resourceName ) {
if( resourceName.charAt(0) == '/' ) {
resourceName = resourceName.substring(1);
}
InputStream is = null;
try {
is = context.getAssets().open( resourceName );
} catch (IOException e) {e.printStackTrace();}
return is;
}
This code opens the bitmaps with full cuality and it takes a lot of time to open it. I will try to use RGB_565 to speed up the opening of the bitmap.
What should i change into my code to open the bitmap with RGB_565? As you can see, i dont know the width and the height of the image.
Also any sugerences to speed up the opening of the bitmap will be welcome
Thanks
Add BitmapFactory.Options to to decodeStream() call:
BitmapFactory.Options bitmapLoadingOptions = new BitmapFactory.Options();
bitmapLoadingOptions.inPreferredConfig = Bitmap.Config.RGB_565;
BitmapFactory.decodeStream(instream,null,bitmapLoadingOptions);
As for how to speed up loading the image? Not sure what else you can do except maybe reduce the size of the image if that's possible.
This code worked for me
AssetManager assetManager = getAssets();
InputStream istr = null;
try {
istr = assetManager.open(strName);
} catch (IOException e) {
e.printStackTrace();
}
BitmapFactory.Options bitmapLoadingOptions = new BitmapFactory.Options();
bitmapLoadingOptions.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeStream(istr,null,bitmapLoadingOptions);
return bitmap;