I was reading an article about how to load bitmaps efficiently here. it had suggested using some techniques to load bitmap with a size that is needed not the real size. the only thing is that I didn't get what inSampleSize variable does(which must be a power of 2). if I choose number 1 for that, does it mean that this would be like if i normally loaded a bitmap with its real size?
Rajesh has quoted the explanation from the documentation of what inSampleSize does; that explanation can be expanded on with diagrams.
The important part is:
The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded bitmap.
So, if we had this image (where each letter denotes a pixel):
AAAABBBB
AAAABBBB
AAAABBBB
AAAABBBB
CCCCDDDD
CCCCDDDD
CCCCDDDD
CCCCDDDD
And we set inSampleSize = 2, we would get a decoded bitmap that looks like this:
AABB
AABB
CCDD
CCDD
That is, 2 pixels in the original image (AA) correspond to 1 pixel (A) in the decoded image.
If we set inSampleSize = 4, we would get a decoded bitmap that looks like this:
AB
CD
That is, 4 pixels in the original image correspond to 1 pixel in the decoded image.
Notice than an inSampleSize of 2 effectively halves the vertical and horizontal resolutions, but uses 1/4 of the pixels - and therefore only 1/4 of the memory.
Please read the documentation for inSampleSize
If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory. The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded bitmap. For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16 the number of pixels. Any value <= 1 is treated the same as 1. Note: the decoder uses a final value based on powers of 2, any other value will be rounded down to the nearest power of 2.
if I choose number 1 for that, does it mean that this would be like if i normally loaded a bitmap with its real size?
Yes, 1 denotes no subsampling.
Related
I'm downloading image data over the network and obviously it's compressed. Inside the app I need the data to be 100% accurate to the original. So for example for a 720x720 image I need a 518400 length int array or 2073600 length byte array where the values have not been modified from the original. Since PNG compression is lossless this should be possible.I noticed that BitmapFactory applies some psychovisual effects when decoding that aren't noticeable when looking at the image, however change the byte values ever so slightly (I'm assuming this is for speed and/or better visual look on screen).
What I'm currently doing is using BitmapFactory.decode and then iterating over every pixel to create a new array from approximation and creating a new bitmap with Bitmap.create This is possible because for now the images are binary and in the original images each pixel is either (r, g, b, 255) or (0, 0, 0, 0). I can find the most prevalent colour and set all pixels with alpha values over some threshold to this RGB value. The solution is quite slow though and seems needlessly complicated.
So my question is - is there any combination of options flags for BitmapFactory.decode that returns a 100% true to the original Bitmap or do I have to create a PNG decoder myself to achieve this?
Have a try with this method:
val opts = BitmapFactory.Options()
opts.inPremultiplied = false
var bitmapDecode = BitmapFactory.decodeByteArray(decode, 0, decode.size, opts)
On API 27, I tried to extract frames from selected video with getScaledFrameAtTime(), one in a series of threads/runnables. Here is the code snippet (in one thread per timestamp):
mediaMetadataRetriever.setDataSource(uri);
bitmap = mediaMetadataRetriever.getScaledFrameAtTime(timeStamp * 1000,
MediaMetadataRetriever.OPTION_CLOSEST,
224,
224);
But sometimes I got bitmaps with width of only 126.
What happened? How to prevent and deal with it?
This method finds a representative frame close to the given time position by considering the given option if possible, and returns it as a bitmap with same aspect ratio as the source while scaling it so that it fits into the desired size of dst_width by dst_height.
https://developer.android.com/reference/android/media/MediaMetadataRetriever#getScaledFrameAtTime(long,%20int,%20int,%20int)
It tries to maintain the original's aspect ratio, using dst_width and dst_height as maximums.
I'm trying to resize a bitmap using inDensity and inTargetDensity following #colt-mcanlis' instructions explained at 1, 2 and 3.
So far so good, good documentation, great video. The problem is that the resulting sizes for the image makes no sense to me.
For example if I use following values:
srcWidth is 11774px and srcHeight is 6340px
dstWidth is 1440px and dstHeight is 2392px
The code I'm using is:
options.inScaled = true;
options.inSampleSize = 8;
options.inDensity = srcWidth;
options.inTargetDensity = dstWidth * 8;
options.inSampleSize;
imageBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.image, options);
And the resulting image has width 70px and height 38px, instead 1440x2393.
I tried without using inSampleSize, and I get a very similar result. Then I assume the problem is with inTargetDensity and inDensity.
I went to the documentation and found the following:
inDensity
int inDensity
The pixel density to use for the bitmap...
As far as I know, to calculate a density I need a width, height and a display size but a display size doesn't make sense to me in this context, since I just want to calculate inDensity and inPixelDensity independent of a display size.
So, what am I doing wrong here ?
I was following Loading Large Bitmaps Efficiently by the book, but was running into the problem that the decoded bitmap ended up having way larger dimensions even than the original image (options.outWidth / options.outHeight).
I noticed that after the "decode bounds" step, inTargetDensity had a larger value than inDensity, and ultimately found that to be the cause of the larger decoded bitmap. Not sure exactly when playing with anything different on this fields would be useful...
But setting options.inTargetDensity = options.inDensity after the "decode bounds" step, worked for having the bitmap be decoded at the expected size (according to the inSampleSize you calculate).
Looking forward to the "more straightforward" API that Romain Guy announced in Google I/O (2018) :D
If you just want to resize an image while decoding, inSampleSize option is enough, but, because the aspect ratio of original and target images are not the same, you can't get the expected result through inSampleSize option directly, you need to do some extra crop operations after resizing. You can refer to the following guide for details:
http://developer.sonymobile.com/2011/06/27/how-to-scale-images-for-your-android-application/
I have the following code to create a canvas with a size of 8303 × 5540, but running that code produces a OutOfMemoryException.
scaledBitmap = Bitmap.createBitmap(8303, 5540, Bitmap.Config.ARGB_8888);
How can I resolve this problem?
Setting android:largeHeap="true" in AndroidManifest.xml helped me.
Well.. Creating a bitmap of that size, you would have to allocate about 183MB of memory. That will be a problem on most phones. You could try to set android:largeHeap="true" in your manifest, but still that will not give you enough memory on most phones.
If you are willing to accept a "subsampled" version of your image, and the image data is coming from file, you could take a look at http://developer.android.com/training/displaying-bitmaps/load-bitmap.html for loading subsamples of large images into memory. Basically, you can tell the BitmapFactory to load one out of every X pixels, thereby avoiding the requirement to have all 183MB of image data in memory.
http://codingaffairs.blogspot.com/2016/07/processing-bitmap-and-memory-management.html
Now here are tips which you can follow and can avoid out of memory exception in your Android Application.
Always use inSampleSize
Now what is inSampleSize ?
with the help of inSampleSize you are actually telling the decoder not to grab every pixel in memory, instead sub sample image.
This will cause less number of pixels to be loaded in memory than the original image. you can tell decoder to grab every 4th pixel or every second pixel from original image.
if inSampleSize is 4. decoder will return an Image that is 1/16 the number of pixels in original image.
so how much memory you have saved ? calculate :)
Read Bitmap Dimensions before loading into memory.
How reading bitmap dimensions before loading image into memory can help you avoid out of
memory error ? Let's Learn
use inJustBounds = true
here is technique with the help of which you can get image dimension beore loading it in memory
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
Above code snippet will not give us any image/bitmap. it will return null for bitmap Object.
but it will definitely return width and height of that image. which is R.id.myimage.
Now you have width and height of Image. you can scale up or scale down image based on these factors:
ImageView size which will be used to display Image.
Available amount of memory. you can check available amount of memory using ActivityManager and getMemoryClass.
Screen size and density of device.
Use appropriate Bitmap Configuration
Bitmap configurations is color space/color depth of an Image. Default bitmap Configuration in Android is RGB_8888 which is 4 bytes per pixel.
If you use RGB_565 color channel which use 2 Bytes per pixel. half the memory allocation for same resolution :)
Use inBitmap property for recycling purpose.
Do not make static Drawable Object as it cannot be garbage collected.
Request large heap in in manifest file.
Use multiple processes if you are doing lot of image processing(memory intensive task) or use NDK (Native Development using c, c++)
I'm making an Android app and I need to load an image (bitmap) in a cavas and resize it using the "pinch zoom" gesture. When the image is over a certain size, however, the application crashes (OutOfMemory exception). How do I optimize the loading and manipulation of the image?
To load the image I use:
BitmapFactory.decodeResource (ctx.getResources (), R.drawable.image)
To draw it:
imgCanvas.drawBitmap (image, posX, posY, null),
To change its size:
Bitmap.createScaledBitmap (originalBitmap, neww, NEWH, true);
This is not trivial.
Based on the current scale of the image and the currently visible part of the image, only load a part of that image at the appropriate resolution:
https://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html
When zoomed-out and you want to show the entire image scaled down, use the methods from this BitmapRegionDecoder class that take a BitmapFactory.Options parameter and set it inSampleSize to a value larger than 1 (preferably a value that is a power of 2):
https://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize
When zooming in, first zoom in the lower resolution that is already shown (where you used a value of inSampleSize > 1) and lazily load a higher resolution version (where inSampleSize is smaller than the previous value you used) using the BitmapRegionDecoder and fade in the higher resolution version gradually.
When the user zooms in, keep doing this until your inSampleSize is 1.