简体   繁体   中英

Process to compress an image for uploading to a server in Android

I want to write my own code for compressing an image before I upload it to server.

I have seen many posts on SO like this and there are many other posts also but everywhere there are only code samples. It is not actually explained anywhere. I am not able to understand from the given code.

I just want to know about the overall approach to compress images so that I can start writing code by myself.

I am not looking for the code, only the steps that one person needs to follow to compress an image. (Like an algorithm is written before writing program ie, pseudocode)

I think Chetan Joshi's answer is the closest so far. But to explain a bit further,

"Compressing" an image and "scaling" (resizing) an image are different. Since you merely want to reduce the bandwidth (kb/Mb) over the network when you upload to the server, you are most likely talking about "scaling" since that will make the most difference to the size of the image.

Note, as in other answers Android, you will need to work with Bitmaps . You need to use the API calls associated with Bitmaps to do what you want.

Starting point:

If you allow your app to take a photo using the camera or pick from the gallery, you get an Uri which allows you to open an InputStream like this:

InputStream is = getContext().getContentResolver().openInputStream(photoUri);

That's the first step. The next part is the pseudocode where you want something like this:

  1. Get an InputStream of the photo/selected picture. This is just a stream of bytes that represent the image.
  2. Decode the stream into a Bitmap using BitmapFactory.decodeStream(InputStream src)
  3. Scale (resize) the bitmap using Bitmap.createScaledBitmap(src, dstWidth, dstHeight, filter); . You'll have to decide on a destination width and destination height you want yourself. See the next set of pseudocode.
  4. Compress the Bitmap using imageFormat.getBitmapCompressFormat(), 100, output) where output is an instance of ByteArrayOutputStream
  5. You can now call output.toByteArray(); to get the bytes.
  6. The bytes are what you want to send to the server.

Note about how to choose the destination width and height to scale to:

  1. Decide on a max height and max width, say 240 x 240. You will be comparing this with the actual height and actual width of the bitmap.
  2. If too high (actual height > max height), calculate a scaling factor that is max height / actual height
  3. If too wide (actual width > max width), calculate a scaling factor that is max width / actual width
  4. Your actual scaling factor is the minimum of these two scaling factors
  5. Return the actual height * scaling factor and the actual width * scaling factor

Altogether the high level process is something like this:

  1. Open a stream of the bytes of the image
  2. Convert the bytes into a bitmap
  3. Scale the bitmap
  4. Compress the bitmap
  5. Convert the bitmap back to bytes

Image Compression in Android means we are working with Bitmaps, while we are compressing any big image to lower then we have method compress in Bitmap class but if difference between size of original bitmap and wanted bitmap is too highr then bitmap quality always reduced .

To compress bitmap with high quality we have to use recursive process to compress any Bitmap rather compressing any bitmap directly from original height width to wanted height and width, we should compress image with 3 to 5 steps for example if our image size is 1000x1000 and wanted width and height are 300x300 then we applied recursive process to do this like below:

1000 x 1000

850 x 850

650 x 650

300 x 300 

In this way our Bitmap quality not reduced but make sure do not create new bitmap object each time use same bitmap object to assigned compressed bitmap otherwise you must face OutOfmemmory issue.

You can just use the android sdk - bitmap.compress();

ByteArrayOutputStream bos = new ByteArrayOutputStream();

if (myBitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos)) {
    //image is now compressed into the output stream
    uploadImage(bos);
} else {
    //compress failed
}

you can always check the android source code to learn more about it but if you are an android developer I think putting the time on learning how to compress shouldn't be your main focus specially when you have such an easy solution for it, if you still think you do I would've start by learning how does one compress files first, then I would move to images and only then I would've looked at the source code

Since you aren't actually looking for code, here is a fantastic resource for learning about the thought process behind image compression Data Compression Theory

