12

I am joining two images using the code below but it throws an OutOfMemory error my images are around 1MB each.

private Bitmap overlayMark(String first, String second)
{
    Bitmap bmp1, bmp2;
    bmp1 = BitmapFactory.decodeFile(first);
    bmp2 = BitmapFactory.decodeFile(second);
    if (bmp1 == null || bmp2 == null)
        return bmp1;

    int height = bmp1.getHeight();
    if (height < bmp2.getHeight())
        height = bmp2.getHeight();

    Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth() + bmp2.getWidth(), height,
            Bitmap.Config.ARGB_8888);// Out of memory
    Canvas canvas = new Canvas(bmOverlay);
    canvas.drawBitmap(bmp1, 0, 0, null);
    canvas.drawBitmap(bmp2, bmp1.getWidth(), 0, null);
    bmp1.recycle();
    bmp2.recycle();
    return bmOverlay;
}

Update: I tried below two answers but it still not allwoing me to create bitmap of such big size the problem is that the resultant bitmap is too large in size around 2400x3200 so its going out of memory.

How can I join large images without running out of memory?

Octavian Helm
  • 39,405
  • 19
  • 98
  • 102
ingsaurabh
  • 15,249
  • 7
  • 52
  • 81
  • Where is the "out of memory" coming from - the BitmapFactory.decodeFile()? – dbryson Jun 02 '11 at 11:16
  • on this line Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth() + bmp2.getWidth(), height, Bitmap.Config.ARGB_8888); – ingsaurabh Jun 02 '11 at 11:25
  • Before creating your new bitmap, why don't you compress the two bitmaps and try. This will reduce the memory much more better – Andro Selva Jun 27 '11 at 05:40
  • @Andro_Selva I can't do that because images are to be zoomed and doing this will reduce the quality – ingsaurabh Jun 27 '11 at 07:02
  • Could you try converting the bmp to a png? Then it can be compressed to a much smaller size but still not lose any quality. Then, if the Java API is smart about it and lets you still access the RGB color value of each pixel without actually expanding it into a BMP array, you could construct a BMP file manually and stream it out to a file. This way you could have all the data you need in memory at one time, and you could buffer and stream out your generated image a few KB at a time. – Loduwijk Jun 29 '11 at 23:51
  • In addition to solution below I tried a non conventional method of calling System.gc and setting the bitmap to null hope somebody get help – ingsaurabh Jul 22 '11 at 11:02

7 Answers7

13

Without loading the image into memory, you CAN get the size of the image, using inJustDecodeBounds. The Bitmap returns null, but all the parameters are set. You can scale down the image accordingly.

If your JPEG images are 1 MiB each, conversion to a BMP will take a lot of memory indeed. You can easily calculate its BMP equivalent by the dimensions of the image. Conversion of such a large image is expected to crash indeed. Android limits its apps to 16 MiB VM only.

Also use RGB_565 instead of ARGB_8888.

So your only solution is: (a) To use BitmapFactory.Options.inSampleSize to scale down the image or (b) Use Android NDK where the 16 MiB limit isn't there.

Shumon Saha
  • 1,415
  • 6
  • 16
  • 33
  • hmmm nice thought but how am I going to draw the bitmap if I dont load it in memory and I fear of using NDK :P well I am waiting for a good response – ingsaurabh Jun 02 '11 at 12:27
  • 1
    Use inJustDecodeBounds=true to find the dimensions of the image without opening the image. Set the inSampleSize parameter accordingly. Then set inJustDecodeBounds=false. Then finally open the image. – Shumon Saha Jun 02 '11 at 12:36
  • Hi I tried your suggestions and updated the question can you suggest me any pointer how to join using ndk – ingsaurabh Jun 15 '11 at 10:19
  • Nope sorry, I am not that well versed with Android. I have no idea about using NDK. – Shumon Saha Jun 15 '11 at 16:58
  • Although It doesn't solves my problem but your answer will help out many problems so I am accepting your answer Thanks – ingsaurabh Jul 01 '11 at 04:51
5

I use this simple rule of the thumb: the heavy lifting (both memory/CPU) is done on the server.

So write some servlet that takes the image, resizes it to a specified dimension (probably reduces the pixel depth too) and returns the result.

Piece of cake and it works on any mobile device you need.

Good luck!

bestsss
  • 11,796
  • 3
  • 53
  • 63
  • I know all the good practices and most of the times follow them but client doesnt want to resize images or reduce the pixel quality – ingsaurabh Jun 30 '11 at 04:35
  • well, you do not need to delete the original after sending to the server. Just make the "thumbnail", send it back and use it the same way you'd do if you have created the resized image on the android device. Point is: just use a server to carry the task. – bestsss Jun 30 '11 at 06:30
  • I am sorry if I am not clear I dont want any thumbnail I just want to join two large images and if I do that on server side then also when I try to to display this image it goes OOM – ingsaurabh Jun 30 '11 at 06:35
  • I understood what you need, yet join the images on the server but also create the small image to display (properly resized), the small image is simply created to prevent the OOM which addresses exactly the question. The large one is for your customer. – bestsss Jun 30 '11 at 06:38
  • Actually the app is only about to show images to user through my app user can not view images apart from my app and in my app images can be zoomed in/out so I cant resize – ingsaurabh Jun 30 '11 at 06:45
