3
votes

I am developing an image manipulation application which has to be able to work on large images e.g. those taken by a camera, the approach I'm taking is to split the source image into multiple suitably sized tiles so that the tiles can be loaded individually into memory as a Bitmap without exceeding the dreaded VM limit, next image manipulation is performed on a tile by tile basis, this is all well and good but until I want to stitch these tiles back to a final jpeg image.
I'm looking for a built in sdk api or free ware solution that can perform the following:

  • Open jpeg output file as output stream
  • Stream RGB pixels from bitmap (tile 1) to jpeg output stream
  • Stream RGB pixels from bitmap (tile 2) to jpeg output stream
  • etc.. for all tiles
  • Close jpeg output stream
  • Any ideas or pointers other than writing my own jpeg encoder?

    2
    Just out of curiosity, how do you slice the images in the beginning?Dan S
    I use BitmapRegionDecoder with a Rect indicating the region of the source (big bitmap/jpeg) that I want to grab, e.g. if my source image is 2000x2000 pixels and I want 4 tiles the first tile would be (0,0)-(1000,1000), second would be (1000,0)-(2000,1000), third (0,1000)-(1000,2000) and fourth (1000,1000)-(2000,2000). I process these one by one and save off to temporary bitmaps on the sdcard, the problem is then stitching these back to form a final image (preferably a jpeg).Daryl Hurst

    2 Answers

    0
    votes

    What about this:

    FileOutputStream outFile = new FileOutputStream(output_file);
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outFile);
    outFile.flush();
    outFile.close();
    

    You can adjust the quality setting (100). See the docs

    You'll still need to decide how to slice it. But if you're taking the image on the device, why would it be a problem loading the whole image if it didn't cause a memory problem in the first place?

    Stitching Tiles back together: Here's one simple approach used by slippy maps the I know works.

    1. Cut tiles in standard size (say 256x256)
    2. Assign each tile an x,y value - this could be a directory structure or filename convention: i.e. "0_0.jpg"
    3. Tile names (numbers) are related to their top/left pixel position.

    Each tile number can be calculated by the following:

    tileX = floor(pixelX / 256)

    tileY = floor(pixelY / 256)

    So the tile at pixels 0,0 = tile (0,0), the tile at pixels 256,0 is tile (1,0), etc...

    0
    votes

    Your stitching approach has two major issues and will ultimately fail. If you ask the system to decode a JPEG file in parts, the decode time will end up being close to N times longer (N = number of parts). The next problem is that when trying to save the image you will run out of memory. JPEG images need to be compressed in one shot. If your entire uncompressed image can't fit in memory, then you won't be able to use the technique you're using.

    The hard truth is that, with Android as it is currently designed, you must use native code to hold on to the bitmap in order to manage an image larger than the VM memory limit. It's not that difficult, but it does require a re-design of your app.