There are a variety of algorithms that make use of different techniques to leverage on the different variables involved. Computational complexity, storage space, compression ration, and retaining image quality are all factors that these different algorithms (or file formats) seek to optimize, often favoring one attribute over the other. eg PNG vs JPEG for quality vs. space

Java provides ImageIO APIs which can help in compressing an image with your choice of quality and factor of compression. ImageWriter and ImageWriteParam provides the mentioned capabilities out of the box.

Are you planning to write your own API for some other reasons custom to your requirement ?

If not using java out of the box APIs , high level approach could be : 1) Read the image file. 2) using ImageIO , get the ImageWriter based on the image type like (jpeg,png etc.) 3) Get ImageWriteParam object from the writer and set compression params as per your requirement. 4) write the image.

Let me know if it doesnot work for you.

The code from your link describes not so much compression but rather sampling the image with a lower density and downscaling it. So it is not really compression but a reduction of quality (number of pixels and size) to make it smaller. Good compression algorithms will usually find a way to save the image in a way that uses less space without compromising the quality too much (with math and stuff).

crop the image and then send to server.Its working well,

add dependency in to gradle,

compile 'com.soundcloud.android:android-crop:1.0.1@aar'

gallery Intent,

private static final int PICK_FROM_FILE = 3;
private Uri mImageCaptureUri=null;
File sdCard = Environment.getExternalStorageDirectory();
String pathName;
File filepath;

Intent i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, PICK_FROM_FILE);

 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode != RESULT_OK)
        return;
     switch (requestCode) {
             case PICK_FROM_FILE:
               mImageCaptureUri = data.getData();
               // METHOD FOR CROP
               beginCrop(mImageCaptureUri);
               System.out.println("CHECK_GALLERY_path :" + mImageCaptureUri);
              break;
              case Crop.REQUEST_CROP:
              handleCrop(resultCode, data);
              break;
     }
  }

  private void beginCrop(Uri source) {
    Uri destination = Uri.fromFile(new File(getCacheDir(), "cropped"));
    Crop.of(source, destination).start(this);
 }

  private void handleCrop(int resultCode, Intent result) {

    if (resultCode == RESULT_OK) {

        mImageCaptureUri = Crop.getOutput(result);

        pathName = mImageCaptureUri.getPath();
        //uploadImageToServer();
        //  getImageUri(mImageCaptureUri);
                       Picasso.with(Activity_My_Account.this).load(mImageCaptureUri).into(getimage);          //IMAGE SAVE TO STORAGE
        getImageUri(mImageCaptureUri);
        System.out.println("path_name+++" + mImageCaptureUri);


    } else if (resultCode == Crop.RESULT_ERROR) {
        Toast.makeText(this, Crop.getError(result).getMessage(),        Toast.LENGTH_SHORT).show();
    }
}


    private void getImageUri(Uri mImageCaptureUri) {
     Bitmap photo = null;
     try {
        InputStream image_stream = getContentResolver().openInputStream(mImageCaptureUri);
        java.util.Date date = new java.util.Date();
         pathName = sdCard + "/" + "LEUF";
            File myDir = new File(pathName);
        System.out.println("GET_CHECK_PATH_NAME :" + pathName);
        myDir.mkdirs();
        Random generator = new Random();
        int n = 10000;
        n = generator.nextInt(n);
        String fname = "Image-" + n + ".jpg";
        File file = new File(myDir, fname);
        // if (file.exists()) file.delete();
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        Bitmap bitmapOrg = Bitmap.createScaledBitmap(BitmapFactory.decodeStream(image_stream), 350, 350, false);
        photo.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
         filepath=file;
         try {
            // file.createNewFile();
            FileOutputStream fo = new FileOutputStream(file);
            fo.write(bytes.toByteArray());
            fo.flush();
            fo.close();
        } catch (IOException e) {

            e.printStackTrace();
        }
      //here call to upload image to server
        public void UploadProfilePic(filepath){
        }

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM