reduce image size in Android programmatically - android

I am developing a application where , there is a profile page where user can set its image via camera or from gallery.
Now when user sets large image then my app stops unfortunate , i found a reason for that is , we can't set image on image View exceeds some size.
So i decided to reduce image size before setting it on image view.
Here i found many solutions but in all that it also reduces its width and height, but i don't want to reduce its width and height.
I only want to reduce its size in percentage. E.x from 1 mb to 100 kb.
How can I achieve this in Android?

Try this way,hope this will help you to solve your problem.
public Bitmap decodeFile(String path) {
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, o);
// The new size we want to scale to
final int REQUIRED_SIZE = 70;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE)
scale *= 2;
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeFile(path, o2);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}

Related

What does BitmapFactory.Options do?

What does BitmapFactory.Options in android.graphics.BitmapFactory.Options do?
There is no theoretical explanation in the android sdk reference manual about this class, it only contains the explanation about the methods of the class.
Bitmapfactory is mainly used for Scaling
Bitmap lBmp = BitmapFactory.decodeResource(getResources(), R.Drawable.ic_dolphin);
It gets the "dolpin" image and it will reduce the image size, if we dnt use bitmapfactory then it leads to insufficient memory allocations
It's used to pass options to the BitmapFactory - as you might expect :)
For example, you can use it to explicitly scale the Bitmap up or down from the source.
See this example
This method is used to create bitmap of given specific size which is stored in sdcard.
public Bitmap decodeFile(String path,int size) {
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, o);
// The new size we want to scale to
final int REQUIRED_SIZE = size;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE)
scale *= 2;
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeFile(path, o2);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}

Android Bitmap OutOfMemoryError [duplicate]

This question already has answers here:
Android: BitmapFactory.decodeStream() out of memory with a 400KB file with 2MB free heap
(8 answers)
Closed 9 years ago.
I'm having an OutOfMemoryError in my VSD220 (It's a 22" Android based All in one)
for (ImageView img : listImages) {
System.gc();
Bitmap myBitmap = BitmapFactory.decodeFile(path);
img.setImageBitmap(myBitmap);
img.setOnClickListener(this);
}
I really don't know what to do, because this image is below the maximum resolution. The image size is something about (1000x1000), and the display it's 1920x1080.
Any help?
(That foreach cycle is for about 20 elements, it gots broken after 6, or 7 loops..)
Thanks a lot.
Ezequiel.
You should take a look at the training docs for Managing Bitmap Memory. Depending on your OS version, you could use different techniques to allow you to manage more Bitmaps, but you'll probably have to change your code anyway.
In particular, you're probably going to have to use an amended version of the code in "Load a Scaled Down Version into Memory", but I at least have found this section to be particularly useful:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
This method makes it easy to load a bitmap of arbitrarily large size
into an ImageView that displays a 100x100 pixel thumbnail, as shown in
the following example code:
mImageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
Are you really sure you want to load the same Bitmap 20 times? Don't you want to load it once and set it inside the loop.
Still, loading a 1000x1000 pixel image is not guaranteed to work, regardless of screen resolution. Remember that a 1000x1000 pixel image takes up 1000x1000x4 bytes =~4MB (if you load it as ARGB_8888). If your heap memory is fragmented/too small you may not have enough space to load the bitmap. You may want to look into the BitmapFactory.Options class and experiment with inPreferredConfig and inSampleSize
I would suggest that you either use the suggestion by DigCamara and decide on a size and load a downsampled image of nearly that size (I say nearly because you won't get the exact size using that technique) or that you try to load the full size image and then recursively increase the sample size (by factors of two for best result) until you either reach a max sample size or the image is loaded:
/**
* Load a bitmap from a stream using a specific pixel configuration. If the image is too
* large (ie causes an OutOfMemoryError situation) the method will iteratively try to
* increase sample size up to a defined maximum sample size. The sample size will be doubled
* each try since this it is recommended that the sample size should be a factor of two
*/
public Bitmap getAsBitmap(InputStream in, BitmapFactory.Config config, int maxDownsampling) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 1;
options.inPreferredConfig = config;
Bitmap bitmap = null;
// repeatedly try to the load the bitmap until successful or until max downsampling has been reached
while(bitmap == null && options.inSampleSize <= maxDownsampling) {
try {
bitmap = BitmapFactory.decodeStream(in, null, options);
if(bitmap == null) {
// not sure if there's a point in continuing, might be better to exit early
options.inSampleSize *= 2;
}
}
catch(Exception e) {
// exit early if we catch an exception, for instance an IOException
break;
}
catch(OutOfMemoryError error) {
// double the sample size, thus reducing the memory needed by 50%
options.inSampleSize *= 2;
}
}
return bitmap;
}

Why decodeStream returns null when options.inSampleSize is derived rather than set

