Titanium Community Questions & Answer Archive

We felt that 6+ years of knowledge should not die so this is the Titanium Community Questions & Answer Archive

Preventing out of memory with high res photo

Hi,

I notice in my app when I take a picture and process it the app crashes if I take the picture in full resolution (8M on my phone). My code is basically trying to do the following:

  • Take a picture
  • Check if it was in portrait or landscape
  • Resize to no bigger than the screen
  • Save to disk

Functionally it all works, but the act of putting the image into an imageview (which I think I need to get the size) causes OOM about 50% of the time.

02-11 10:22:14.039: E/dalvikvm-heap(6131): 24023040-byte external allocation too large for this process.
02-11 10:22:14.039: E/dalvikvm(6131): Out of memory: Heap Size=8263KB, Allocated=4617KB, Bitmap Size=23478KB, Limit=32768KB
02-11 10:22:14.039: E/dalvikvm(6131): Trim info: Footprint=8263KB, Allowed Footprint=8263KB, Trimmed=1624KB
02-11 10:22:14.079: E/GraphicsJNI(6131): VM won't let us allocate 24023040 bytes
02-11 10:22:14.079: D/dalvikvm(6131): GC_FOR_MALLOC freed <1K, 45% free 4617K/8263K, external 23478K/25526K, paused 26ms
02-11 10:22:14.079: D/skia(6131): --- decoder->decode returned false
02-11 10:22:14.079: E/TiDrawableReference(6131): (main) [82,70664] Unable to load bitmap. Not enough memory: bitmap size exceeds VM budget(Heap Size=8839KB, Allocated=4617KB, Bitmap Size=23478KB)
02-11 10:22:14.079: E/TiDrawableReference(6131): java.lang.OutOfMemoryError: bitmap size exceeds VM budget(Heap Size=8839KB, Allocated=4617KB, Bitmap Size=23478KB)
02-11 10:22:14.079: E/TiDrawableReference(6131):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
02-11 10:22:14.079: E/TiDrawableReference(6131):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:694)
02-11 10:22:14.079: E/TiDrawableReference(6131):     at org.appcelerator.titanium.view.TiDrawableReference.getBitmap(TiDrawableReference.java:253)
02-11 10:22:14.079: E/TiDrawableReference(6131):     at org.appcelerator.titanium.view.TiDrawableReference.getBitmap(TiDrawableReference.java:483)
02-11 10:22:14.079: E/TiDrawableReference(6131):     at ti.modules.titanium.ui.widget.TiUIImageView.setImage(TiUIImageView.java:738)

Here is my code:

    Ti.API.info("creating picture image view");
                var iView = Ti.UI.createImageView({
                    image:event.media,
                    visible:false,

                    height:'auto',
                    width:'auto'
                });    
                Ti.API.info("image view created");


                //parentWin.add(iView);
                var img = iView.toImage();
                Ti.API.info("Size "+img.height+' '+img.width);
                if (img.width>img.height){
                    //do stuff
                }
                else {




                    var factor = AppUtil.ui.screenWidth/img.width;
                    var newH = Math.round(img.height*factor);
                    var newW = Math.round(img.width*factor);
                    Ti.API.info("Resizing Factor "+factor+" New Height "+newH+" New Width "+newW);
                    var f = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory,fname);
                    if (AppUtil.isAndroid()){

                        newImg = ImageFactory.imageAsResized(event.media, { width:newW, height:newH});
                        ImageFactory.compressToFile(newImg,.5,f.getNativePath());
                    } else {
                        //alert("Resizing Factor "+factor+" New Height "+newH+" New Width "+newW);

                    //    newImg = ImageFactory.imageAsResized(event.media, { width:640, height:960});
                        //alert("resizing complete");
                        newImg2 = ImageFactory.compress(event.media.imageAsResized(640,960),.5);
                        f.write(newImg2);
                    }

I haven't seen the issue on iOS. A few questions:

  • It appears that its trying to allocate 24megs but the image is only 8 - any idea why?
  • ANything I can do in this area of the code to be more memory efficient?
— asked February 11th 2012 by Frank A
  • android
  • camera
  • memory
1 Comment
  • My gut says that needing to create the image view to get the height and width is the part that is most problematic here.

    — commented February 11th 2012 by Frank A

