Android如何将相机拍摄的照片文件大小减小到不超过500kb?

21

我的要求是将相机拍摄的图像上传到服务器,但它应该小于500 KB。如果大于500 KB,则需要将其缩小到大小小于500 KB (但尽可能靠近500 KB)

为此,我使用以下代码 -

@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        try {
            super.onActivityResult(requestCode, resultCode, data);
            if (resultCode == getActivity().RESULT_OK) {

                    if (requestCode == REQUEST_CODE_CAMERA) {

                        try {

                            photo = MediaStore.Images.Media.getBitmap(
                                    ctx.getContentResolver(), capturedImageUri);
                            String selectedImagePath = getRealPathFromURI(capturedImageUri);

                            img_file = new File(selectedImagePath);

                            Log.d("img_file_size", "file size in KBs (initially): " + (img_file.length()/1000));

                            if(CommonUtilities.isImageFileSizeGreaterThan500KB(img_file)) {
                                photo = CommonUtilities.getResizedBitmapLessThan500KB(photo, 500);
                            }
                            photo = CommonUtilities.getCorrectBitmap(photo, selectedImagePath);


//  // CALL THIS METHOD TO GET THE URI FROM THE BITMAP

                            img_file = new File(ctx.getCacheDir(), "image.jpg");
                            img_file.createNewFile();

//Convert bitmap to byte array
                            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
                            photo.compress(Bitmap.CompressFormat.JPEG, 100, bytes);

//write the bytes in file
                            FileOutputStream fo = new FileOutputStream(img_file);
                            fo.write(bytes.toByteArray());

// remember close de FileOutput
                            fo.close();
                            Log.d("img_file_size", "file size in KBs after image manipulations: " + (img_file.length()/1000));


                        } catch (Exception e) {
                            Logs.setLogException(class_name, "onActivityResult(), when captured from camera", e);
                        }


                    } 

            }
        } catch (Exception e) {
            Logs.setLogException(class_name, "onActivityResult()", e);
        } catch (OutOfMemoryError e) {
            Logs.setLogError(class_name, "onActivityResult()", e);

        }
    }

public static Bitmap getResizedBitmapLessThan500KB(Bitmap image, int maxSize) {
        int width = image.getWidth();
        int height = image.getHeight();



        float bitmapRatio = (float)width / (float) height;
        if (bitmapRatio > 0) {
            width = maxSize;
            height = (int) (width / bitmapRatio);
        } else {
            height = maxSize;
            width = (int) (height * bitmapRatio);
        }
        Bitmap reduced_bitmap = Bitmap.createScaledBitmap(image, width, height, true);
        if(sizeOf(reduced_bitmap) > (500 * 1000)) {
            return getResizedBitmap(reduced_bitmap, maxSize);
        } else {
            return reduced_bitmap;
        }
    }

如有需要,旋转图像。

public static Bitmap getCorrectBitmap(Bitmap bitmap, String filePath) {
        ExifInterface ei;
        Bitmap rotatedBitmap = bitmap;
        try {
            ei = new ExifInterface(filePath);

            int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_NORMAL);
            Matrix matrix = new Matrix();
            switch (orientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    matrix.postRotate(90);
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    matrix.postRotate(180);
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    matrix.postRotate(270);
                    break;
            }

            rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return rotatedBitmap;
    }

以下是图像文件大小最初和经过所有减小文件大小的操作后的输出结果。

img_file_size﹕文件大小(最初):3294 KB

img_file_size﹕文件大小(经过图像处理后):235 KB

如上所述,需要查看大小差异(在输出中)。未经这些操作的初始文件大小以及进行压缩和其他操作后的文件大小。我希望该大小能够接近500 KB。

对我来说,上述代码运行得还算不错,因为它可以减小图像文件大小使其小于500 KB。

但是,上述代码存在以下问题: -

  • 即使文件大小小于500 KB,此代码也会将其减小。

  • 如果文件大小大于500 KB,则减小后的文件大小与500 KB相比太少,尽管我希望它更接近。

我需要解决以上两个问题。因此,需要知道我应该在上述代码中进行哪些修改。

由于我还想纠正EXIF方向(旋转后的图像),因此需要进行修改。


