adi
adi

Reputation: 708

Programmatic Image Resizing in Android, Memory Issues

Days, I've spent working on this. Weeks, perhaps. Literally. :(

So I've got an image on an SD card that more than likely came out of the built-in camera. I want to take that image and downsample it to an arbitrary size (but always smaller and never larger). My code uses standard Android Bitmap methods to decode, resize, recompress, and save the image. Everything works fine as long as the final image is smaller than 3MP or so. If the image is larger, or if I try to do several of these at once, the application crashes with an OutOfMemoryError. I know why that's happening, and I know it's happening for a perfectly legitimate reason, I just want it to not happen anymore.

Look, I'm not trying to launch a rocket here. All I want to do is resize a camera image and dump it to an OutputStream or even a temporary file. Surely someone out there must have done such a thing. I don't need you to write my code for me, and I don't need my hand held. But between my various programming abortions and days of obsessed Googling, I don't even know which direction to head in. Roughly speaking, does anyone know how to decode a JPEG, downsample it, re-compress it in JPEG, and send it out on an OutputStream without allocating a massive amount of memory?

Upvotes: 2

Views: 4404

Answers (2)

Ok I know it's a little bit late but, I had this problem and I found solution. It is actually easy and I am sure it supports back to api 10(I have no idea about before 10). I tried this with my phone. It is a samsung galaxy s2 with an 8mp camera and the code perfectly resized camera images to the 168x168 as well as images i found on web. I checked the images by using file manager too. I never tried resizing images to bigger resoulation.

private Bitmap resize(Bitmap bp, int witdh, int height){
    return Bitmap.createScaledBitmap(bp, width, height, false);
}

you can save it like this

private void saveBitmap(Bitmap bp) throws FileNotFoundException{
    String state = Environment.getExternalStorageState();
    File folder;
    //if there is memory card available code choose that
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        folder=Environment.getExternalStorageDirectory();
    }else{
        folder=Environment.getDataDirectory();
    }
    folder=new File(folder, "/aaaa");
    if(!folder.exists()){
        folder.mkdir();
    }

    File file=new File(folder, (int)(Math.random()*10000)+".jpg");
    FileOutputStream os=new FileOutputStream(file);
    bp.compress(Bitmap.CompressFormat.JPEG, 90, os);
}

thanks to this link

Upvotes: 3

TieDad
TieDad

Reputation: 9889

The following code is from my previous project. Key point is "options.inSampleSize".

public static Bitmap makeBitmap(String fn, int minSideLength, int maxNumOfPixels) {
    BitmapFactory.Options options;
    try {
        options = new BitmapFactory.Options();

        options.inPurgeable = true;
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(fn, options);
        if (options.mCancel || options.outWidth == -1
                || options.outHeight == -1) {
            return null;
        }
        options.inSampleSize = computeSampleSize(
                options, minSideLength, maxNumOfPixels);
        options.inJustDecodeBounds = false;
        //Log.e(LOG_TAG, "sample size=" + options.inSampleSize);

        options.inDither = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        return BitmapFactory.decodeFile(fn, options);
    } catch (OutOfMemoryError ex) {
        Log.e(LOG_TAG, "Got oom exception ", ex);
        return null;
    }
}

private static int computeInitialSampleSize(BitmapFactory.Options options,
        int minSideLength, int maxNumOfPixels) {
    double w = options.outWidth;
    double h = options.outHeight;

    int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
            (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
    int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :
            (int) Math.min(Math.floor(w / minSideLength),
            Math.floor(h / minSideLength));

    if (upperBound < lowerBound) {
        // return the larger one when there is no overlapping zone.
        return lowerBound;
    }

    if ((maxNumOfPixels == UNCONSTRAINED) &&
            (minSideLength == UNCONSTRAINED)) {
        return 1;
    } else if (minSideLength == UNCONSTRAINED) {
        return lowerBound;
    } else {
        return upperBound;
    }
}

Upvotes: 0

Related Questions