Ok Someone help me figure this out. I am using the Bitmap.options as recommended by other threads and android tutorials to figure out the inSample size. The problem that the following code is resulting in null bitmap instead of scaled bitmap
private int determineCorrectScale(InputStream imageStream){
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(imageStream, null, o);
// The new size we want to scale to
final int REQUIRED_SIZE = 100;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE) {
scale *= 2;
}
return scale;
}
private String saveScaledBitmapToPhone(Uri imagUri){
InputStream imageStream;
try {
imageStream = getContentResolver().openInputStream(imagUri);
int scale= determineCorrectScale(imageStream);
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = scale;
Bitmap yourSelectedImage = BitmapFactory.decodeStream(imageStream, null, options );
.
.
.
.
} catch (Exception e) {
return imagUri.toString(); //default
}
}
The problem that yourSelectedImage is null. However if I comment out the line
int scale= determineCorrectScale(imageStream);
and set the insampleSize to 8 or 16 or any other fixed manual number then everything works fine. Can any one explain this behaviour or how to fix it? My feeling says it is due to creating two Options objects of static class but that's just a guess. I still can't fix it :(
PLEASE HELP
You're reusing the same data stream. Either reset it, cache the data in a byte array, or open a new stream.

Android out of memory exception with bitmaps

First off, I have read many posts and articles about out of memory exceptions but none of them have helped with my situation. What I'm trying to do is load an image from the sd card but scale it to an exact pixel size.
I first get the width and height of the image and calculate the sample size:
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(backgroundPath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, getWidth(), getHeight());
Here's how I get the sample size (although its not really relevant):
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
// NOTE: we could use Math.floor here for potential better image quality
// however, this also results in more out of memory issues
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float)height / (float)reqHeight);
} else {
inSampleSize = Math.round((float)width / (float)reqWidth);
}
}
return inSampleSize;
}
Now that I have a sample size I load the image from disk to an approximate size (sample size):
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
options.inPurgeable = true;
Bitmap bmp = BitmapFactory.decodeFile(backgroundPath, options);
Now, I scale this bitmap that I have created to the exact size I need and clean up:
// scale the bitmap to the exact size we need
Bitmap editedBmp = Bitmap.createScaledBitmap(bmp, (int) (width * scaleFactor), (int) (height * scaleFactor), true);
// clean up first bitmap
bmp.recycle();
bmp = null;
System.gc(); // I know you shouldnt do this, but I'm desperate
The above step is usually get my out of memory exception. Does anyone know a way to load an exact size bitmap from disk to avoid having to create two separate bitmaps like above?
Also, it seems like more exceptions occur when the user runs this code for a second time (sets a new image). However, I make sure to unload the drawable that was created from the bitmap which allows it to be garbage collected before this code is run again.
Any suggestions?
Thanks,
Nick
In your case there's no need to create the intermediate bitmap after you've performed the first decode. Since you're drawing to to a Canvas, you can use either the following methods (whichever you find most convenient) to scale the image to the perfect size.
drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)
drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)
Maybe this method would be helpful, I think I pulled it off of stackoverflow myself. It solved my out of memory exception issue.
private Bitmap decodeFile(File f){
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
//The new size we want to scale to
final int REQUIRED_SIZE=250;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
scale*=2;
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}

How to resize an image to fit multiple screen densities

I need to add an avatar to a grid item.
I want to know how to handle the resizing of an image chosen from the phones gallery. Once chosen, I imagine some resizing will be needed, to fit it into the grid.
However, do I need to store a resized image for each screen density; store one xhdpi version and scale down for other devices, or be clever in some other way?
The reason is, the app stores this image to a cloud db and other people can download this image. They may see the image on different devices (hence the requirement of different image sizes). How should the managment of this image be processed?
I hope you find the code below useful. It will return image with reqd dimensions with minimum overhead. I have used this many times, works like charm. You can set the required dimension according to target device. Scaling will cause blur in picture but this doesn't.
private Bitmap getBitmap(Uri uri) {
InputStream in = null;
try {
final int IMAGE_MAX_SIZE = 200000; // 0.2MP
in = my_context.getContentResolver().openInputStream(uri);
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true; //request only the dimesion
BitmapFactory.decodeStream(in, null, o);
in.close();
int scale = 1;
while ((o.outWidth * o.outHeight) * (1 / Math.pow(scale, 2)) > IMAGE_MAX_SIZE) {
scale++;
}
Bitmap b = null;
in = my_context.getContentResolver().openInputStream(uri);
if (scale > 1) {
scale--;
// scale to max possible inSampleSize that still yields an image
// larger than target
o = new BitmapFactory.Options();
o.inSampleSize = scale;
b = BitmapFactory.decodeStream(in, null, o);
// resize to desired dimensions
int height = b.getHeight();
int width = b.getWidth();
double y = Math.sqrt(IMAGE_MAX_SIZE
/ (((double) width) / height));
double x = (y / height) * width;
Bitmap scaledBitmap = Bitmap.createScaledBitmap(b, (int) x, (int) y, true);
b.recycle();
b = scaledBitmap;
System.gc();
} else {
b = BitmapFactory.decodeStream(in);
}
in.close();
return b;
} catch (IOException e) {
return null;
}
}
android:scaleType="fitXY"
android:layout_gravity="center"
Will scale an image and center it Size the Size a container to fill_parent with and it should do just that.
You can put your images to drawable (without creating xhdpi,hdpi,mdpi,ldpi...) (global images).
Then you can create 4 same layouts for different screensizes . All of your layout can use the images in your drawable director. So you can resize your image easly.

Categories

Resources