android将相机捕获的图像的文件大小减小到小于500 kb
Posted
技术标签:
【中文标题】android将相机捕获的图像的文件大小减小到小于500 kb【英文标题】:android reduce file size for camera captured image to be less than 500 kb 【发布时间】:2016-09-14 22:23:27 【问题描述】:我的要求是将相机捕获的图像上传到服务器,但它应该小于 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:以 KB 为单位的文件大小(最初):3294
img_file_size:图像处理后的文件大小(KB):235
查看上面的区别(在输出中)。没有这些操作的初始文件大小,以及那些压缩和其他操作之后的文件大小。我需要这个大小接近 500 kb。
上面的代码对我来说工作得很好,因为它减少了图像文件的大小,使其小于 500 KB。
但是,以下是上述代码的问题 -
即使文件小于 500 KB,此代码也会减小文件大小
如果文件大小超过 500 KB,则缩小后的文件大小会从 500 KB 变得太小,尽管我需要更接近一些。
我需要摆脱以上 2 个问题。所以,需要知道我应该在上面的代码中操作什么。
因为我还想纠正 EXIF 方向(旋转图像),以及我上面提到的要求。
【问题讨论】:
显然不可能(除了调整大小或更改质量(对于 jpg)和测试大小)为 png/jpg,因为如果不压缩,您将不知道压缩数据的大小 那么,它没有任何解决方案吗? @塞尔文 暴力解决方案...调整大小或更改质量并检查大小...如果大小大于要求,调整大小或质量并再次执行,直到获得正确的大小... 请再次检查我的代码,我在 isImageFileSizeGreaterThan500KB() 中执行此操作,但在那之后,我正在旋转图像以使其正确定向(这也是必要的,我不能跳过)。可能,那就是制造麻烦 我在做不,你没有……我没有在你的代码中看到任何循环……不,我不会写任何代码...基本上,您可以知道未压缩的图像数据需要多少(只需H * W * perPixelDataSize
(对于 ARGB_8888 为 4,对于 RGB_888 为 3,等等),但在压缩之前您无法获得压缩后的图像数据大小它(对于 png、jpg 编解码器)
【参考方案1】:
您可以在调整大小之前检查大小。如果位图大小大于 500kb,则调整其大小。
同样为了使更大的位图接近 500kb 大小,检查大小之间的差异并相应地压缩。
if(sizeOf(reduced_bitmap) > (500 * 1024))
return getResizedBitmap(reduced_bitmap, maxSize, sizeOf(reduced_bitmap));
else
return reduced_bitmap;
并在resize Bitmap中计算大小差异并相应压缩
【讨论】:
你是怎么导入sizeOf方法的? 它是用户在提问者代码中编写的方法,它不是内置方法【参考方案2】:请检查此代码是否有用:
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 的值? 该假设来自 Wikipedia 关于 jpg,它指出“JPEG 通常实现 10:1 压缩,图像质量几乎没有明显损失”。我认为它适用于 RGB 格式。对于 ARGB (perPixelDataSize = 4),我只是将其放大。【参考方案3】:你可以试试这个方法
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);
对你有帮助。
【讨论】:
【参考方案4】:将我的 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;
【讨论】:
【参考方案5】:我假设您想使用图库或系统相机来获取图像。请注意,通过Intent
传输的最大数据有一个上限,这就是为什么您总是获得缩小版图像的原因。
标准解决方案可以参考https://developer.android.com/training/camera/photobasics.html。总之,您应该获得对外部存储的访问权限并为相机生成URI
或从图库应用程序获取URI
。然后使用ContentResolver
获取图像。
InputStream inputStream = mContentResolver.openInputStream(mUri);
您可能希望为其他应用程序实现内容解析器来访问您的数据,这是标准做法。
【讨论】:
【参考方案6】:您遇到的另一个问题是您正在测量位图的大小(未压缩),然后将其转换为 JPG 并进行测量。 JPG 可能总是会更小,它的压缩程度将成为图像的一个因素。有很多相同颜色的大区域?伟大的!一个非常“忙碌”的模式?压缩不会那么好。
好的,所以进一步澄清:
如果您以特定文件大小为目标,则无法通过查看压缩前的大小来做到这一点。您可以大致了解一下(例如,这些照片的 JPEG 压缩系数约为 15),因此您可以将位图定位为 500k * 15。但根据照片中的内容,您可能无法准确击中该目标。所以你可能想要这样做:
-
选择一个 jpegFactor
bitMapTarget = 目标 * jpegFactor
调整位图大小以适合 bitMapTarget
将位图压缩为 JPEG
如果这仍然高于目标,则调整 jpegFactor 并重试。
您可以在 #5 上采取一些步骤来确定您有多接近并尝试将其考虑在内。
【讨论】:
它似乎不是一个anwser。你能提供答案吗?【参考方案7】:为了减小图像的大小,我使用了此代码.. 及其对我的工作.. 请检查一次..它可能对你有帮助..
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是什么? imageUrl 是一个 Arraylist,imgCount 是从 arraylist 获取图像 url 的位置。【参考方案8】:这不是您问题的解决方案,而是您得到非常小的文件的错误。
在getResizedBitmapLessThan500KB(photo, 500)
中,500 是图像的最大像素/高度(以像素为单位),而不是以 kb 为单位的最大大小。
所以所有压缩文件都小于 500x500 像素
【讨论】:
以上是关于android将相机捕获的图像的文件大小减小到小于500 kb的主要内容,如果未能解决你的问题,请参考以下文章