4

I think a solution sort of like Sumon suggests might work.

  • Figure out the size of the final image based on what will fit on the screen.
  • Get the size of the first image using the inJustDecodeBounds technique. Figure out the size of the first image in the final image. Calculate re-sizing parameters.
  • Resize image, loading into memory.
  • Write resized image back to disk. Recycle the bitmap. (This will help when resizing the 2nd image)
  • Repeat for the second image, only you can skip the writing to disk part.
  • Load first image.

If you only need to display, then just do that. If not then you can combine into a single bitmap at this point and write to disk. If this is the case, it may be difficult because you wil have essentially 2x the screen size in memory. In that case I would recommend resizing smaller. If you can't go smaller, then you will have to go the NDK route, thought I'm not sure how much that will help. Here's an amusing intro to the NDK and JNI. Finally, I would highly recommend developing this using a phone running Android 2.3+ since its use of heap-allocated bitmaps will make debugging much easier. More about those here.

Community
  • 1
  • 1
michaelg
  • 2,692
  • 1
  • 17
  • 16
  • First of all I dont have to resize the image at all that will lose the quality apart from resize I had already implemented his method but of no use and when I create an empty bitmap of equal to size of both bitmap at that line I get OOM, I am also trying to go with NDK but I have no knowledge in C any link might be helpful – ingsaurabh Jun 28 '11 at 04:32
3

It's not necessary that the space taken by in-memory representation of bitmaps correspond closely with file size. So even if you have 3mb memory available to jvm, you might still get OutOfMemoryException.

Your code is creating three in-memory images simultaneously. If you can find the size of both images without reading the complete files, you can modify the code in a way to have only one of the source images in memory at a time. If even that doesn't prove to be sufficient you might need some sort of streaming method of reading the images.

Tahir Akhtar
  • 11,385
  • 7
  • 42
  • 69
  • Its like that without loading into memory you will not get the size of images so I have to do this what I can do is after darwing first bitmap remove it but that seems to make no much diff – ingsaurabh Jun 02 '11 at 11:40
  • 1
    See Sumon's answer for getting the image size without reading the whole image – Tahir Akhtar Jun 02 '11 at 12:42
  • Another trick could be to draw just the part of the image that user can see at a time (taking the screen size into consideration). Then handle scroll events to load more on demand simultaneously releasing the memory for the portion that have scrolled off the screen. – Tahir Akhtar Jun 02 '11 at 12:45
  • earlier I have to implement pdf instead of images and this approach will tend to lead me the same hectic approach as required in pdf rendering I already tasted the pain of doing this however I found a solution that worked for me and I testing it with more heavy sizes to see if that works for me – ingsaurabh Jun 02 '11 at 12:49
3

you may get some idea from here.

Community
  • 1
  • 1
Sandy
  • 6,285
  • 15
  • 65
  • 93
3

Are you trying to display this super large image or are you just trying to save it?

  1. If your trying to display it. Cut the images into tiles. Then only display the tiles that are being viewed. If the user zooms out you need to reduce the size of the bitmap before showing the whole thing.

  2. If your trying to save it, try saving it in sections to the same file by cutting the image up.

Loading 2 1m files in memory then creating a 2m file leaves you with 4M in memory for your images alone. Dynamically loading and unloading the memory solves this issue similar to tiles on Google maps or dynamic zooming in other map oriented solutions.

Chris Lucian
  • 1,013
  • 6
  • 15
3

If you need to return that huge 2400x3200 bitmap as your result, there is no way to actually realize this goal. The reason is that 2400*3200*4 bytes ~ 30 Mb! How can you hope to implement this method, when even you can't even fit the return value into your limited heap space (ie 16Mb)?

And even if you used 16-bit color, it would still fail because you would end up using about 15MB, which would not leave you enough space for the language run time.

Mikola
  • 9,176
  • 2
  • 34
  • 41
  • I agree with you but I dont understand why android devices are powered with such Hi-res camera that will produce images of 3-5 MB if they doesnt support in App development – ingsaurabh Jun 28 '11 at 04:40
  • @Saurabh: You got me, but the math works out that you can't do what you are trying to do there. Now you could fake by compressing the images in memory, but it would be expensive. I guess the work around is to use the native SDK instead. – Mikola Jun 28 '11 at 04:41
  • Yeah most of guys here suggested me NDK path but I have no exp in C that too in Linux env. however resizing is not an option as the client is too specific about the quality of images, if you can provide me any pointer on doing this thing in NDK that will be great – ingsaurabh Jun 28 '11 at 04:43
  • Well, the other thing you could do is chop the images up and page them out to the device's flash card. It would be more complex, but you might be able to acheive the same effect. Also if you roll your own bitmap routines you could stream out the file (but wouldn't be able to render). All of these things are nasty though. Anyway, good luck! – Mikola Jun 28 '11 at 04:45
  • Thanks Mikola already dealt with the suggested problem earlier instead of bitmaps I have to display PDF's I tried to my level best using all techniques but this approach is really messy and too tricky and buggy also :( – ingsaurabh Jun 28 '11 at 04:57