显然,除了调整大小或更改质量(对于jpg),以及测试大小之外,对于png / jpg来说,如果不进行压缩,您将无法知道压缩数据的大小。 - Selvin
那么,它没有任何解决方案吗?@Selvin - Narendra Singh
暴力解决方案...调整大小或更改质量并检查大小...如果大小大于所需,则调整大小或质量,然后再次执行,直到获得正确的大小... - Selvin
请再次检查我的代码,我正在 isImageFileSizeGreaterThan500KB() 中执行它, 但是在这之后,我旋转图像以使其正确定位(这也是必要的,我不能跳过)。 可能这就是问题所在。 - Narendra Singh
我正在做这件事。不,你没有……我在你的代码中看不到任何循环……而且,不,我不会写任何代码……基本上,你可以知道未压缩的图像数据需要多少空间(简单地说是 H * W * perPixelDataSize(对于 ARGB_8888 是 4,对于 RGB_888 等是 3),但是你无法在压缩之前得知图像数据压缩后的大小(对于 png、jpg 编解码器)。 - Selvin
@Selvin请再次检查*isImageFileSizeGreaterThan500KB()*。虽然没有循环,但它是递归调用。 - Narendra Singh
8个回答

5

在调整大小之前,您可以检查大小。如果位图的大小大于500kb,则将其调整大小。

另外,为了使更大的位图接近500kb的大小,请检查大小差异并相应地压缩。

if(sizeOf(reduced_bitmap) > (500 * 1024)) {
    return getResizedBitmap(reduced_bitmap, maxSize, sizeOf(reduced_bitmap));
} else {
    return reduced_bitmap;
}

在调整位图大小时,需要计算尺寸差异并相应地进行压缩。

你是如何导入sizeOf方法的? - Mark O'Sullivan
这是用户自己编写的方法,不是内置方法。 - JAAD

1
这不是你问题的解决方案,而是你获得非常小文件的错误原因。
getResizedBitmapLessThan500KB(photo, 500)中,500是图像的最大宽度/高度(以像素为单位),而不是最大大小(以kb为单位)。
因此,所有压缩文件都小于500x500像素。

1
你面临的另一个问题是,你正在测量位图的大小(它没有压缩),然后将其转换为JPG并测量其大小。JPG可能总是会更小,而它的压缩效果取决于图像内容。颜色相同的大区域?很好!非常“繁忙”的图案?压缩效果就不会太好。
好的,进一步解释:
如果你要针对特定文件大小进行设置,你不能根据压缩前的大小来确定。你可以得到一个大致的想法(例如JPEG对这些照片的压缩因子约为15),因此你可以将位图的大小设置为500k * 15。但是,取决于照片中包含的内容,你可能无法完全达到该目标。所以你可能需要这样做:
1.选择jpegFactor 2.bitMapTarget = target * jpegFactor 3.调整位图大小以适应bitMapTarget 4.将位图压缩为JPEG 5.如果仍高于目标,则调整jpegFactor并重试。
你可以在第5步中添加一些步骤来计算出你的接近程度,并尝试考虑这一点。

这似乎不是一个答案。你能否提供答案? - Narendra Singh

1

为了缩小图像的大小,我使用了这段代码...它对我很有效...请检查一下...它可能对您有帮助...

Bitmap photo1 ;
private byte[] imageByteArray1 ;


BitmapFactory.Options opt1 = new BitmapFactory.Options();
opt1.inJustDecodeBounds=true;
BitmapFactory.decodeFile(imageUrl.get(imgCount).toString(),opt1);

// The new size we want to scale to
final int REQUIRED_SIZE=320;

// Find the correct scale value. It should be the power of 2.
int width_tmp=opt1.outWidth,height_tmp=opt1.outHeight;
int scale=2;
while(true){
    if(width_tmp>REQUIRED_SIZE||height_tmp>REQUIRED_SIZE)
        break;
    width_tmp/=2;
    height_tmp/=2;
    scale*=2;
}
// Decode with inSampleSize
BitmapFactory.Options o2=new BitmapFactory.Options();
o2.inSampleSize=scale;
o2.inJustDecodeBounds=false;
photo1=BitmapFactory.decodeFile(imageUrl.get(imgCount).toString(),o2);

ByteArrayOutputStream baos1=new ByteArrayOutputStream();
photo1.compress(Bitmap.CompressFormat.JPEG,60,baos1);
imageByteArray1=baos1.toByteArray();

这里的imageUrl和imgCount是什么? - Narendra Singh
imageUrl 是一个 ArrayList,imgCount 是从 ArrayList 中获取图片 URL 的位置。 - Preet_Android

1
你可以尝试这种方法。
    public static Bitmap getScaledBitmap(Bitmap b, int reqWidth, int reqHeight)
        {
            Matrix m = new Matrix();
            m.setRectToRect(new RectF(0, 0, b.getWidth(), b.getHeight()), new RectF(0, 0, reqWidth, reqHeight), Matrix.ScaleToFit.CENTER);
            return Bitmap.createBitmap(b, 0, 0, b.getWidth(), b.getHeight(), m, true);
        }

//call this method like
    Bitmap bm400=getScaledBitmap(bm,500,500);

它对你有帮助。


0
请检查一下这段代码是否有帮助:
    final static int COMPRESSED_RATIO = 13;
    final static int perPixelDataSize = 4;
    public byte[] getJPGLessThanMaxSize(Bitmap image, int maxSize){
        int maxPixelCount = maxSize *1024 * COMPRESSED_RATIO / perPixelDataSize;
        int imagePixelCount = image.getWidth() * image.getHeight();
        Bitmap reducedBitmap;
        // Adjust Bitmap Dimensions if necessary.
        if(imagePixelCount > maxPixelCount) reducedBitmap = getResizedBitmapLessThanMaxSize(image, maxSize);
        else reducedBitmap = image;

        float compressedRatio = 1;
        byte[] resultBitmap;
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        int jpgQuality = 100;
        // Adjust Quality until file size is less than maxSize.
        do{
            reducedBitmap.compress(Bitmap.CompressFormat.JPEG, jpgQuality, outStream);
            resultBitmap = outStream.toByteArray();
            compressedRatio = resultBitmap.length / (reducedBitmap.getWidth() * reducedBitmap.getHeight() * perPixelDataSize);
            if(compressedRatio > (COMPRESSED_RATIO-1)){
                jpgQuality -= 1;
            }else if(compressedRatio > (COMPRESSED_RATIO*0.8)){
                jpgQuality -= 5;
            }else{
                jpgQuality -= 10;
            }
        }while(resultBitmap.length > (maxSize * 1024));
        return resultBitmap;
    }

    public Bitmap getResizedBitmapLessThanMaxSize(Bitmap image, int maxSize) {
        int width = image.getWidth();
        int height = image.getHeight();
        float bitmapRatio = (float)width / (float) height;

        // For uncompressed bitmap, the data size is:
        // H * W * perPixelDataSize = H * H * bitmapRatio * perPixelDataSize
        // 
        height = (int) Math.sqrt(maxSize * 1024 * COMPRESSED_RATIO / perPixelDataSize / bitmapRatio);
        width = (int) (height * bitmapRatio);
        Bitmap reduced_bitmap = Bitmap.createScaledBitmap(image, width, height, true);
        return reduced_bitmap;
    }

我应该如何决定COMPRESSED_RATIO和perPixelDataSize的值? - Narendra Singh
假设来自维基百科关于 jpg 格式,它指出“JPEG通常在图像质量上几乎看不到损失的情况下实现10:1的压缩”。我认为这是针对 RGB 格式的。对于 ARGB(perPixelDataSize = 4),我只需将其放大。 - i_A_mok

0

我猜你想使用相册或系统相机来获取图片。请注意,通过Intent传输的最大数据有一个上限,这就是为什么你总是得到缩小版本的图像。

你可以参考https://developer.android.com/training/camera/photobasics.html获取标准解决方案。简而言之,你应该获取外部存储器的访问权限并生成相机的URI或从相册应用程序获取URI。然后使用ContentResolver获取图像。

InputStream inputStream = mContentResolver.openInputStream(mUri);

你可能想要实现内容解析器,以便其他应用程序可以访问你的数据,这是标准做法。


0

将我的getResizedBitmapLessThan500KB()自定义为下面这样,对我很有效。

    public static final long CAMERA_IMAGE_MAX_DESIRED_SIZE_IN_BYTES = 2524970;
        public static final double CAMERA_IMAGE_MAX_SIZE_AFTER_COMPRESSSION_IN_BYTES = 1893729.0;

 public static Bitmap getResizedBitmapLessThan500KB(Bitmap image, int maxSize, long file_size_in_bytes) {
                int width = image.getWidth();
                int height = image.getHeight();


                if(file_size_in_bytes <= AppGlobalConstants.CAMERA_IMAGE_MAX_DESIRED_SIZE_IN_BYTES) {
                    if (width > height) {
                        if (width > 500)
                            maxSize = width * 75 / 100;
                    } else {
                        if (height > 500)
                            maxSize = height * 75 / 100;
                    }
                } else {
                    double percentage = ((AppGlobalConstants.CAMERA_IMAGE_MAX_SIZE_AFTER_COMPRESSSION_IN_BYTES/file_size_in_bytes)*100);
                    if (width > height) {
                        if (width > 500)
                            maxSize = width * (int)percentage / 100;
                    } else {
                        if (height > 500)
                            maxSize = height * (int)percentage / 100;
                    }

                    if(maxSize > 600) {
                        maxSize = 600;
                    }

                }

                float bitmapRatio = (float)width / (float) height;
                if (bitmapRatio > 0) {
                    width = maxSize;
                    height = (int) (width / bitmapRatio);
                } else {
                    height = maxSize;
                    width = (int) (height * bitmapRatio);
                }
                Bitmap reduced_bitmap = Bitmap.createScaledBitmap(image, width, height, true);
        //        Log.d("file_size","file_size_during_manipulation: "+String.valueOf(sizeOf(reduced_bitmap)));
                if(sizeOf(reduced_bitmap) > (500 * 1000)) {
                    return getResizedBitmap(reduced_bitmap, maxSize, sizeOf(reduced_bitmap));
                } else {
                    return reduced_bitmap;
                }
            }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接