OutOfMemory decoding image with inJustDecodeBounds=true - java

I continuously get OutOfMemory exceptions trying to decode an image from camera in my Android app. There are many questions dealing with the problem, but my case is especially weird because I get the exception even when just trying to get the bounds with options.inJustDecodeBounds=true.
Here's the code that starts the camera:
protected void takePicture() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File image = new File(IMAGE_PATH, "camera.jpg");
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(image));
startActivityForResult(takePictureIntent, 0);
}
Here's the code that triggers the exception:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK ) {
String rawImageName = new String(IMAGE_PATH + "/camera.jpg");
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(rawImageName); // The exception is thrown here
.
.
.
}
}
I tried to decode the image using a very high sampling rate, but still I get the same exception:
options.inSampleSize = 20;
options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap photo = BitmapFactory.decodeFile(rawImageName); // Again the exception
Except for that, the application seems to run correctly and there is enough free memory. I can open correctly the image in the gallery app. Moving the image to a different directory didn't help. Any ideas what could cause it? What could possibly cause the exception while decoding with inJustDecodeBounds = true?

You need to pass the options to the decode call:
BitmapFactory.decodeFile(rawImageName, options);

BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = 4; // 1/4
o2.inPurgeable = true;
Bitmap b=BitmapFactory.decodeFile(imagePath,o2);
try this. and also resize your image and make bitmap objects null after use.
give call to System.gc(); it doesn call gc but it gives hint.
also dont make lots of bitmap objects. reuse the same bitmap object and make it null after use.

Related

FileNotFoundException when loading an Image I just captured

I'm making an app that allows a user to capture and image and then use that image on a puzzle. I can use the camera successfully but after image capture, when I'm supposed to be taken to the puzzle screen where the image is loaded from local storage, I get an FNF exception. (I have a section in the app that shows the images that the user can use for the puzzle and the newly captured image shows up there - after restarting the app since it has crashed).
My code is as follows
public static Bitmap decodeSampledBitmapFromPath(String filepath, int reqWidth, int reqHeight) throws FileNotFoundException {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filepath);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath),
reqWidth, reqHeight, false);
}
Exception is thrown on the return line. Please help me resolve this. Thank you.
Edit: wrapped try catch around return line and now Logcat displays
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int android.graphics.Bitmap.getWidth()' on a null object reference
at apps.mine.puzzle.Board.countTileSize(Board.java:60)
at apps.mine.puzzle.PlayPuzzleActivity.onCreate(PlayPuzzleActivity.java:138)
I had similar issue , this will fix it:
if(BitmapFactory.decodeFile(Image[position])!=null)
{
Bitmap bitmap=Bitmap.createScaledBitmap(BitmapFactory.decodeFile(Image[position]), 32, 32, true);
imageView.setImageBitmap(bitmap);
}
else
{
Log.d("TAG", "unable to decode");
}
the main cause of the issue is that decodeResource is returning you a null for one of the following reasons :
The image file is corrupt
No read permission
There is not enough memory to decode the file
The resource does not exist
Invalid options specified in the options variable.
UPADTE
if you don't want to decode the file twice as #Zoe pointed out, you can modify the code of decodeResource so it'll null-check without having to do it twice, like this :
public class BitmapScalingHelper
{
public static Bitmap decodeResource(Resources res, int resId, int dstWidth, int dstHeight)
{
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth,
dstHeight);
options = new Options();
Bitmap unscaledBitmap = BitmapFactory.decodeResource(res, resId, options);
if(unscaledBitmap == null)
{
Log.e("ERR","Failed to decode resource" + resId + " " + res.toString());
return null;
}
return unscaledBitmap;
}
}

How to display Video thumbnail in ImageView?

