How to predict image size in megabytes after JPEG-compression - android

For example, we have file.jpg with size 5Mb. And our aim is to compress it to size 2Mb. There is souch methodin SDK: Bitmap.compress(Bitmap.CompressFormat format, int quality, OutputStream stream).
Is there any variant to calculate target quality value before compression?

You cannot predict the final output of JPEG compression.
The only option for you would be to perform some brute forcing, but in an optimized way.
Generally JPEG compresses the bitmap in the ratio 10:1.
So if your image resolution is 4096*4096, it requires 4096*4096*2 raw bytes (2 because RGB_565 is used), which equals 33554432 bytes = 32MB raw. Assuming 10:1 compression, It will take 3.2MB.
But it is greater than 2MB! So you will need to make the quality to 2/3.2=62.5℅. So first try with 62℅ as a quality parameter.
Now compress the image to a ByteArrayOutputStream and check the length of the byte array. If the length of the array is less than 2*1024*1024=2097152, it is okay. Otherwise you should try decreasing the quality eg by 10℅ to 52℅ and try again.

According to this, quality is a int that Hint to the compressor, 0-100. When 0 meaning compress for small size and 100 meaning compress for max quality. But some formats like PNG which is lossless, will ignore this quality setting. So, i think it's depend on your goal. If you want to get the maximum quality in minimum size then you need to resize & implement high number of quality.
Hope, it can help you.

Related

Why does Android BitmapFactory.decodeFile() want 4x the memory needed?

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.

Does BitmapFactory.decodeFile() file decompress the JPEG image when decoding it?

JPEG is a compressed format.
Does BitmapFactory.decodeFile("path to a JPEG file") decompress the JPEG image when decoding it as a Bitmap in the memory?
And when I use Bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) on the resulting bitmap from the decoded JPEG file, then the size of the compressed image is more than the size of the original image. Can anybody exactly explain this phenomenon?
Does BitmapFactory.decodeFile("path to a JPEG file") decompress the JPEG image when decoding it as a Bitmap in the memory?
Yes.
And when I use Bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) on the resulting bitmap from the decoded JPEG file, then the size of the compressed image is more than the size of the original image
That is very possible. With a quality level of 100, I would consider it likely, though perhaps not assured.
Can anybody exactly explain this phenomenon?
There is no requirement that they be the same for a JPEG. In fact, it will be almost random chance if they are the same.
Suppose we start with an in-memory image (Boriginal, where B is short for bitmap). We then compress that image to a JPEG (Joriginal). JPEG incorporates a lossy compression algorithm, to achieve better compression on real-world images (e.g., photos) by taking into account the fact that human eyes cannot discern small amounts of change.
Suppose we then decode Joriginal back into an in-memory bitmap (Breloaded). Breloaded will not be the same image as Boriginal, because the JPEG compression will have changed the image. How close Breloaded is to Boriginal will depend on a variety of factors, partly tied to the image itself, and partly tied to the quality level used when saving the JPEG (the 100 in your code). This quality level ranges from 0 to 100, with 100 meaning highest quality.
If we then compress Breloaded to a second JPEG (Jreloaded), the new JPEG will not be the same as the original JPEG (Joriginal). Partly, that is because the source bitmap changed, per the previous paragraph. Partly, that is because we might not choose the same quality level as we did with the original compression work.
In your case, you did not create Joriginal. You do not necessarily know what quality level was used (that information might be stored in the JPEG header; I forget). But because Breloaded will be different that the original bitmap (wherever it came from), when you compress the bitmap to Jreloaded, it is going to be different than Joriginal. On the whole, whether it is larger or smaller is difficult to say in the abstract. However, since you are choosing a quality level of 100, and Joriginal might well have been compressed with a lower quality level, your compressed image very easily could be larger.
This has nothing to do with Android. This is purely a function of how JPEG works. If you have further questions about JPEG itself, you may wish to read more about JPEG and ask questions on some site that has something to do with image formats.

how to reduce the bitmap memory usage byte count but keep its dimensions

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.

How android decodes the Resources

In the attempt of performing animation with large set of images, I tried with FrameAnimation but I found of outofmemory exception with large set of images, after some exploration for the solution I found one good solution here Pinhassi's solution.
This one helps me to achieve animation but not smoothly because it taking a time to load the images. Then I decided to decode bitmap before starting the animation (giving bitmap instead of resID to ImageView). Through this I got smooth animation. But it is taking a time to decode the bitmap from the resources.
Is there any better approach to do the same? Any suggestion would be appreciated.
Details of the image
Total Number of images =30;
Image Dimension =1000x 720;
Size =180kb
Devices on which I am performing
Manufacture: Motorola
Model: MZ601
Android Version: 3.0
To decode Bitmap
Bitmap mBitmap=BitmapFactory.decodeResource(getResources(),resID);
int bitmapSize= mBitmap.getRowBytes()*mBitmap.getHeight())
The approximate bitmapSize values is 3000000 (might be it is in bytes) converting it to kilobytes 23437.5kb
Here original png file is 180kb but after decoding it, taking nearly 2MB of data. Why?
Is I am doing anything wrong in analyzing the size of bitmap?
If yes, then how to get the size of decode bitmap?
If no, then any one explain why it is taking large space?
Note: I don’t want to scale the bitmap since it is perfect to my device screen dimension which is (1280 x 752).
Thanks
In advance for valuable suggestions and solutions.
Here original png file is 180kb but after decoding it, taking nearly
2MB of data. Why?
Assuming 1 byte per pixel with three color channels(RGB) Byte size = 3X(1000X720)/(1024*1024) = 2.05 MB approx.
There is a limit such processing can reach with plain bitmap loading, that's why Video codec's exist.

Resize image to exact file size

Actually i am developing one application, where my requirement is like i have to scale large size images to some predefined size as per user selection. eg,Suppose i have an image which is of 1Mb and user select it to convert it into an image of size 100kb then i have to resize that image to 100kb.
last one day i am trying to achevive this with Bitmap APIs but i am not able to achieve the exact size what user want. Sometimes its very large sometimes very small. So please if anyone knows how to resize image in android to exact size(which is changing as per user selection).
Please help me out in this.
If only the file size matters, you will want to use an image file format that does not use compression. You should be able to take an image with attributes such as width, height, bit-depth, and so on and calculate the expected file size for such an image.
So, using such a file format you start with your original image and you have
File size = width x height x bit depth + metadata/overhead.
I assume you want to maintain the original aspect ratio also. In that case, you can probably just figure out the % reduction in file size from the current file size, and multiply the width and height by the same %. Scale your image using a bitmap image manipulation API and then save it. It should be close to the file size you are looking for.
Specific to the Android Bitmap api you can use getByteCount() to determine how many bytes the image currently takes up. You can also use getConfig() to determine how many bytes per pixel.
So, your final goal file size converted to bytes divided by the number of bytes per pixel, gives you the number of pixels you are allowed. Number of pixels allowed divided by the number of pixels in your current image will give you the scaling factor. Use the scaling factor and scale the image keeping the current aspect ratio and you should have a bitmap with a number of pixels close to your goal. Then save in a file format that does not use compression.

Categories

Resources