I have an 8MB png and when I try to load it into an Android ImageView I get an OutOfMemoryError that says it tried to allocate 32MB of memory and failed.
I'm working on changing the code to downsample the image to avoid using too much memory to avoid most of these problems, so I'm not looking for answers about downsampling. I'm trying to understand why the memory needs of the image are higher than the file size would imply.
Why is Android trying to allocate 4x the memory when loading the png?
I've set my options to tell it not to scale for pixel density:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inScaled = false;
It is 2848 x 4209 pixels
At the 4 bytes/pixel rate for ARGB_8888, that will be 47,948,928 bytes as a Bitmap.
The on-disk size of images represents a red herring. The major file formats (particularly PNG and JPEG) are compressed as files. That does not matter. What matters is the resolution and bit depth of the desired decoded image.
Also note that your image is bigger than the display resolution of most Android devices. Depending on your use case, you may wish to consider widgets like this one that can load and display portions of the image at a time.
Related
Searched and only find solutions for scale the bitmap to reduce its dimensions and size. But I am looking for a way to reduce the bitmap's memory byte count and don't change its dimension.
The images are from remote sources, they are not in our control. , after save the images to device locally, they will be shown as thumbnail later. Noticed it frequently throws OOM exception. The first step was to scale down the bitmap to a smaller dimensions when loading from the file (such as 500x500, or 300x300), here has some suggestions, which helps a little bit.
A closer look find the scale downed bitmap may still have large byte count (from a few hundreds k to over one meg).
Since for this case the bitmap with lower resolution in memory should be ok, so is there a way it can keep the bitmap's dimensions but reduce it memory byte count?
Thanks!
You can read it in as RGB_565 instead of ARGB_8888, to reduce the bit depth from 4 bytes/pixel to 2 bytes/pixel. However, that's all you can do, as the memory footprint of a Bitmap is the number of pixels times the bit depth per pixel.
I'm trying to lower the memory usage of my app, and I noticed most of it is caused by the activities' backgrounds: in XHDPI, 720*1280*4 = 3.6Mbytes each, and Android doesn't seem to release them as soon as they're not in use anymore.
Is there any way to tell Android to load certain resource images as RGB 565 instead of RGB 8888 in order to save some memory on those larger images without alpha channel ?
I know this can be done programmatically, but I was wondering if this can also be done with images and backgrounds used in the activities' layouts or with setBackgroundResource().
You can do it like this:
protected void onCreate(Bundle savedInstanceState)
{
setContentView(R.layout.high_scores);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inPreferredConfig = Config.RGB_565;
BitmapDrawable highScoresBg = BitmapFactory.decodeResource(getResources(), R.drawable.achievements, opts);
findViewById(R.id.high_scores_root).setBackgroundDrawable(highScoresBg);
Pixels of your image will be stored on 2 bytes, instead of 4 when using ARGB_8888. 50% saved memory, but of course less quality.
This article tells us that on Android < 2.3 the images are in RGB_565 by default. However, if the image has alpha channel (PNG for instance), it will be loaded in ARGB_8888. On Android >= 2.3 all the images are loaded in ARGB_8888 by default.
There's currently no way to flag a resource to be RGB_565.
The best you could do, would be to provide a subsequent data file which maps your image files to desired in-memory formats. At load time, you can check each resource ID against the file to determine if you'd like to set the BitmapFactory options to 565 or 8888.
A similar process is used by most applications that pull down image data. For example, some JPG images which have a small resolution, are assumed to be thumbnails, and loaded into memory using the 565 path. Where full-screen JPGs are allowed to load into memory as 8888.
Basically, you'll always need some level of logic in your app to set the BitmapOptions accordingly.
Can someone suggest me a library that can do simplest operations like scale, crop, rotate without loading image fully into memory?
The situation: I need to scale image down from a very large size, but the scaled down image is still too large to be allocated in memory (if we use standard android tools). Since I only need to upload scaled down version, I thought of scaling it through native library and upload it through FileInputStream.
I've tried to use ImageMagic and it does the job, but performance is very poor (maybe there is a way to speed things up?)
Might want to check out OpenCV for Android
You can use the original Android Bitmap functionality by pulling the image into memory but allowing Android to sample the image before it is loaded.
For example:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap myBitmap = BitmapFactory.decodeStream(inputstream,null,options);
This will load your bitmap into memory with half the memory footprint of the full image. You can experiment with changing the inSampleSize to get a good fit for your application.
You can also calculate the sample size on the fly, if you know the final image size you are aiming for, you can get the current file size of the image before you load it into memory and calculate the sample size using the equation inSampleSize = OriginalSize/RequiredSize. Though sample size is best used when it is a power of 2, so you can make adjustments for this.
Edit:
A great example here https://stackoverflow.com/a/823966/637545
I would be very grateful if someone could confirm that I have solved the below problem correctly or if there is an alternative solution?
I have an app that loads a large image (e.g. 800*1720 pixels) into memory and displays it in a scrollview. The image is a floor plans for a museum and I wanted a simple map like experience of scrolling and zooming. The image loaded fine on older devices, but caused an out of memory error on a Samsung Galaxy S3.
Looking at the LogCat messages it turned out that in creaing the bitmap 22MB was being allocated for the bitmap instead of 800*1720*4 = 5.5MB. Essentially 4x as much memory was being allocated as required by other devices and pushing the memory usage over the 50MB heap size.
The recommended solution to this problem is to use the BitmapFactory.Options.inSampleSize option to reduce the resolution of the image loaded and have it require less memory. However, this reduces the quality of the image, and I actually want to display it at it's full size in the original quality as works fine on older devices.
After much head scratching I concluded that the issue was that the pixel density on the S3's WXGA screens is 2.0 and thus for each pixel in the image, the bitmap was actually allocating 4 pixels. With a bit of trial and error I discovered I could prevent this happening by setting options.inScaled = false;
http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inScaled
On the way, I also realised that I could cut my memory usage in half to 2.7MB by using a lower fidelity colour depth of 2 pixels instead of 4 pixels by setting options.inPreferredConfig = Bitmap.Config.RGB_565;. For my floorpans this didn't effect the visible image quality.
The final code was thus:
String uri = "drawable/floorplan";
int imageResource = getResources().getIdentifier(uri, null, getPackageName());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inScaled = false;
Bitmap bm = BitmapFactory.decodeResource(getResources(), imageResource, options);
IVfloorplan.setImageBitmap(bm);
When displaying this bitmap you need to scale it back up. To work out the scaling you can obtain the pixel density from:
float density = getResources().getDisplayMetrics().density;
I reduced memory usage for the bitmap from 22MB to 2.7MB, which in a 50MB heap is significant.
The screen of the S3 has a really high res, so it's quite understandable. Also, if the image is in the drawable folder, it could be getting upscaled automatically. There might be ways to turn that off. Even if your image size doesn't chance, the OS has to allocate a buffer to accommodate the image, also possibly one to accommodate showing it on the screen.
An alternative is using tiling, which is used in games like SNES games. This is also how they handled lots of graphics without running out of RAM. Evidence shows that this is how Google Maps has a map of Planet Earth. You basically chop up the large image into tiles and show the tiles on the screen as you are panned to them (of course, maybe 1 extra tile on each side).
Even though this is post-Honeycomb, where Google put in code to better manage Bitmap allocations, be careful with Bitmap allocation. It's more like a C program than a Java program, and it's a good idea to manage it like one. It's very easy to run out of heap space when using Bitmap objects in Android
Getting this
05-25 23:55:59.145: ERROR/AndroidRuntime(3257): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at this code of mine -
Bitmap cs = null;
cs = Bitmap.createBitmap(frameImg.getWidth(), frameImg.getHeight(), Bitmap.Config.ARGB_8888);
in activity class. How to fix this :(
I ran into a similar problem when selecting images from my EVO ... off the SD Card. The camera saves those images at over 1MB a piece, and in some cases close to 3 MB. Ironically, the size of the bitmap the Intent sends to you when "taking a picture" is only about 40 Kb.
The solution I came up with was:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2; // this will cut the sampling by 50%
Bitmap bitmap = BitmapFactory.decodeFile( imageFilePath, options );
I was able to take pictures and cut them down to less than 100 Kb by increasing the factor number, and the images were still pretty good quality.
The bottom line here is it prevented OOME errors, and thus prevent me from crushing the JVM by exceeding the heap allocation. Simple - effective.
You might find this an interesting read also: BitmapFactory OOM driving me nuts
Make the bitmap smaller in resolution and potentially use a different file format..
If the image is really big then you should just display the parts you need.
Android has a relatively limited amount of spare of memory for bitmaps (which can get smaller depending on what else is going on in the device). So the other answers are correct...
scale the bitmap down (in pixels or resolution)
only display the subset of teh bitmap that is visible to the user.
Check out Displaying a bitmap of arbitrary size without running out of memory for background on what's going on under the covers - which might help you avoid the OOM.
hi #user418366 i was having the same problem when dealing with images what you can do is if you are using the emulator you can rise the max VM application Heap size so that you can get override of this problem or else as told by all try to compress the size of the image make sure that it was a loss less compression