Java Android - 缩放和压缩位图

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20599834/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 03:02:44  来源:igfitidea点击:

Android - Scale and compress a bitmap

javaandroidimageimage-processingbitmap

提问by Allan Jiang

I am working on an android app, which has camera capture and photo uploading feature. If the device has a high resolution camera, the captured image size will be really large (1~3MB or more).
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.

我正在开发一个 android 应用程序,它具有相机捕获和照片上传功能。如果设备有高分辨率的摄像头,捕获的图像大小会非常大(1~3MB 或更多)。
由于应用程序需要将此图像上传到服务器,因此我需要在上传前压缩图像。例如,如果相机拍摄了 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. 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. 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

  1. 它无法保持比例,代码强制将图像调整为 480x800。如果用户以其他比例捕获图像,则压缩后的图像将不好看。
  2. 它不能很好地运作。无论原始文件大小如何,代码都会始终将图像大小更改为 7990272byte。如果原始图像尺寸已经很小,它会变大(我的测试结果是给我的墙拍照,它几乎是单色的):

    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?

我在问是否有更好的方法来压缩照片以便它可以顺利上传?

采纳答案by Sz.

  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 rawsize of that 480x800 bitmap, not the file size, which should be a JPEG in your case. How are you going about saving that bitmap, BTW? Your code doesn't seem to contain the saving part.

    See this question herefor 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);
    
  1. 您需要决定宽度或高度的限制(显然不是两者)。然后用计算的图像大小替换这些固定的图像大小,例如:

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

    (根据需要为横向/纵向添加检查和计算替代方案。)

  2. 正如@harism 还评论的那样:您提到的大尺寸是该480x800 位图的原始尺寸,而不是文件大小,在您的情况下应该是 JPEG。你打算如何保存那个位图,顺便说一句?您的代码似乎不包含保存部分。

    在此处查看此问题以获取帮助,关键是:

    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);
    

回答by Prashant Maheshwari Andro

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;
}