Image Compression before uploading Bitmap.CompressFormat - android

I'm trying to compress images selected by user from gallery for uploading. I saw that my camera pictures are over 5MB and I would like to compress them(same as facebook if possible). What I've been trying:
I let the user select the photo from gallery,get the uri and use this:
File file = new File(getRealPathFromURI(getActivity(), selectedImageUri));
long length = file.length();
Log.e("Filesize:", "Before: " + length);
if (file.getName().toLowerCase().endsWith("jpg")||file.getName().toLowerCase().endsWith("jpeg")){
Bitmap original;
try {
original = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), selectedImageUri);
length = sizeOf(original);
Log.e("Filesize:", "BeforeCompression: " + length);
ByteArrayOutputStream out = new ByteArrayOutputStream();
original.compress(Bitmap.CompressFormat.JPEG, 50, out);
Bitmap decoded = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));
length = sizeOf(decoded);
Log.e("Filesize:", "AfterCompression: " + length);
} catch (IOException e) {
e.printStackTrace();
Log.e("Filesize:", "Error: " + e);
}
I did this to test if it was working first, but what I get in the console is:
/name.company.newapp E/Filesize:: Before: 4970874
/name.company.newapp E/Filesize:: BeforeConversion: 63489024
/name.company.newapp E/Filesize:: AfterConversion: 63489024
The size doesn't change at all. Is this the right approach ?

This happens because you're actually getting the size of memory used by the Bitmap object by calling sizeOf(bitmap) and not the actual file size.
As you should know, a bitmap operates with the number of pixels in an image. Even though you compress the image using a JPEG compression, the image's width and height do not change. Thus the number of pixels do not change and thus the Bitmap's size (in memory) would not change too.
However, if you save the compressed bitmap to a location in your hard disk and use File.length() to calculate the size of the compressed image, then you will notice the difference.

Please check the size before decoding and after compression:
length = sizeOf(original);
Also i would recommend you to flush and close the outputstream:
out.flush();
out.close();
Hope i could help!
Edit:
Please try the following method to decode your bitmap:
public static Bitmap decodeFile(File f,int WIDTH,int HEIGHT){
try {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
final int REQUIRED_WIDTH=WIDTH;
final int REQUIRED_HEIGHT=HEIGHT;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HEIGHT)
scale*=2;
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
You can change the width and height of your picture to make it smaller.

Related

How to write rotated bitmap into specific file without compress android

I want to rotate and save rotated bitmap to specific file path without compress it. Before i have used the below code for rotate, compress and store it to a specific file. Now i dnt want to compress my bitmap. Please suggest me an idea to rotate and save the bitmap into specified path.
public static void compressToStandard(String file) {
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file, bmOptions);
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = getInSampleSize(bmOptions);
try {
Bitmap bitmap = BitmapFactory.decodeFile(file, bmOptions);
bitmap = ExifUtils.rotateBitmap(file, bitmap);
Log.i("ImageUtils", "compressed bitmap size:" + bitmap.getWidth() + "x" + bitmap.getHeight());
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, new FileOutputStream(file));
} catch (Exception e) {
e.printStackTrace();
}
}
when i call this method. I will pass my image path to this method.
PNG is lossless so you can use Bitmap.CompressFormat.PNG
Compresion
Hint to the compressor, 0-100. 0 meaning compress for
small size, 100 meaning compress for max quality. Some
formats, like PNG which is lossless, will ignore the
quality setting

Bitmap size much greater than file size from which it's loaded

I'm using the following code to retrieve an bitmap from image file in android:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
However, the size of the bitmap is more than double the size of file. Eg, for a file of size 520kb, the bitmap size is around 1.3MB. Is there a way I can get the bitmap of the same size as that of file?
The bitmap size is pure memory data without compression. You can calculate the size with 4 bytes per pixel (with your settings).
Your file is probably in a compressed format like jpg. Without a compression it would take up the same space.
The reason why the bitmap is kept uncompressed in memory is basically the performance. You can read the data and work with it much faster in an uncompressed way. For example if you would like to see what color a specific pixel has, you would need to uncompress the data first. That takes time and the more pixels you check the more time it would take.
While reading the file from the storage, the uncompression just takes an ignorable amount of time compared to the complete reading process. So you will not have a big performance impact compared to "on demand" uncompression.
try like this may help you,
public static Bitmap decodeFile(File f,int WIDTH,int HIGHT){
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//The new size we want to scale to
final int REQUIRED_WIDTH=WIDTH;
final int REQUIRED_HIGHT=HIGHT;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT)
scale*=2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}

java.lang.OutOfMemoryError: (Heap Size=17863KB, Allocated=11272KB, Bitmap Size=12444KB)

