Whatsapp like Image Compression in Android with demo

The images captured by the recent cameras often exceed 2 MB. Using such images frequently results in OutOfMemoryError. Also, due to landscape/portrait orientation, the images often are displayed as rotated. To deal with these issues we need to compress the image and give proper rotation before loading it to memory. The following method compresses image (similar to whatsapp), while maintaining its aspect ratio and also preventing significant loss to its quality.

A demo project implementing this compression can be downloaded from here.

	public String compressImage(String imageUri) {

		String filePath = getRealPathFromURI(imageUri);
		Bitmap scaledBitmap = null;

		BitmapFactory.Options options = new BitmapFactory.Options();

//		by setting this field as true, the actual bitmap pixels are not loaded in the memory. Just the bounds are loaded. If
//		you try the use the bitmap here, you will get null.
		options.inJustDecodeBounds = true;
		Bitmap bmp = BitmapFactory.decodeFile(filePath, options);

		int actualHeight = options.outHeight;
		int actualWidth = options.outWidth;

//		max Height and width values of the compressed image is taken as 816x612

		float maxHeight = 816.0f;
		float maxWidth = 612.0f;
		float imgRatio = actualWidth / actualHeight;
		float maxRatio = maxWidth / maxHeight;

//		width and height values are set maintaining the aspect ratio of the image

		if (actualHeight > maxHeight || actualWidth > maxWidth) {
			if (imgRatio < maxRatio) { 				imgRatio = maxHeight / actualHeight; 				actualWidth = (int) (imgRatio * actualWidth); 				actualHeight = (int) maxHeight; 			} else if (imgRatio > maxRatio) {
				imgRatio = maxWidth / actualWidth;
				actualHeight = (int) (imgRatio * actualHeight);
				actualWidth = (int) maxWidth;
			} else {
				actualHeight = (int) maxHeight;
				actualWidth = (int) maxWidth;

			}
		}

//		setting inSampleSize value allows to load a scaled down version of the original image

		options.inSampleSize = calculateInSampleSize(options, actualWidth, actualHeight);

//		inJustDecodeBounds set to false to load the actual bitmap
		options.inJustDecodeBounds = false;

//		this options allow android to claim the bitmap memory if it runs low on memory
		options.inPurgeable = true;
		options.inInputShareable = true;
		options.inTempStorage = new byte[16 * 1024];

		try {
//			load the bitmap from its path
			bmp = BitmapFactory.decodeFile(filePath, options);
		} catch (OutOfMemoryError exception) {
			exception.printStackTrace();

		}
		try {
			scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight,Bitmap.Config.ARGB_8888);
		} catch (OutOfMemoryError exception) {
			exception.printStackTrace();
		}

		float ratioX = actualWidth / (float) options.outWidth;
		float ratioY = actualHeight / (float) options.outHeight;
		float middleX = actualWidth / 2.0f;
		float middleY = actualHeight / 2.0f;

		Matrix scaleMatrix = new Matrix();
		scaleMatrix.setScale(ratioX, ratioY, middleX, middleY);

		Canvas canvas = new Canvas(scaledBitmap);
		canvas.setMatrix(scaleMatrix);
		canvas.drawBitmap(bmp, middleX - bmp.getWidth() / 2, middleY - bmp.getHeight() / 2, new Paint(Paint.FILTER_BITMAP_FLAG));

//		check the rotation of the image and display it properly
		ExifInterface exif;
		try {
			exif = new ExifInterface(filePath);

			int orientation = exif.getAttributeInt(
					ExifInterface.TAG_ORIENTATION, 0);
			Log.d("EXIF", "Exif: " + orientation);
			Matrix matrix = new Matrix();
			if (orientation == 6) {
				matrix.postRotate(90);
				Log.d("EXIF", "Exif: " + orientation);
			} else if (orientation == 3) {
				matrix.postRotate(180);
				Log.d("EXIF", "Exif: " + orientation);
			} else if (orientation == 8) {
				matrix.postRotate(270);
				Log.d("EXIF", "Exif: " + orientation);
			}
			scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0,
					scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix,
					true);
		} catch (IOException e) {
			e.printStackTrace();
		}

		FileOutputStream out = null;
		String filename = getFilename();
		try {
			out = new FileOutputStream(filename);

//			write the compressed bitmap at the destination specified by filename.
			scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		return filename;

	}

The method getFilename():: It creates a folder in the SDCard used to store the images.

	public String getFilename() {
		File file = new File(Environment.getExternalStorageDirectory().getPath(), "MyFolder/Images");
		if (!file.exists()) {
			file.mkdirs();
		}
		String uriSting = (file.getAbsolutePath() + "/" + System.currentTimeMillis() + ".jpg");
		return uriSting;

	}

