Reputation: 32273
How do I get a bitmap with a certain (memory friendly) size from the camera?
I'm starting a camera intent with:
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
cameraIntent.putExtra("return-data", true);
photoUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "mytmpimg.jpg"));
cameraIntent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, photoUri);
startActivityForResult(cameraIntent, REQUEST_CODE_CAMERA);
I handle the result here:
// Bitmap photo = (Bitmap) intent.getExtras().get("data");
Bitmap photo = getBitmap(photoUri);
Now if I use the commented line - get the bitmap directly, I get always a 160 x 120 bitmap, and that's too small. If I load it from the URI using some standard stuff I found (method getBitmap), it loads a 2560 x 1920 bitmap (!) and that consumes almost 20 mb memory.
How do I load let's say 480 x 800 (the same size the camera preview shows me)?
Without having to load the 2560 x 1920 into memory and scaling down.
Upvotes: 13
Views: 4191
Reputation: 32273
Here is what I came up with, based on a method called getBitmap() from a crop library which was removed from old Android version. I did some modifications:
private Bitmap getBitmap(Uri uri, int width, int height) {
InputStream in = null;
try {
int IMAGE_MAX_SIZE = Math.max(width, height);
in = getContentResolver().openInputStream(uri);
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, o);
in.close();
int scale = 1;
if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
scale = (int)Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
}
//adjust sample size such that the image is bigger than the result
scale -= 1;
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
in = getContentResolver().openInputStream(uri);
Bitmap b = BitmapFactory.decodeStream(in, null, o2);
in.close();
//scale bitmap to desired size
Bitmap scaledBitmap = Bitmap.createScaledBitmap(b, width, height, false);
//free memory
b.recycle();
return scaledBitmap;
} catch (FileNotFoundException e) {
} catch (IOException e) {
}
return null;
}
What this does is load the bitmap using BitmapFactory.Options() + some sample size - this way the original image is not loaded into memory. The problem is that the sample size just works in steps. I get the "min" sample size for my image using some maths I copied - and subtract 1 in order to get the sample size which will produce the min. bitmap bigger than the size I need.
And then in order to get the bitmap with exactly the size requested do normal scaling with Bitmap.createScaledBitmap(b, width, height, false);
. And immediatly after it recycle the bigger bitmap. This is important, because, for example, in my case, in order to get 480 x 800 bitmap, the bigger bitmap was 1280 x 960 and that occupies 4.6mb memory.
A more memory friendly way would be to not adjust scale
, so a smaller bitmap will be scaled up to match the required size. But this will reduce the quality of the image.
Upvotes: 2