I wanted to display my video thumbnail right after taking a video.
This is my current code in the onActivityResult method
if (requestCode == 101)
{
Bitmap bmThumbnail = ThumbnailUtils.createVideoThumbnail("/storage/emulated/0/myvideo.mp4", MediaStore.Images.Thumbnails.MICRO_KIND);
viewImage.setImageBitmap(bmThumbnail);
}
But it doesn't display anything on the image view.
Any workaround for this?
*UPDATE: FIXED
I've changed the code to:
if (requestCode == 101)
{
Bitmap bmThumbnail = ThumbnailUtils.createVideoThumbnail(f.getAbsolutePath().toString(), MediaStore.Images.Thumbnails.MINI_KIND);
viewImage.setImageBitmap(bmThumbnail);
}
The thumbnail is now showing and I have a bigger size, which is acceptable for viewing. Thanks to Sassa.
I just retrieved the absolute path of the file using f.GetAbsolutePath:
if (requestCode == 101)
{
Bitmap bmThumbnail = ThumbnailUtils.createVideoThumbnail(f.getAbsolutePath().toString(), MediaStore.Images.Thumbnails.MINI_KIND);
viewImage.setImageBitmap(bmThumbnail);
}
ThumbnailUtils.createVideoThumbnail will not work with all android OS hence you have to load ffmpeg library in your application to create thumbnail if you want to provide support for all android OS devices.
Thanks,
Devang

java.lang.OutOfMemoryError: bitmap size exceeds VM budget [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Android: Strange out of memory issue while loading an image to a Bitmap object
I am getting following error when I load bitmap on imageview in AsyncTask:
Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
Is there any help?
It could be helpful to watch the memory management session of this year's google i/o:
Memory management for Android Apps Here, Patrick Dubroy explains some cases which can lead to memory leaks (particularly keeping a long lived reference to the application's context via static members). He also talks about the garbage collector.
I had the same problem.
I solved the problem resampling the bitmap to lower resolution, and then using imageView.clearAnimation(); before to load the new bitmap on the imageView.
Vote me if this helps you too.
Regards.
Note: in the following sample, data contains the new image data for the camera image picker and imageView is our ImageView.
Bitmap photo=null;
imageView.clearAnimation();
Uri photoUri = data.getData();
InputStream imageStream = null;
try {
imageStream = getContentResolver().openInputStream(photoUri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
options.inSampleSize=2;
Rect outPadding= new Rect(-1, -1, -1, -1);
photo = BitmapFactory.decodeStream(imageStream, outPadding, options);
imageView.setImageBitmap(photo);

com.android.camera.action.CROP sets wallpaper on Motorola Defy

I'm working on an Android 2.1+ app and trying to take a photo and then send the results of that intent to a crop function. It seems to work on the Sony Ericsson xmp, but when i put it on a Moto Defy, the crop function fails because it is seemingly ignoring the file name i put in for the data, and instead looks at /data/data/com.motorola.gallery/files/temp-wallpaper for some reason. This has the result of the crop function not returning with any data, and the image i just took gets set as the wallpaper image of the phone!
Here's some sample code:
public static final String SD_CARD_TEMP_DIR = Environment.getExternalStorageDirectory() + File.separator;
public static final String SD_CARD_TEMP_ORIG = SD_CARD_TEMP_DIR + "origPhoto.jpg";
public static final String SD_CARD_TEMP_CROP = SD_CARD_TEMP_DIR + "croppedPhoto.jpg";
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(Uri.fromFile(new File(Const.SD_CARD_TEMP_ORIG)), "image/*");
intent.putExtra("crop", true);
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 512);
intent.putExtra("outputY", 512);
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Const.SD_CARD_TEMP_CROP)));
The output from this is something like this:
INFO/ActivityManager(1242): Starting activity: Intent { act=com.android.camera.action.CROP dat=file:///sdcard/origPhoto.jpg typ=image/* cmp=com.motorola.gallery/.CropImage (has extras) }
INFO/ActivityManager(1242): Start proc com.motorola.gallery:CropImage for activity com.motorola.gallery/.CropImage: pid=25733 uid=10014 gids={1015, 9003, 9007, 9008, 2001, 3003}
WARN/CropImage(25733): CropImg mBitmap was null, retreive frm URIfile:///sdcard/origPhoto.jpg
ERROR/CropImage(25733): got IOException java.io.FileNotFoundException: /data/data/com.motorola.gallery/files/temp-wallpaper
INFO/ActivityManager(1242): Displayed activity com.motorola.gallery/.CropImage: 5664 ms (total 5664 ms)
WARN/System.err(25672): java.io.FileNotFoundException: /sdcard/croppedPhoto.jpg
DEBUG/(25672): unable to unlink '/sdcard/croppedPhoto.jpg': No such file or directory (errno=2)
Does anyone have any experience with this issue, or ideas for a work around?
I'd stay away from the Intent you're using, because it isn't standard, so may not be supported everywhere,
From the extra-data you're using, I believe you're doing a fixed-size crop, and don't need any user input. This makes the solution quite simple, as long as you don't run out of memory.
Load the image via BitmapFactory.decodeFile. You can pass in a BitmapFactory.options object to optionally scale the image at load time.
Create a cropped bitmap with Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height)
Write it out to disk with Bitmap.compress

