简体   繁体   中英

Bitmap.compress results in too large of file

I have an app that needs to resize an image and then save it to jpg. My test image is a photo with very smooth gradients in the sky. I'm trying to save it to jpeg after a resize using this code:

dstBmp = Bitmap.createBitmap(srcBmp, cropX, 0, tWidth, srcBmp.getHeight());
if (android.os.Build.VERSION.SDK_INT > 12) {
    Log.w("General", "Calling setHasAlpha");
    dstBmp.setHasAlpha(true);
}
dstBmp = Bitmap.createScaledBitmap(dstBmp, scaledSmaller, scaledLarger, true);
OutputStream out = null;
File f = new File(directory + "/f"+x+".jpg");
try {
    f.createNewFile();
    out = new FileOutputStream(f);
} catch (FileNotFoundException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}

dstBmp.compress(CompressFormat.JPEG, jpegQuality, out);

The problem is that excessive banding occurs in the gradients of the image unless I bump the quality up to around 95. However, at quality level 95, the resulting file is over 150kb. When I perform these same functions in photoshop and do a "Save for web", I can avoid banding all the way down to quality level 40 with an image size of 50kb. Using ImageCR on my web server I can accomplish the same at 30kb.

Is there any way in Java to compress an image into jpeg more efficiently, or is there a separate library or api I can use to do so? I'm loading a lot of images into memory and at this rate the app is threatening OOM errors on older devices. I'd be happy to allocate more time to image manipulation if that will help the final result.

Try this code from http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

In short, you have 2 static methods

the first for calculating the new size of the image

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;
}

second for loading the scaled size image into memory:

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);
}

and You can call this function like this:

Bitmap b = decodeSampledBitmapFromResource(getResources(), R.id.myimage, 128, 128));

You can modify the second method to take input parametar String (the path if the image is file on sdcard) instead of resource, and instead of decodeResource use:

BitmapFactory.decodeFile(path, options);

I always use this code when loading big images.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM