android 将相机捕获图像的文件大小减小到 500 kb 以下
android reduce file size for camera captured image to be less than 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﹕ file size in KBs (initially): 3294
img_file_size﹕ file size in KBs after image manipulations: 235
查看上面的差异(在输出中)。没有那些操作的初始文件大小,以及那些压缩和其他操作之后的文件大小。我需要那个大小稍微接近 500 kb。
上面的代码对我来说工作得很好,因为它减少了图像文件的大小,使其小于 500 KB。
但是,上面的代码存在以下问题 -
此代码正在减小文件大小,即使它小于 500 KB
如果超过 500 KB,缩小后的文件大小会从 500 KB 变得太小,虽然我需要它更接近一些。
我需要解决以上两个问题。所以,需要知道我应该在上面的代码中操作什么。
因为我还想更正 EXIF 方向(旋转图像),以及我上面提到的要求。
这不是您问题的解决方案,而是导致文件非常小的错误。
在 getResizedBitmapLessThan500KB(photo, 500)
中,500 是图像的最大 with/height 像素而不是最大尺寸 kb。
因此所有压缩文件都小于 500x500 像素
您遇到的另一个问题是您正在测量位图(未压缩)的大小,然后将其转换为 JPG 并进行测量。 JPG 可能总是会更小,它的压缩程度将成为影响图像质量的一个因素。很多相同颜色的大区域?伟大的!极其 "busy" 的模式?压缩不会那么大。
好的,进一步说明:
如果您的目标是某个文件大小,则无法通过查看压缩前的大小来实现。您可以获得一个大概的想法(例如,JPEG 对这些照片进行约 15 倍的压缩),因此您可以将位图定位为 500k * 15。但根据照片中的内容,您可能无法准确击中该目标。所以你可能想这样做:
- 选择一个 jpegFactor
- bitMapTarget = 目标 * jpegFactor
- 调整位图大小以适合 bitMapTarget
- 将位图压缩为 JPEG
- 如果这仍然高于目标,则调整 jpegFactor 并重试。
您可以在 #5 上采取一些步骤来弄清楚您有多接近并尝试将其考虑在内。
请检查此代码是否有帮助:
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;
}
为了减小图像的大小,我使用了这段代码..它对我有用..
请检查一次..它可能对你有帮助..
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();
我假设您想使用图库或系统相机来获取图像。请注意,通过 Intent
传输的最大数据有上限,这就是为什么你总是得到缩小版本的图像。
标准解可参考https://developer.android.com/training/camera/photobasics.html。总之,您应该获得对外部存储的访问权限并为相机生成 URI
或从图库应用程序中获取 URI
。然后使用 ContentResolver
获取图像。
InputStream inputStream = mContentResolver.openInputStream(mUri);
您可能希望为其他应用程序实施内容解析器以访问您的数据,这是标准做法。
将我的 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;
}
}
你可以试试这个方法
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);
对你有帮助。
您可以在调整大小之前检查大小。如果位图的大小大于 500kb,则调整它的大小。
同样为了使更大的位图接近 500kb 大小,检查大小之间的差异并相应地压缩。
if(sizeOf(reduced_bitmap) > (500 * 1024)) {
return getResizedBitmap(reduced_bitmap, maxSize, sizeOf(reduced_bitmap));
} else {
return reduced_bitmap;
}
并在调整位图大小时计算大小差异并进行相应压缩
我的要求是将摄像头拍摄的图像上传到服务器,但它应该小于 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﹕ file size in KBs (initially): 3294
img_file_size﹕ file size in KBs after image manipulations: 235
查看上面的差异(在输出中)。没有那些操作的初始文件大小,以及那些压缩和其他操作之后的文件大小。我需要那个大小稍微接近 500 kb。
上面的代码对我来说工作得很好,因为它减少了图像文件的大小,使其小于 500 KB。
但是,上面的代码存在以下问题 -
此代码正在减小文件大小,即使它小于 500 KB
如果超过 500 KB,缩小后的文件大小会从 500 KB 变得太小,虽然我需要它更接近一些。
我需要解决以上两个问题。所以,需要知道我应该在上面的代码中操作什么。
因为我还想更正 EXIF 方向(旋转图像),以及我上面提到的要求。
这不是您问题的解决方案,而是导致文件非常小的错误。
在 getResizedBitmapLessThan500KB(photo, 500)
中,500 是图像的最大 with/height 像素而不是最大尺寸 kb。
因此所有压缩文件都小于 500x500 像素
您遇到的另一个问题是您正在测量位图(未压缩)的大小,然后将其转换为 JPG 并进行测量。 JPG 可能总是会更小,它的压缩程度将成为影响图像质量的一个因素。很多相同颜色的大区域?伟大的!极其 "busy" 的模式?压缩不会那么大。
好的,进一步说明:
如果您的目标是某个文件大小,则无法通过查看压缩前的大小来实现。您可以获得一个大概的想法(例如,JPEG 对这些照片进行约 15 倍的压缩),因此您可以将位图定位为 500k * 15。但根据照片中的内容,您可能无法准确击中该目标。所以你可能想这样做:
- 选择一个 jpegFactor
- bitMapTarget = 目标 * jpegFactor
- 调整位图大小以适合 bitMapTarget
- 将位图压缩为 JPEG
- 如果这仍然高于目标,则调整 jpegFactor 并重试。
您可以在 #5 上采取一些步骤来弄清楚您有多接近并尝试将其考虑在内。
请检查此代码是否有帮助:
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;
}
为了减小图像的大小,我使用了这段代码..它对我有用.. 请检查一次..它可能对你有帮助..
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();
我假设您想使用图库或系统相机来获取图像。请注意,通过 Intent
传输的最大数据有上限,这就是为什么你总是得到缩小版本的图像。
标准解可参考https://developer.android.com/training/camera/photobasics.html。总之,您应该获得对外部存储的访问权限并为相机生成 URI
或从图库应用程序中获取 URI
。然后使用 ContentResolver
获取图像。
InputStream inputStream = mContentResolver.openInputStream(mUri);
您可能希望为其他应用程序实施内容解析器以访问您的数据,这是标准做法。
将我的 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;
}
}
你可以试试这个方法
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);
对你有帮助。
您可以在调整大小之前检查大小。如果位图的大小大于 500kb,则调整它的大小。
同样为了使更大的位图接近 500kb 大小,检查大小之间的差异并相应地压缩。
if(sizeOf(reduced_bitmap) > (500 * 1024)) {
return getResizedBitmap(reduced_bitmap, maxSize, sizeOf(reduced_bitmap));
} else {
return reduced_bitmap;
}
并在调整位图大小时计算大小差异并进行相应压缩