简体   繁体   English

Android - 缩放和压缩位图

[英]Android - Scale and compress a bitmap

I am working on an android app, which has camera capture and photo uploading feature. 我正在开发一款Android应用程序,它具有摄像头捕捉和照片上传功能。 If the device has a high resolution camera, the captured image size will be really large (1~3MB or more). 如果设备具有高分辨率相机,则捕获的图像尺寸将非常大(1~3MB或更多)。
Since the app will need to upload this image to server, I will need to compress the image before uploading. 由于应用程序需要将此图像上传到服务器,因此我需要在上传之前压缩图像。 If the camera captured a 1920x1080 full-res photo for example, the ideal output is to keep a 16:9 ratio of the image, compress it to be a 640x360 image to reduce some image quality and make it a smaller size in bytes. 例如,如果相机捕获了1920x1080全分辨率照片,则理想输出是保持图像的16:9比例,将其压缩为640x360图像以降低某些图像质量并使其以字节为单位缩小。

Here is my code (referenced from google): 这是我的代码(从谷歌引用):

/**
 * this class provide methods that can help compress the image size.
 *
 */
public class ImageCompressHelper {

/**
 * Calcuate how much to compress the image
 * @param options
 * @param reqWidth
 * @param reqHeight
 * @return
 */
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {

    final int halfHeight = height / 2;
    final int halfWidth = width / 2;

    // Calculate the largest inSampleSize value that is a power of 2 and keeps both
    // height and width larger than the requested height and width.
    while ((halfHeight / inSampleSize) > reqHeight
            && (halfWidth / inSampleSize) > reqWidth) {
        inSampleSize *= 2;
    }
}

return inSampleSize;
}

/**
 * resize image to 480x800
 * @param filePath
 * @return
 */
public static Bitmap getSmallBitmap(String filePath) {

    File file = new File(filePath);
    long originalSize = file.length();

    MyLogger.Verbose("Original image size is: " + originalSize + " bytes.");

    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(filePath, options);

    // Calculate inSampleSize based on a preset ratio
    options.inSampleSize = calculateInSampleSize(options, 480, 800);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;

    Bitmap compressedImage = BitmapFactory.decodeFile(filePath, options);

    MyLogger.Verbose("Compressed image size is " + sizeOf(compressedImage) + " bytes");

    return compressedImage;
}

The problem with the above code is: 上面代码的问题是:

  1. It cannot keep the ratio, the code is forcing the image to resized to 480x800. 它无法保持比例,代码强制图像调整为480x800。 if user captured a image in another ratio, the image will not look good after compress. 如果用户以另一个比例捕获图像,压缩后图像看起来不会很好。
  2. It doesn't functioning well. 它运作不正常。 The code will always change the image size to 7990272byte no matter what the original file size is. 无论原始文件大小是多少,代码都会始终将图像大小更改为7990272byte。 If the original image size is pretty small already, it will make it big (my test result to take a picture of my wall, which is pretty much mono-colored): 如果原始图像尺寸已经非常小,它会变大(我的测试结果是拍摄我的墙,这是非常单色):

    Original image size is: 990092 bytes.
    Compressed image size is 7990272 bytes

I am asking if there's suggestion of a better way to compress photo so it can be uploaded smoothly? 我问是否有更好的方法压缩照片,以便可以顺利上传?

  1. You need to decide on a limit for either your width or height (not both, obviously). 您需要决定宽度或高度的限制(显然不是两者)。 Then replace those fixed image sizes with calculated ones, say: 然后用计算的图像替换那些固定的图像尺寸,例如:

     int targetWidth = 640; // your arbitrary fixed limit int targetHeight = (int) (originalHeight * targetWidth / (double) originalWidth); // casts to avoid truncating 

    (Add checks and calculation alternatives for landscape / portrait orientation, as needed.) (根据需要添加横向/纵向方向的检查和计算备选方案。)

  2. As @harism also commented: the large size you mentioned is the raw size of that 480x800 bitmap, not the file size, which should be a JPEG in your case. 正如@harism所评论的那样:你提到的大尺寸是480x800位图的原始尺寸,而不是文件大小,在你的情况下它应该是JPEG。 How are you going about saving that bitmap, BTW? 你打算如何保存这个位图,BTW? Your code doesn't seem to contain the saving part. 您的代码似乎不包含保存部分。

    See this question here for help on that, with the key being something like: 在这里看到这个问题的帮助,关键是:

     OutputStream imagefile = new FileOutputStream("/your/file/name.jpg"); // Write 'bitmap' to file using JPEG and 80% quality hint for JPEG: bitmap.compress(CompressFormat.JPEG, 80, imagefile); 