I am using Base64 Encoded String to convert image and then create it at Windows Server.
It is working fine in most of devices but It is giving error java.lang.OutOfMemoryError in android Version 2.3.5. I tried android:largeHeap="true" but it din't work.
Android Code:
BitmapDrawable drawable = (BitmapDrawable) imageView.getDrawable();
Bitmap bitmap = drawable.getBitmap();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] data = baos.toByteArray();
strBase64 = Base64.encodeToString(data, Base64.DEFAULT);
I want to give crop image option to user and then store it at windows server. Is there any easy and better way for this ?
My code at asp.net:
public System.Drawing.Image Base64ToImage(string base64String)
{
byte[] imageBytes = Convert.FromBase64String(base64String);
using (var ms = new MemoryStream(imageBytes, 0, imageBytes.Length))
{
ms.Write(imageBytes, 0, imageBytes.Length);
System.Drawing.Image image = System.Drawing.Image.FromStream(ms, true);
return image;
}
}
System.Drawing.Image convertedImage = Base64ToImage(Photo);
convertedImage.Save(Server.MapPath("~\\images\\profileImg\\jeeten.jpg"), System.Drawing.Imaging.ImageFormat.Jpeg);
I tried some cropping image codes but It gave error : A generic error occurred in GDI+.
I would make these changes:
Instead of getting the image from the ImageView, consider using it's original source (file system? asset?). Then you dont have to re-compress the image.
Do not compress JPGs at 100% quality. There is large cost for un-noticeable image quality gains. If you need 100%, use PNG, otherwise use an 85% quality JPG.
You have several copies of the image in memory - in the drawable, in your byte array, in your base 64 string, etc. You can eliminate some of these.
Why convert to base 64? Just send the bytes to the server - Here's an example using PHP, but in .NET use HttpPostedFile to receive it.
Before Compressing the bitmap you can follow this:
//Put your image in file.
File file = new File("/mnt/sdcard/image.jpg");
//Pass your file in decodeImage method (your file, with its width and height as you want to display)
Bitmap bitmapImg=decodeImage(file,100,100);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmapImg.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArray = stream.toByteArray();
String encodedString = Base64.encodeToString(byteArray, Base64.DEFAULT);
//Body of decodeFile(File f,int WIDTH,int HIGHT)
public Bitmap decodeFile(File f,int WIDTH,int HIGHT)
{
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//The new size we want to scale to
final int REQUIRED_WIDTH=WIDTH;
final int REQUIRED_HIGHT=HIGHT;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT)
scale*=2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
b1= BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (Exception e) {}
return b1;
}

bmpFactoryOptions don't work, no idea why

I use the following code to get a Blob back from my SQLite database and get back a bitmap. My problem is that the reconstructed bitmap is larger than the original picture (input). It seems that my BitmapFactory.Options isn't working, but I have no idea what is wrong, nor am I getting an error. What is wrong with this code?
byte[] blob = contact.getMP();
ByteArrayInputStream inputStream = new ByteArrayInputStream(blob);
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
bmpFactoryOptions.inScaled = false;
bmpFactoryOptions.outHeight = 240;
bmpFactoryOptions.outWidth = 320;
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, bmpFactoryOptions);
try {
FileOutputStream out = new FileOutputStream("mnt/sdcard/test5.png"); bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
} catch (Exception e) {
e.printStackTrace();
}
When I check the output my 320 x 240 picture is 427 x 320. I don't want to use Bitmap.createScaledBitmap because it messes up the quality.
you have to pass to decodeStream in order to work
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, bmpFactoryOptions);

Bitmap Not Reading EXIF Data on Decode

My Problem
I have a series of Bitmaps that I would like to load up in the correct orientation.
When I save the image I go in and set the orientation attribute using the ExifInterface
ExifInterface exif = new ExifInterface(EXTERNAL_IMAGE_PATH+File.separator+this._currentPhotoName+JPEG_FILE_SUFFIX);
int rotation = CCDataUtils.exifToDegrees(exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL));
Log.v("PhotoManager", "Rotation:"+rotation);
if (rotation > 0) {
exif.setAttribute(ExifInterface.TAG_ORIENTATION,String.valueOf(0));
This works fine and if I was to pull this image off of my device it would be in the correct orientation. However, when I then decode my Bitmap later down the line it stays in the camera's default orientation of left-horizontal even if the image was taken in portrait?
My Question
How can I decode the bitmap and take into account its EXIF information?
I don't want to have to rotate the image after I decode it every time as I would have to create another Bitmap and that is memory I don't have.
Thanks in advance.
For those that are also stuck on this and have oom issues when manipulating multiple bitmaps here is my solution.
Do not change the exif data like I originally thought in the question - We need this later down the line.
When it comes to decoding the image to view, instead of decoding the full size image just decode the image scaled down to what you need. The following code example contains both the decoding of the bitmap to the device screen size and then it also handles the rotation of the bitmap for you.
public static Bitmap decodeFileForDisplay(File f){
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
DisplayMetrics metrics = MyApplication.getAppContext().getResources().getDisplayMetrics();
//The new size we want to scale to
//final int REQUIRED_SIZE=180;
int scaleW = o.outWidth / metrics.widthPixels;
int scaleH = o.outHeight / metrics.heightPixels;
int scale = Math.max(scaleW,scaleH);
//Log.d("CCBitmapUtils", "Scale Factor:"+scale);
//Find the correct scale value. It should be the power of 2.
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
Bitmap scaledPhoto = BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
try {
ExifInterface exif = new ExifInterface(f.getAbsolutePath());
int rotation = CCDataUtils.exifToDegrees(exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL));
if (rotation > 0)
scaledPhoto = CCBitmapUtils.convertBitmapToCorrectOrientation(scaledPhoto, rotation);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return scaledPhoto;
} catch (FileNotFoundException e) {}
return null;
}
public static Bitmap convertBitmapToCorrectOrientation(Bitmap photo,int rotation) {
int width = photo.getWidth();
int height = photo.getHeight();
Matrix matrix = new Matrix();
matrix.preRotate(rotation);
return Bitmap.createBitmap(photo, 0, 0, width, height, matrix, false);
}
So the image Bitmap thats returned after calling decodeFileForDisplay(File f); is in the correct orientation and the correct size for you screen saving you tons of memory problems.
I hope it helps someone

Categories

Resources