BitmapFactory.decodeResource returns a mutable Bitmap in Android 2.2 and an immutable Bitmap in Android 1.6

I am developing an application and testing it on my device running Android 2.2. In my code, I make use of a Bitmap that I retrieve using BitmapFactory.decodeResource, and I am able to make changes by calling bitmap.setPixels() on it. When I test this on a friend's device running Android 1.6, I get an IllegalStateException in the call to bitmap.setPixels. Documentation online says an IllegalStateException is thrown from this method when the bitmap is immutable. The documentation doesn't say anything about decodeResource returning an immutable bitmap, but clearly that must be the case.
Is there a different call I can make to get a mutable bitmap reliably from an application resource without needing a second Bitmap object (I could create a mutable one the same size and draw into a Canvas wrapping it, but that would require two bitmaps of equal size using up twice as much memory as I had intended)?
You can convert your immutable bitmap to a mutable bitmap.
I found an acceptable solution that uses only the memory of one bitmap.
A source bitmap is raw saved (RandomAccessFile) on disk (no ram memory), then source bitmap is released, (now, there's no bitmap at memory), and after that, the file info is loaded to another bitmap. This way is possible to make a bitmap copy having just one bitmap stored in ram memory per time.
See the full solution and implementation here: Android: convert Immutable Bitmap into Mutable
I add a improvement to this solution, that now works with any type of Bitmaps (ARGB_8888, RGB_565, etc), and deletes the temp file. See my method:
/**
* Converts a immutable bitmap to a mutable bitmap. This operation doesn't allocates
* more memory that there is already allocated.
*
* #param imgIn - Source image. It will be released, and should not be used more
* #return a copy of imgIn, but muttable.
*/
public static Bitmap convertToMutable(Bitmap imgIn) {
try {
//this is the file going to use temporally to save the bytes.
// This file will not be a image, it will store the raw image data.
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "temp.tmp");
//Open an RandomAccessFile
//Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
//into AndroidManifest.xml file
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
// get the width and height of the source bitmap.
int width = imgIn.getWidth();
int height = imgIn.getHeight();
Config type = imgIn.getConfig();
//Copy the byte to the file
//Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes()*height);
imgIn.copyPixelsToBuffer(map);
//recycle the source bitmap, this will be no longer used.
imgIn.recycle();
System.gc();// try to force the bytes from the imgIn to be released
//Create a new bitmap to load the bitmap again. Probably the memory will be available.
imgIn = Bitmap.createBitmap(width, height, type);
map.position(0);
//load it back from temporary
imgIn.copyPixelsFromBuffer(map);
//close the temporary file and channel , then delete that also
channel.close();
randomAccessFile.close();
// delete the temp file
file.delete();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return imgIn;
}
Copy the bitmap to itself with mutable option true. This way neither extra memory consumption nor long lines of code are needed.
Bitmap bitmap= BitmapFactory.decodeResource(....);
bitmap= bitmap.copy(Bitmap.Config.ARGB_8888, true);
We can first set options for BitmapFactory by instantiating an BitmapFactory.Options class and then set the options field named 'inMutable' as true and and then pass this options instance to decodeResource.
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inMutable = true;
Bitmap bp = BitmapFactory.decodeResource(getResources(), R.raw.white, opt);
Here's a solution i've created that uses the internal storage and doesn't require any new permission, based on "Derzu"'s idea, and the fact that starting with honeycomb, this is built in :
/**decodes a bitmap from a resource id. returns a mutable bitmap no matter what is the API level.<br/>
might use the internal storage in some cases, creating temporary file that will be deleted as soon as it isn't finished*/
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static Bitmap decodeMutableBitmapFromResourceId(final Context context, final int bitmapResId) {
final Options bitmapOptions = new Options();
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB)
bitmapOptions.inMutable = true;
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), bitmapResId, bitmapOptions);
if (!bitmap.isMutable())
bitmap = convertToMutable(context, bitmap);
return bitmap;
}
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static Bitmap convertToMutable(final Context context, final Bitmap imgIn) {
final int width = imgIn.getWidth(), height = imgIn.getHeight();
final Config type = imgIn.getConfig();
File outputFile = null;
final File outputDir = context.getCacheDir();
try {
outputFile = File.createTempFile(Long.toString(System.currentTimeMillis()), null, outputDir);
outputFile.deleteOnExit();
final RandomAccessFile randomAccessFile = new RandomAccessFile(outputFile, "rw");
final FileChannel channel = randomAccessFile.getChannel();
final MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, imgIn.getRowBytes() * height);
imgIn.copyPixelsToBuffer(map);
imgIn.recycle();
final Bitmap result = Bitmap.createBitmap(width, height, type);
map.position(0);
result.copyPixelsFromBuffer(map);
channel.close();
randomAccessFile.close();
outputFile.delete();
return result;
} catch (final Exception e) {
} finally {
if (outputFile != null)
outputFile.delete();
}
return null;
}
another alternative is to use JNI in order to put the data into it, recycle the original bitmap, and use the JNI data to create a new bitmap, which will be (automatically) mutable, so together with my JNI solution for bitmaps, one can do the following:
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
final JniBitmapHolder bitmapHolder=new JniBitmapHolder(bitmap);
bitmap.recycle();
bitmap=bitmapHolder.getBitmapAndFree();
Log.d("DEBUG",""+bitmap.isMutable()); //will return true
however, i'm not sure what is the minimal requirement of API level. it works very well on API 8 and above.
I know I'm late to the party, but this is how we avoided this painfully annoying Android problem, and cropped and modified an image with only one copy ever in memory.
Situation
we want to process the pixels of a cropped version of an image saved to file. With high memory demands, we never want to have more than one copy of this image in memory at any given time.
What should have worked but didn't
Opening the image subsection (the bit we wanted to crop to) with BitmapRegionDecoder, passing in a BitmapFactory.option with inMutable = true, processing the pixels then saving to file.
Though our app declared an API minimum of 14 and a target of 19, BitmapRegionDecoder was returning an immutable bitmap, effectively ignoring our BitMapFactory.options
What won't work
opening an mutable image with BitmapFactory (which respects our inMutable option) and croppping it: all cropping techniques are non-imperitive (require a copy of the entire image to exist in memory at a time, even if garbage collected immediately after with overwrites and recycling)
opening an immutable image with BitmapRegionDecoder (effectively cropped) and converting it to a mutable one; all available techniques again require a copy in memory.
The great work-around of 2014
open the full size image with BitmapFactory as a mutable bitmap, and perform our pixel operations
save the bitmap to file and recycle it from memory (it's still un-cropped)
open the saved bitmap with BitmapRegionDecoder, opening only the region to be cropped to (now we don't care if the bitmap is immutable or not)
save this bitmap (which has effectively been cropped) to file, overwriting the previously saved bitmap (which was un-cropped)
With this method, we can crop and perform pixel processing on a bitmap with only 1 copy ever in memory (so we can avoid those pesky OOM errors as much as possible), trading RAM for time as we have to perform extra (slow) file IOs.
It is happening because you want to resize the bitmap by calling setHeight() or setWidth()
To resize any bitmap or drawable (Png, Svg, vector, etc)
public Bitmap editMyBitmap(int drawableId, int newHeight, int newWidth) {
Bitmap myBitmap = BitmapFactory.decodeResource(getResources(), drawableId);
myBitmap = Bitmap.createScaledBitmap(myBitmap, newWidth, newHeight, false);
return myBitmap;
}
Usage Example:
Bitmap facebookIcon = editMyBitmap(R.drawable.facebookImage);
// now use it anywhere
imageView.setBitmapImage(facebookIcon);
canvas.drawBitmap(facebookIcon, 0, 0, null);
I know the question is solved but what about:
BitmapFactory.decodeStream(getResources().openRawResource(getResources().getIdentifier("bitmapname", "drawable", context.getPackageName())))

Categories

Resources