Firstly i check the size of image then i compress image according to size and get compressed bitmap then send that bitmap to server For Compressed bitmap call below funtion we have to pass image path in below funtion 首先我检查图像的大小然后我根据大小压缩图像并获得压缩位图然后将该位图发送到服务器对于压缩位图调用下面的功能我们必须在下面的功能中传递图像路径

public Bitmap get_Picture_bitmap(String imagePath) {

    long size_file = getFileSize(new File(imagePath));

    size_file = (size_file) / 1000;// in Kb now
    int ample_size = 1;

    if (size_file <= 250) {

        System.out.println("SSSSS1111= " + size_file);
        ample_size = 2;

    } else if (size_file > 251 && size_file < 1500) {

        System.out.println("SSSSS2222= " + size_file);
        ample_size = 4;

    } else if (size_file >= 1500 && size_file < 3000) {

        System.out.println("SSSSS3333= " + size_file);
        ample_size = 8;

    } else if (size_file >= 3000 && size_file <= 4500) {

        System.out.println("SSSSS4444= " + size_file);
        ample_size = 12;

    } else if (size_file >= 4500) {

        System.out.println("SSSSS4444= " + size_file);
        ample_size = 16;
    }

    Bitmap bitmap = null;

    BitmapFactory.Options bitoption = new BitmapFactory.Options();
    bitoption.inSampleSize = ample_size;

    Bitmap bitmapPhoto = BitmapFactory.decodeFile(imagePath, bitoption);

    ExifInterface exif = null;
    try {
        exif = new ExifInterface(imagePath);
    } catch (IOException e) {
        // Auto-generated catch block
        e.printStackTrace();
    }
    int orientation = exif
            .getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
    Matrix matrix = new Matrix();

    if ((orientation == 3)) {
        matrix.postRotate(180);
        bitmap = Bitmap.createBitmap(bitmapPhoto, 0, 0,
                bitmapPhoto.getWidth(), bitmapPhoto.getHeight(), matrix,
                true);

    } else if (orientation == 6) {
        matrix.postRotate(90);
        bitmap = Bitmap.createBitmap(bitmapPhoto, 0, 0,
                bitmapPhoto.getWidth(), bitmapPhoto.getHeight(), matrix,
                true);

    } else if (orientation == 8) {
        matrix.postRotate(270);
        bitmap = Bitmap.createBitmap(bitmapPhoto, 0, 0,
                bitmapPhoto.getWidth(), bitmapPhoto.getHeight(), matrix,
                true);

    } else {
        matrix.postRotate(0);
        bitmap = Bitmap.createBitmap(bitmapPhoto, 0, 0,
                bitmapPhoto.getWidth(), bitmapPhoto.getHeight(), matrix,
                true);

    }

    return bitmap;

}

getFileSize funtion for getting the size of image 获取图像大小的getFileSize函数

    public long getFileSize(final File file) {
    if (file == null || !file.exists())
        return 0;
    if (!file.isDirectory())
        return file.length();
    final List<File> dirs = new LinkedList<File>();
    dirs.add(file);
    long result = 0;
    while (!dirs.isEmpty()) {
        final File dir = dirs.remove(0);
        if (!dir.exists())
            continue;
        final File[] listFiles = dir.listFiles();
        if (listFiles == null || listFiles.length == 0)
            continue;
        for (final File child : listFiles) {
            result += child.length();
            if (child.isDirectory())
                dirs.add(child);
        }
    }

    return result;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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