3 Answers

  • I've created a standlone app.js that recreates the issue. The OOM happens when I take a full resolution (8m) picture. It can happen in a few different spots:

    
    // this sets the background color of the master UIView (when there are no windows/tab groups on it)
    Titanium.UI.setBackgroundColor('#000');
    // create tab group
    var tabGroup = Titanium.UI.createTabGroup();
    
    var win1 = Titanium.UI.createWindow({  
        title:'Tab 1',
        backgroundColor:'#fff'
    });
    var tab1 = Titanium.UI.createTab({  
        icon:'KS_nav_views.png',
        title:'Tab 1',
        window:win1
    });
    //
    //  add tabs
    //
    tabGroup.addTab(tab1);  
    // open tab group
    tabGroup.open();
    // Create a Button.
    var aButton = Ti.UI.createButton({
        title : 'take pic',
        height : 100,
        width : 250,
        top : 20,
        left : 20
    });
    
    // Listen for click events.
    aButton.addEventListener('click', function() {
        addPictures(1,win1);
    });
    
    // Add to the parent view.
    win1.add(aButton);
    
    var ImageFactory = require('ti.imagefactory');
    function addPictures(mid,parentWin){
        Titanium.Media.showCamera({
            success:function(event) {
                // called when media returned from the camera
                Ti.API.debug('Our type was: '+event.mediaType);
                if(event.mediaType == Ti.Media.MEDIA_TYPE_PHOTO) {
                           var fname = 'test.jpg';
                        Ti.API.debug("Writing file "+fname + JSON.stringify(event.media));
    
    
                    Ti.API.info("creating picture image view");
                    Ti.API.info("memory = "+Ti.Platform.availableMemory);
                    var iView = Ti.UI.createImageView({
                        image:event.media,
                        visible:false,
    
                        height:'auto',
                        width:'auto'
                    });    
                    Ti.API.info("image view created");
                    Ti.API.info("memory = "+Ti.Platform.availableMemory);
                    //this sometimes causes OOM
                    var img = iView.toImage();
                    var ih = img.height;
                    var iw = img.width;
                    var len = img.length;
                    //img = null;
                    //iView = null;
    
                    Ti.API.info("Size "+ih+' '+iw);
                    Ti.API.info("Img Info: "+JSON.stringify(img));
                    if (iw>ih){
                        alert("landscape");                    
                    }
                    else {    
                        //already in portrait
                        savePic(fname,mid,1,event.media,ih,iw,parentWin);
                   }
                  } else {
                    alert("got the wrong type back ="+event.mediaType);
                }
            },
            cancel:function() {
                alert('cancel');
            },
            error:function(error) {
                // called when there's an error
                var a = Titanium.UI.createAlertDialog({title:'Camera'});
                if (error.code == Titanium.Media.NO_CAMERA) {
                    a.setMessage('No Camera Found');
                } else {
                    a.setMessage('Unexpected error: ' + error.code);
                }
                a.show();
            },
            saveToPhotoGallery:false,
            allowEditing:false,
            mediaTypes:[Ti.Media.MEDIA_TYPE_PHOTO]
        });
    }
    
    function savePic(fname,mid,thisImgIdx,media,ih,iw,parentWin){
                        var factor = Ti.Platform.displayCaps.platformWidth/iw;
                        var newH = Math.round(ih*factor);
                        var newW = Math.round(iw*factor);
    
                        Ti.API.info("Resizing Factor "+factor+" New Height "+newH+" New Width "+newW);
                        Ti.API.info("memory = "+Ti.Platform.availableMemory);
                        var f = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory,fname);
    
                            //sometimes either of these lines cause OOM
                            newImg = ImageFactory.imageAsResized(media, { width:newW, height:newH});
                            ImageFactory.compressToFile(newImg,.5,f.getNativePath());
    
                        Ti.API.debug("Writing file, complete "+fname);
                        Ti.API.info("memory = "+Ti.Platform.availableMemory);
                        alert('done');
    }
    
    — answered February 12th 2012 by Frank A
    permalink
    1 Comment
    • There appears to be some type of workaround in the java code.

      http://stackoverflow.com/a/3549021/555543

      — commented February 12th 2012 by Frank A
  • you have multiple instances of the image object in memory, it is no wonder the thing is crashing.

    this is a phone with limited memory, you cannot keep all copies of the image around.

    you have the event.media, the imageView you created, then the image from the toImage call and then you load a module to manipulate another copy of the image.

    — answered February 13th 2012 by Aaron Saunders
    permalink
    1 Comment
    • A few follow up points:

      • This is a "higher" end phone (HTC Sensation) that shows over 180 megs of free ram when I run the application.
      • I agree with the issue of having multiple copies of things in memory, I've tried nulling some things out but nothing seems to improve the issue. Fundamentally the need to create the image view to determine the height and width seems like an issue to me.
      • Sometimes the out of memory occurs on the initial creation of the image view.

      — commented February 13th 2012 by Frank A
  • The solution I'm using, compliments of iotashan, is to use this module which handles the images natively. https://github.com/yagitoshiro/ImageAsResized Thanks to the author as well, it's a life saver.

    — answered February 13th 2012 by Frank A
    permalink
    0 Comments
The ownership of individual contributions to this community generated content is retained by the authors of their contributions.
All trademarks remain the property of the respective owner.