android уменьшить размер файла для камеры захваченного изображения, чтобы быть менее 500 Кб
Мое требование-загрузить изображение с камеры на сервер, но оно должно быть меньше 500 КБ. В случае, если он больше 500 КБ, его нужно уменьшить до размера меньше 500 КБ (но несколько ближе к нему)
Для этого я использую следующий код -
@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﹕ размер файла в KBs (изначально): 3294
Img_file_size﹕ размер файла в KBs после манипуляций с изображением: 235
Смотрите разницу выше (в выходных данных). Исходный размер файла без этих операций, а также после этих операций сжатия и других операций. Мне нужно, чтобы этот размер был немного ближе к 500 Кб.
Приведенный выше код работает довольно хорошо для меня, так как он уменьшает размер файла изображения, чтобы сделать его менее 500 КБ.
Но ... , ниже приведены проблемы с вышеуказанным кодом -
-
Этот код уменьшает размер файла, даже если он меньше 500 КБ
-
В случае, если он больше 500 КБ, уменьшенный размер файла становится тоже меньше от 500 КБ, хотя мне он нужен несколько ближе.
Мне нужно избавиться от вышеперечисленных проблем. Итак, нужно знать, чем я должен манипулировать в приведенном выше коде.
Поскольку я также хочу исправить EXIF-ориентацию (повернутые изображения), вместе с моим вышеуказанное требование.
8 ответов:
Вы можете проверить размер перед изменением размера, если размер растрового изображения превышает 500 кб, а затем изменить его размер .
Также для того, чтобы увеличить размер растрового изображения до 500 Кб, проверьте разницу между размером и сжать соответственно .
if(sizeOf(reduced_bitmap) > (500 * 1024)) { return getResizedBitmap(reduced_bitmap, maxSize, sizeOf(reduced_bitmap)); } else { return reduced_bitmap; }
И в resize Bitmap вычислить разницу в размере и соответственно сжать
Это не решение вашей проблемы, а ошибка, из-за которой вы получаете очень маленькие файлы.
В
getResizedBitmapLessThan500KB(photo, 500)
500-это максимальный с / высота в пикселе изображения, а не максимальный размер в КБ.Таким образом, все сжатые файлы имеют размер менее 500x500 пикселей
Чтобы уменьшить размер изображения, я использовал этот код.. и его работа для меня.. Пожалуйста, проверьте это один раз.. Это может оказаться полезным для вас..
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();
Еще одна проблема заключается в том, что вы измеряете размер растрового изображения (которое не сжимается), а затем преобразуете его в JPG и измеряете это. JPG, вероятно, всегда будет меньше, и то, насколько хорошо он сжимается, будет фактором того, что представляет собой изображение. Много больших участков одного цвета? Отлично! Чрезвычайно "занятый" паттерн? Сжатие будет не таким сильным.
Хорошо, поэтому дальнейшее уточнение:
Если вы нацелены на определенный размер файла, вы не можете сделать это с помощью глядя на размер перед сжатием. Вы можете получить общую идею (например, сжатие JPEG в ~15 раз для этих фотографий), поэтому вы можете выбрать 500k * 15 для растрового изображения. Но в зависимости от того, что на фотографии, вы можете не попасть точно в эту цель. Так что вы, вероятно, хотите сделать это:
- выберите jpegFactor
- bitMapTarget = target * jpegFactor
- adjustBitmap size to fit bitMapTarget
- сжатие растрового изображения в JPEG
- Если это все еще выше цель, затем отрегулируйте jpegFactor и повторите попытку.
Вы можете сделать несколько шагов на #5, чтобы выяснить, насколько близко вы были, и попытаться принять это во внимание.
Настройка моего 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);
Это полезно для вас.
Я предполагаю, что вы хотите использовать галерею или системную камеру, чтобы получить изображение. Обратите внимание, что существует верхний предел для максимального количества данных, передаваемых через
Intent
, и поэтому вы всегда получаете уменьшенную версию изображений.Вы можете обратиться к https://developer.android.com/training/camera/photobasics.html для стандартного решения. Таким образом, вы должны получить доступ к внешнему хранилищу и создать
URI
для камеры или получитьURI
из приложения gallery. Затем используйтеContentResolver
чтобы получить изображение.InputStream inputStream = mContentResolver.openInputStream(mUri);
Возможно, вам захочется реализовать распознаватель содержимого для другого приложения, чтобы получить доступ к вашим данным, и это стандартная практика.
Пожалуйста, проверьте, если этот код полезен:
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; }