Out of Memory error on createbitmap - android

BitmapFactory.Options bounds = new BitmapFactory.Options();
bounds.inJustDecodeBounds = true;
BitmapFactory.decodeFile(Common.getRealPathFromURI(selectedImage,rootView.getContext()), bounds);
BitmapFactory.Options opts = new BitmapFactory.Options();
Common.setBitmap(null);
Bitmap bm = BitmapFactory.decodeFile(Common.getRealPathFromURI(selectedImage, rootView.getContext()), opts);
saveToInternalSorage(bm);
ExifInterface exif = new ExifInterface(Common.getRealPathFromURI(selectedImage,rootView.getContext()));
String orientString = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
int orientation = orientString != null ? Integer.parseInt(orientString) : ExifInterface.ORIENTATION_NORMAL;
int rotationAngle = 0;
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) rotationAngle = 90;
if (orientation == ExifInterface.ORIENTATION_ROTATE_180) rotationAngle = 180;
if (orientation == ExifInterface.ORIENTATION_ROTATE_270) rotationAngle = 270;
Matrix matrix = new Matrix();
matrix.postRotate(rotationAngle);
try
{
Bitmap rotatedBitmap = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
bitmap2 = rotatedBitmap;//BitmapFactory.decodeStream(imageStream);
}
catch(OutOfMemoryError e)
{
}
Basically at times, i get my image rotated. So I have implemented that function, the problem lies on createbitmap i get outofmemory error. I want to avoid that error , without modifying the size of the image... and the quality. i want to keep same size and quality.

If you want to use large heap, you should write in AndroidManifest.xml.
android:largeHeap="true"
If you want to check heap size on your device, call ActivityManager.getLargeMemoryClass().
ActivityManager am = ((ActivityManager)getSystemService(Activity.ACTIVITY_SERVICE));
int largeMemory = am.getLargeMemoryClass();

You have far bigger problems, such as the fact that your app will not work on most Android devices. Use your Uri properly and get rid of getRealPathFromURI().
That being said, there is no guarantee that you can allocate a bitmap of the size needed to rotate the image. Whether you can or not will be based on the state of your heap and the resolution of the image. Your choices are:
Switch to using native code via the NDK for the image rotation. This too is not guaranteed to work, but the odds are a lot higher, because you have all system RAM to work with.
Switch to using android:largeHeap="true". This will have no effect on many devices. However, on some, it will increase your heap limit and make it somewhat more likely that your bitmap allocation will succeed.
Settle for resizing the image.
Tell the user "sorry, we do not have enough memory to rotate the image".

Related

Android camera: get a straight picture on all devices

So i got this simple app, open the camera (either front or back), take a picture, post it.
I managed to get a straight preview, despite the display orientation.
The problem is when I get the picture. If for example I take a photo with different devices in portrait mode, the resulting pictures are rotated of different angles, depending which device was used.
All I'm asking is simple: do you know how I can get straight pictures (either portrait or landscape, according to the rotation) and make sure it works on every device?
Well' if any of you has a solution,please share it,I'll be eternally grateful!
Different devices use different orientations on their pictures by default. This can be annoying. What you need to do is find that orientation, and flip the image correspondingly.
Here's some code that I use in my application:
Note: I'm sure this isn't perfect. If anyone has suggestions on how to improve this, please feel free to comment/edit!
private int getOrientation(String pathToYourImage) throws IOException{
ExifInterface exif = new ExifInterface(path);
int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
if (rotation == ExifInterface.ORIENTATION_ROTATE_90){
return 90;
} else if (rotation == ExifInterface.ORIENTATION_ROTATE_180){
return 180;
} else if (rotation == ExifInterface.ORIENTATION_ROTATE_270){
return 270;
}
return 0;
}
then something like
if (orientation > 0){
originalImage = rotateBitmap(originalImage, orientation);
}
private Bitmap rotateBitmap(Bitmap srcBitmap, int angle){
Matrix matrix = new Matrix();
matrix.postRotate(angle);
Bitmap rotatedBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.getWidth(), srcBitmap.getHeight(), matrix, true);
srcBitmap.recycle();
return rotatedBitmap;
}
This might not completely suit your needs, but it works well enough for me. Ask if you have any more questions!

Out of memory when processing multiple large bitmaps