The method getRealPathFromURI(imageUri):: Gives the actual filepath of the image from its contentUri::

private String getRealPathFromURI(String contentURI) {
		Uri contentUri = Uri.parse(contentURI);
		Cursor cursor = getContentResolver().query(contentUri, null, null, null, null);
		if (cursor == null) {
			return contentUri.getPath();
		} else {
			cursor.moveToFirst();
			int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
			return cursor.getString(index);
		}
	}

The method calculateInSampleSize:: calculates a proper value for inSampleSize based on the actual and required dimensions:

	public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
		final int height = options.outHeight;
		final int width = options.outWidth;
		int inSampleSize = 1;

		if (height > reqHeight || width > reqWidth) {
			final int heightRatio = Math.round((float) height/ (float) reqHeight);
			final int widthRatio = Math.round((float) width / (float) reqWidth);
			inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; 		} 		final float totalPixels = width * height; 		final float totalReqPixelsCap = reqWidth * reqHeight * 2; 		while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
			inSampleSize++;
		}

		return inSampleSize;
	}
About This Author

Ambalika, Currently working as a software developer is a B.Tech in Computer Science.

  • Rakesh Rocky

    thanks Ambalika Saha. Very useful one. Can u please tell me the exact ratio or pixel of compression, i mean the rate of compression. thanks again. mail me at rakeshsundar@rocketmail.com

    • ambalika saha

      In this approach, the max height and width of the output image is maintained at around 800px and 600px respectively [float maxHeight = 816.0f; float maxWidth = 612.0f;] The final height and width are decided depending upon the aspect ratio(width:height) of the image. The extent of compression required to achieve this ia calculated by the method “calculateInSampleSize”. The method returns an integer value “inSampleSize”. This “inSampleSize” decides the extent of compression. For example if inSampleSize=2, the image will be compressed to half; inSampleSize = 4 means the image the will be compressed to one-fourth. So when we are loading the image from the path, it loads a scaled down version.

    • Rakesh Rocky

      wow! thanks. this will be very helpful in my project.

    • Rakesh Rocky

      by the way, whats ur opinion about this code, developed by me:

      Bitmap bitmap = ((BitmapDrawable)myicon).getBitmap();
      int h=bitmap.getHeight(); //can be of float type too
      int w=bitmap.getWidth();
      h=h/4; //depends on compression rate/file size/pixels (dynamic)
      w=w/4; //like inSampleSize in ur code
      bitmap=Bitmap.createScaledBitmap(bitmap, w,h , true);

      in this case the image will be compressed to 1/4th. this code maintains aspect ratio and also prevents significant loss to quality.

    • ambalika saha

      Yeah!!! This approach too is right, infact lot simple, but it depends upon your requirements. “createScaledBitmap” is no doubt a wonder function but sometimes in applications that deals with hundreds of images, it may give that dreaded “outOfMemory” error. Also if you zoom an image created by “createdScaledBitmap” the distortion is awfully visible. So, it all depends upon your necessity and functionality.

    • Rakesh Rocky

      thank u. can u pls help me one more time. my project also requires video conferencing. how can i achieve video conferencing in android? Any advice will b very useful…

    • Anil Nayak

      Hello Ambalika,
      i have found some error when i was tried to run your code in android studio.I got some Asynctask error.Can you tell me is there anything which i have left .Thnks

    • kritika

      how can i do it with video

    • anand chavan

      Anand chavan

  • Raman Kumar Sharma

    Hello Ambalika,

    Thanks for a useful tutorial.

    Can you please tell me how to compress the video.

    mail me at sahil0shrma@gmail.com

  • Hemant Chand Dungriyal

    above method works fine except crop the height when image orientation is 270 and 90 degree. So is there any solution for prevent cropping image

    • Hemant Chand Dungriyal

      Hi Ambalika,
      issue on matrix. When orientation is 90 and 270 degree then it rotate the image with cut upper and lower section.

  • Venkat

    Very nice post, but i have one exception from getRealPathFromURI – it says Failed to read row 0, column -1 from a CursorWindow

  • Rudresh Agarwal

    Hello Ambika, I am a part of a startup organisation working on a product to help teachers communicate with students better via images, video and audio using mobile apps. We would require your help regarding compression technology. Please do reply soon.

  • Hrithik r

    In few devices when I take picture from camera actualHeight,actualHeight are being returned as null.
    int actualHeight = options.outHeight; // null
    int actualHeight = options.outWidth; // null

    Any work around for this ?

  • James

    good Jobs!! god bless u 🙂

  • sarath kumar

    Thanks @ambalikasaha:disqus Is it possible to compress video files using same technique without loss quality

  • Sanket Patel

    Thanks Ambika for such a useful post.

    I am using your code in my project. But i am getting a problem if i select 5 images from Gallery>Camera, and try to call your method compressImage() for those 5 image paths. OutOfMemoryError occurred at bellow code.

    try
    {
    scaledBitmap = Bitmap.createBitmap(actualWidth, actualHeight, Bitmap.Config.ARGB_8888);
    } catch (OutOfMemoryError exception) {
    exception.printStackTrace();

    }

    And because of this application doesn’t works properly. Could you please help me?

  • Pratham Bane

    Which video compression technique used by whatsapp,viber,hike,line.??

  • Pratham Bane

    Which Video compression technique used by whatsapp, line,viber,hike??

  • Tamanna Khan

    I downloaded your project but it is giving error at : Uri contentUri = Uri.parse(contentURI);

  • Tejas

    Hello Ambalika,

    You have not provided any comments for the Canvas object that is used. What does that part of code do ?

  • Sergii Z.

    it seems that better to use :
    float imgRatio = actualWidth / *(float)* actualHeight;

    otherwise you’ll get wrong result of division?

  • hon lung lhimark lau

    Is it possible to show the compressed images in Gallery?
    Great if you would share the methodology as well

  • Stefano Occhi

    It’s a fantastic sample! Thanks! Can we “return” to the original full size image?

  • rudresh sp

    hello Ambika, i want to re size an image using fingers onTouch, i mean zoom in and out and re size the image ,also set max zoom in , zoom out value..but image not supposed to move
    pls help me rudresh.soft@gmail.com,

  • Neeraj Singh

    Best Thanks Its Work for me as i aspect.

  • Shivam Pasricha

    Thanks it was very helpfull example alot thanks

  • Nsereko Louis

    i have tried to change the bitmap to a base64 string and send it to the server as a string. but on decoding it fails, why could this be

  • Shrey Promact

    This is really good work on Image compression. I really appreciate it. I have one suggestion. I also need video compression as whats app do. Please provide me any link or guide for video compression if you have.

    Thanks a lot. 🙂

  • Abhishek Aggarwal

    Hi Ambalika.. thanks for the code. could you please guide me as to how I can decrease the level of compression in this and still ensure that the result is below 500kb

  • mohammed shaiban

    Hey, First of all thanks for the detailed post.

    I am facing an issue after storing an compressed image in new location, when i send that new image (placed at new location )by converting it to bitmap to server, it takes original image size ( before compress, instead of compressed one ). when i see in galary it will show the reduced size.

    why it take original size instead of compressed image size ?

  • Ajay Gadadasu

    Hi Ambalika.,
    its working fine in samsung devices…for other devices actual with n height it is showing as “0”.., please tell me solution

  • Monu Kumar

    hii Ambika saha you give a good tutorial for image compression but i have an error that “could not open the database” can you please tell me about this
    if you give me suggestion please send on my gmail that is monukumar011989@gmail.com

  • Percy Mugadza

    this is a highly functional compression method managed to compress my camera images from 3.24 mb to 36kb. This is awesome work awesome man. keep the up the good work.

    • Fady Zarif

      can you help me please ?!

    • Percy Mugadza

      How can i help u sorry for the late reply

    • Fady Zarif

      Thank you for your Reply 🙂
      I want to compress image that iam get from gallery to push it with small size to Firebase server so i want Uri of copressed image to push itt how can i do this ?

    • Percy Mugadza

      The method returns a string which is the path to the compressed image

    • Fady Zarif

      hey
      where are you ?!

  • Fady Zarif

    Help please

    Unable to decode stream: java.io.FileNotFoundException: /external/images/media/239067: open failed: ENOENT (No such file or directory) when i do this i get this error ?!!

  • LeoWiki Android

    Thanks a lot Ambalika Saha.. Very-Very useful post. Do you have any idea about Video compression..?

  • pawan kevin

    can some one give me a demo project on this.

  • tiagotaraczuk

    Thanks! That code solved my problem.

  • Xentric Android

    getImageUri always stores the photo in phone,how can the compression be done without storing it in the device because for security reasons the pics cannot be shown in device gallery.

  • Swapnil Bhai

    Hey ambalika saha very usefull. Working properly for big images but it working opposite to smaller images ie. increasing image size than existing .
    for eg. if img is 91 kb after compressalgo it is 200kb.
    please help me.
    Thanks in advance.

  • Richa Sharma

    Hi Ambalika! Can u please suggest the way to compress video files(.mp4).

  • kritika

    please tell me how do i compress video when i select video from my phones gallery

    my mail is kritika_gusain@yahoo.co.in

  • Krishna Lohiya

    Hello Ambalika thanks alot for the code. It was so damn useful.
    Do you know how to do achieve it in swift for iOS?