I already known that I can choose the image quality(compression quality? not sure) of JPEG from 0 to 100.
FileOutputStream fos = new FileOutputStream(saveFile, true);
saveBitmap.compress (android.graphics.Bitmap.CompressFormat.PNG, 100, fos);
but I don't know what is the default image quality(compression quality? not sure) of JPEG? Is that 100?
Thank you.
"Quality" is not part of JPEG. The quality after JPEG compression is determined by the quantization tables selected and sampling used. Some encoders use a "quality" setting to simplify that. However, there is no guarentee that one encoder's quality "X" is equivalent to another encoder's quality "X".
Related
how to compress GIF image before upload it to the server ?, i tried some android libraries to compress gif image but it convert it to png image. is there any way to compress gif image ?
im using a bitmap in my projects.
Bitmap
Example code:
ImageView carView = (ImageView) v.findViewById(R.id.imagen_cr7);
byte[] decodedString = Base64.decode(picture, Base64.NO_WRAP);
InputStream input=new ByteArrayInputStream(decodedString);
Bitmap ext_pic = BitmapFactory.decodeStream(input);
carView.setImageBitmap(ext_pic);
GIF is a lossless image compression format: it is set up to reproduce the image exactly.
As a consequence, there is no "image quality" slider (as in JPEG encoders); although a GIF will likely be much smaller than an uncompressed format (such as many camera RAW or common TIFF options), there is a limit to how far it can go.
Also, you should know that GIF is limited to 8 bits per pixel (so it is most appropriate for line art, not photo-like images). If your source image is a full 24 bits, it must be dithered to fit into a 256-entry color palette. So, although the GIF format itself is lossless, the image processing required to use it in the first place may be lossy.
There are a number of things you can do to reduce the size of your image file:
You can choose a lossy format (such as JPEG), which will allow much greater compression. Note that JPEG works well on photo-like images, but not so well on line art. Also, (although your question explicitly rejects it) PNG may be a reasonable option, as it (losslessly) supports 24-bpp images.
As mentioned in a comment, you can try reducing the resolution of your image, and shipping the reduced version. If you can't generate a smaller image to start with, image resizing typically works well on photo-like images, and there are nonlinear resizing filters available that are specifically intended to handle line art.
If a full-resolution GIF is mandatory for your application, you may be able to generate an image that is more compressible by the GIF format. GIF compresses solid blocks of color extremely well -- but does less well on dithered or noisy images (such as you might get from converting a 24-bpp image to GIF format).
Since you have not given any information on your requirements (what kind of image you have, where you got it, and what you need it for), it is hard to come up with specific advice.
However, there is a good chance that your GIF has far more resolution than you need for your particular application (leading to option #2).
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.
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.
I have a jpg that is taken using a camera intent, displayed to the user (with some size adjustments), and eventually saved as png.
I first read the file into a bitmap using
BitmapFactory.decodeFile(path);
I then reduce the image size (in pixels) using
Bitmap.createScaledBitmap()
and eventually I want to save the resized bitmap into a png, using
FileOutputStream fos = new FileOutputStream(pngFile);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
The problem is that the final png file should not exceed a certain size (in bytes). If it does, I would like to continue with resizing the bitmap until my file is small enough.
I can't check it before the png compression (while still a bitmap, using bitmap.getAllocationByteCount() - API 19 and up), because the bitmap is always larger than the png, (at least from what I've seen, including some basic tests I tried out in paint), and the png is the one I actually need at the end.
Of course, I can always save the png to the disk, read the length of the saved file, if it's too big read as bitmap, reduce again, save again, etc. But I prefer not to do that...
Is there any other way?
I am developing an image manipulation application which has to be able to work on large images e.g. those taken by a camera, the approach I'm taking is to split the source image into multiple suitably sized tiles so that the tiles can be loaded individually into memory as a Bitmap without exceeding the dreaded VM limit, next image manipulation is performed on a tile by tile basis, this is all well and good but until I want to stitch these tiles back to a final jpeg image.
I'm looking for a built in sdk api or free ware solution that can perform the following:
Open jpeg output file as output stream
Stream RGB pixels from bitmap (tile 1) to jpeg output stream
Stream RGB pixels from bitmap (tile 2) to jpeg output stream
etc.. for all tiles
Close jpeg output stream
Any ideas or pointers other than writing my own jpeg encoder?
What about this:
FileOutputStream outFile = new FileOutputStream(output_file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outFile);
outFile.flush();
outFile.close();
You can adjust the quality setting (100). See the docs
You'll still need to decide how to slice it. But if you're taking the image on the device, why would it be a problem loading the whole image if it didn't cause a memory problem in the first place?
Stitching Tiles back together: Here's one simple approach used by slippy maps the I know works.
Cut tiles in standard size (say 256x256)
Assign each tile an x,y value - this could be a directory structure or filename convention: i.e. "0_0.jpg"
Tile names (numbers) are related to their top/left pixel position.
Each tile number can be calculated by the following:
tileX = floor(pixelX / 256)
tileY = floor(pixelY / 256)
So the tile at pixels 0,0 = tile (0,0), the tile at pixels 256,0 is tile (1,0), etc...
Your stitching approach has two major issues and will ultimately fail. If you ask the system to decode a JPEG file in parts, the decode time will end up being close to N times longer (N = number of parts). The next problem is that when trying to save the image you will run out of memory. JPEG images need to be compressed in one shot. If your entire uncompressed image can't fit in memory, then you won't be able to use the technique you're using.
The hard truth is that, with Android as it is currently designed, you must use native code to hold on to the bitmap in order to manage an image larger than the VM memory limit. It's not that difficult, but it does require a re-design of your app.