In the app I'm working on, part of the user's input is a series of images. Some of these might be 4MB large in their raw form. I resize and rotate them, then save them in the app's portion of the device memory for later use. The problem I'm experiencing is that I seem to run out of memory even though I recycle each Bitmap after it's saved.
Here's the main processing
private class SaveImagesTask extends AsyncTask<Long, Void, Void>{
#Override
protected Void doInBackground(Long... ids){
long id = ids[0];
Iterator<ImageButton> itImg = arrBtnImage.iterator();
Iterator<TextView> itLbl = arrLblImage.iterator();
while(itImg.hasNext() && itLbl.hasNext()){
String imgPath = (String) itImg.next().getTag();
String imgLbl = itLbl.next().getText().toString().trim();
String imgName = imgLbl.replace(" ", "_").replace(",", "_");
imgName += ".jpg";
if(imgPath != null){
/* Save resized version of image */
File dir = getApplicationContext().getFilesDir();
dir = new File(dir, "temp/" + Long.toString(plantId));
boolean madeDir = dir.mkdirs();
File path = new File(dir, imgName);
Bitmap toSave = getScaledBitmap(imgPath, IMAGE_MAX_SIDE_LENGTH, IMAGE_MAX_SIDE_LENGTH);
try{
BufferedOutputStream outStream = new BufferedOutputStream(new FileOutputStream(path));
boolean insertSuccess = toSave.compress(Bitmap.CompressFormat.JPEG, 90, outStream);
outStream.close();
}
catch(FileNotFoundException e){
e.printStackTrace();
}
catch(IOException e){
e.printStackTrace();
}
toSave.recycle();
}//if
}//while(more images to process)
}// method: doInBackground(params)
}// inner class: saveImages extends AsyncTask
And here's where I resize the image
private Bitmap getScaledBitmap(String picturePath, int newWidth, int newHeight){
/* Size */
BitmapFactory.Options sizeOptions = new BitmapFactory.Options();
sizeOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(picturePath, sizeOptions);
int sampleSize = 1;
int rawHeight = sizeOptions.outHeight;
int rawWidth = sizeOptions.outWidth;
if(rawHeight > newHeight || rawWidth > newWidth){
/* Find the dimension that needs to change the most */
int heightRatio = Math.round((float) rawHeight / (float) newHeight);
int widthRatio = Math.round((float) rawWidth / (float) newWidth);
sampleSize = (heightRatio > widthRatio ? heightRatio : widthRatio);
}//if(raw image is wider or taller than it should be){reduce size so neither is too large}
sizeOptions.inJustDecodeBounds = false;//Load pixels for display.
sizeOptions.inSampleSize = sampleSize;//Set shrink factor.
Bitmap scaledBitmap = BitmapFactory.decodeFile(picturePath, sizeOptions);
/* Rotation */
int rotation = 1;
try{
ExifInterface exif = new ExifInterface(picturePath);
rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
}
catch(IOException e){
e.printStackTrace();
}
int rotationInDegrees = 0;
if(rotation == ExifInterface.ORIENTATION_ROTATE_90)
rotationInDegrees = 90;
else if(rotation == ExifInterface.ORIENTATION_ROTATE_180)
rotationInDegrees = 180;
else if(rotation == ExifInterface.ORIENTATION_ROTATE_270)
rotationInDegrees = 270;
Matrix matrix = new Matrix();
if(rotation != 0f)
matrix.preRotate(rotationInDegrees);
return Bitmap.createBitmap(scaledBitmap, 0, 0,
scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
}// method: getScaledBitmap(String, int, int)
Before I start getting comments about this being so common of a question, I'll point out that I'm not displaying these images, so it's not like I'm trying to keep all of these in memory. I need to keep large images because users will want to be able to zoom in on the pictures, but I'm resizing them because they don't need to be ridiculously huge. Pretty much any other solution I've seen on SO for images and OOM errors don't apply to my back-to-back access of multiple images.
So like I said, I'm recycling each Bitmap after it's saved, but they still seem to be using memory. Any idea what I'm missing?
You're not recycling scaledBitmap in getScaledBitmap. Fixing that should help. Change this line:
return Bitmap.createBitmap(scaledBitmap, 0, 0,
scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
to something like:
Bitmap newBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0,
scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
scaledBitmap.recycle();
return newBitmap;
If you have multiple threads working on large bitmaps, you will use a lot of memory on some cases.
What you need is to find the best approach according to your needs. here are some things you can do and/or need to know:
use a single thread for the images handling.
always recycle old bitmaps that you don't need anymore, as soon as possible. it's true that the GC will help you, but that can help it too, and it will work even on pre-honeycomb devices.
do the image manipulations via NDK (so you won't need to have 2 bitmaps for each image manipulation), for example using this.
downsample the image to the minimal size that you need, and never assume that the memory is large enough for any given image (unless you are 100% sure that the images are small).
remember that the requirements for android devices are still very low in terms of RAM per app (heap size) - the bare minimal is still 16MB per app.
you can use android:largeHeap="true" in the manifest, but that doesn't mean anything about how much more you will get, if at all.

How to set captured images in portrait mode

I am working on a application in which I have given facility to user that he can take a picture using his mobile camera and then I am displaying the image in a Imageview.
Now the problem is that if I am capturing the image in a portrait mode or in landscape mode it is always setting the image in landscape mode in ImageView, but I want the image to be set in portrait mode only. Please help me out with this problem.
Any help would be appreciable...
Thank you
Matrix mat = new Matrix();
ExifInterface exif = new ExifInterface(yourimagepath);
String orientstring = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
int orientation = orientstring != null ? Integer.parseInt(orientstring) : ExifInterface.ORIENTATION_NORMAL;
int rotateangle = 0;
if(orientation == ExifInterface.ORIENTATION_ROTATE_90)
rotateangle = 90;
if(orientation == ExifInterface.ORIENTATION_ROTATE_180)
rotateangle = 180;
if(orientation == ExifInterface.ORIENTATION_ROTATE_270)
rotateangle = 270;
mat.setRotate(rotateangle, (float) bmpPic.getWidth() / 2, (float) bmpPic.getHeight() / 2);
File f = new File(yourimagepath);
Bitmap bmpPic = BitmapFactory.decodeStream(new FileInputStream(f), null, null);
Bitmap bmpPic1 = Bitmap.createBitmap(bmpPic, 0, 0, bmpPic.getWidth(), bmpPic.getHeight(), mat, true);
use like that
Matrix matrix=new Matrix();
imageView.setScaleType(ScaleType.MATRIX); //required
matrix.postRotate((float) angle, pivX, pivY);
imageView.setImageMatrix(matrix);
for exp:
matrix.postRotate( 90f, imageView.getDrawable().getBounds().width()/2, imageView.getDrawable().getBounds().height()/2)
Here is a great solution I came across for this:
https://stackoverflow.com/a/34241250/8033090
One line solution:
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
or
Picasso.with(context).load("file:" + photoPath).into(imageView);
This will autodetect rotation and place image in correct orientation
Picasso is a very powerful library for handling images in your app includes: Complex image transformations with minimal memory use. It can take a second to load but I just put some text behind the image view that says "Loading image" and when the image loads it covers the text.

Android Camera Intent Saving Image Landscape When Taken Portrait [duplicate]

This question already has answers here:
Why does an image captured using camera intent gets rotated on some devices on Android?
(23 answers)
Closed 4 years ago.
I have had a look around but there doesn't seem to be a solid answer/solution to the, very irritating, problem.
I take a picture in portrait orientation and when I hit save/discard the buttons are in the correct orientation also. The problem is when I then retrieve the image later on it is in landscape orientation (the picture has been rotated 90 degrees anti-clockwise)
I don' want to force the user to use the camera in a certain orientation.
Is there a way to maybe detect whether the photo was taken in portrait mode and then decode the bitmap and flip it the correct way up?
The picture is always taken in the orientation the camera is built into the device. To get your image rotated correctly you'll have to read the orientation information that is stored into the picture (EXIF meta data). There it is stored how the device was oriented, when the image was taken.
Here is some code that reads the EXIF data and rotates the image accordingly:
file is the name of the image file.
BitmapFactory.Options bounds = new BitmapFactory.Options();
bounds.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file, bounds);
BitmapFactory.Options opts = new BitmapFactory.Options();
Bitmap bm = BitmapFactory.decodeFile(file, opts);
ExifInterface exif = new ExifInterface(file);
String orientString = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
int orientation = orientString != null ? Integer.parseInt(orientString) : ExifInterface.ORIENTATION_NORMAL;
int rotationAngle = 0;
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) rotationAngle = 90;
if (orientation == ExifInterface.ORIENTATION_ROTATE_180) rotationAngle = 180;
if (orientation == ExifInterface.ORIENTATION_ROTATE_270) rotationAngle = 270;
Matrix matrix = new Matrix();
matrix.setRotate(rotationAngle, (float) bm.getWidth() / 2, (float) bm.getHeight() / 2);
Bitmap rotatedBitmap = Bitmap.createBitmap(bm, 0, 0, bounds.outWidth, bounds.outHeight, matrix, true);
UPDATE 2017-01-16
With the release of the 25.1.0 Support Library, an ExifInterface Support Library was introduced, which should perhaps make the access to the Exif attributes easier. See the Android Developer's Blog for an article about it.
The selected answer uses the most common method answered to this and similar questions. However, it did not work for me with both front and back cameras on Samsung. For those needing another solution which works across both front and back cameras for Samsung and other major manufacturers, this answer by nvhausid is awesome:
https://stackoverflow.com/a/18915443/6080472
For those who don't want to click through, the relevant magic is to use the CameraInfo rather then relying on EXIF or a Cursor over the media files.
Bitmap realImage = BitmapFactory.decodeByteArray(data, 0, data.length);
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(mCurrentCameraId, info);
Bitmap bitmap = rotate(realImage, info.orientation);
Full code in the link.

Android get Orientation of a camera Bitmap? And rotate back -90 degrees

I have this code:
//choosed a picture
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == ImageHelper.SELECT_PICTURE) {
String picture = "";
Uri selectedImageUri = data.getData();
//OI FILE Manager
String filemanagerstring = selectedImageUri.getPath();
//MEDIA GALLERY
String selectedImagePath = ImageHelper.getPath(mycontext, selectedImageUri);
picture=(selectedImagePath!=null)?selectedImagePath:filemanagerstring;
...
This is only a picture chooser, from gallery. This is nice, but when I open this picture on an ImageView, the images when took on "PORTRAIT MODE" with the camera look nice, but the images that took "LANDSCAPE MODE" with the camera, opening in -90 degrees.
How can i rotate those pictures back?
Bitmap output = Bitmap.createBitmap(newwidth, newheight, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
I tried this:
Log.e("w h", bitmap.getWidth()+" "+bitmap.getHeight());
if (bitmap.getWidth()<bitmap.getHeight()) canvas.rotate(-90);
But this is not working, all image size is: *2560 1920 pixel (PORTRAIT, and LANDSCAPE mode all)
What can I do to rotate back the LANDSCAPE pictures?
If a photo is taken with a digital camera or smartphone, rotation is often stored in the photo's Exif data, as part of the image file. You can read an image's Exif meta-data using the Android ExifInterface.
First, create the ExifInterface:
ExifInterface exif = new ExifInterface(uri.getPath());
Next, find the current rotation:
int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
Convert exif rotation to degrees:
int rotationInDegrees = exifToDegrees(rotation);
where
private static int exifToDegrees(int exifOrientation) {
if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) { return 90; }
else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) { return 180; }
else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) { return 270; }
return 0;
}
Then use the image's actual rotation as a reference point to rotate the image using a Matrix.
Matrix matrix = new Matrix();
if (rotation != 0) {matrix.preRotate(rotationInDegrees);}
You create the new rotated image with the Bitmap.createBitmap method that take a Matrix as a parameter:
Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)
where Matrix m holds the new rotation:
Bitmap adjustedBitmap = Bitmap.createBitmap(sourceBitmap, 0, 0, width, height, matrix, true);
See this tutorial for a useful source code example:
Read Exif information in a JPEG file.
if you are Using Jetpack CameraX, inside onImageCaptured method you can access rotation degree provided by EXIF data from the imageProxy like this:
image.imageInfo.rotationDegrees
then while setting your image you can rotate your image according to this degree
Last answer was technically perfect, but I tried hard to create a system to manage pictures, rotate, resize, cache and load into ImageViews and I can tell it is a hell. Even when all it was done it crashes sometimes cause OutOfMemory in some devices.
My point is do not reinvent the wheel, it has a perfect design. Google itself encourage you to use Glide. It works in one line, super easy to use, lightweight in size and functions number, it manage EXIF by default, and it use memory like a charm.. It is simply black magic coded ;)
I'm not sure if Picasso also manages EXIF, but there is a quick intro to both of them:
https://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en
My Advice: do not waste your time and use them. You can solve your problem in one line:
Glide.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

Categories

Resources