Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Out of memory error while loading a Bitmap

I'm working with big size images (for ex. 16000x9440 px) and cut some regions for other things. I'm getting an exception "Out of memory" when create a new Bitmap instance:

using (FileStream fileStream = new FileStream(mapFileResized, FileMode.Open))
{
    byte[] data = new byte[fileStream.Length];
    fileStream.Read(data, 0, data.Length);
    using (MemoryStream memoryStream = new MemoryStream(data))
    {
        using (Bitmap src = new Bitmap(memoryStream)) // <-- exception
        {
            tile = new Bitmap(tileWidth, tileHeight, PixelFormat.Format24bppRgb);
            tile.SetResolution(src.HorizontalResolution, src.VerticalResolution);
            tile.MakeTransparent();
            using (Graphics grRect = Graphics.FromImage(tile))
            {
                grRect.CompositingQuality = CompositingQuality.HighQuality;
                grRect.SmoothingMode = SmoothingMode.HighQuality;
                grRect.DrawImage(
                        src,
                        new RectangleF(0, 0, tileWidth, tileHeight),
                        rTile,
                        GraphicsUnit.Pixel
                );
            }
        }
    }
}

When I use small image sizes (for ex. 8000x4720 px) then all work fine.

How can I work with big size images?

PS tile Bitmap is disposed in finally block.

Best regards, Alex.

like image 848
Alex M Avatar asked Jun 25 '10 10:06

Alex M


3 Answers

You are using about a gigabyte of ram, not very suprising that you run out of memory.

Assuming you use a 32bpp Fileformat with 16000x9440 pixel you get a filesize of about:

16000 * 9440 * (32/8) = ~576MB

byte[] data = new byte[fileStream.Length];
fileStream.Read(data, 0, data.Length);
using (MemoryStream memoryStream = new MemoryStream(data))
{
  [... snip ...]
}

You load the whole File into a memory stream, this requires 576MB.

[... snip ...]
    using (Bitmap src = new Bitmap(memoryStream)) // <-- exception
    {
        [... snip ...]
    }
[... snip ...]

You load the whole stream contents into a bitmap, this requires at least another 576MB (depending on how much memory the bitmap requires per pixel, should be at least 4, propably more). At that point you have the image twice in memory which seriously hurts for such big images.

You can reduce the memory footprint by getting rid of the memory stream and loading the bitmap directly from the file stream.

Another solution would be to load only a part of the bitmap and load the other parts on-demand (much like google maps), but i can't help you with that solution, might require reading the bitmap manually.

like image 69
Morfildur Avatar answered Sep 22 '22 19:09

Morfildur


Not a complete answer to your question, but you are probably better of using a library like ImageMagick.NET

like image 22
Maurits Rijk Avatar answered Sep 25 '22 19:09

Maurits Rijk


MemoryStream is implemented using an array of bytes that stores the data. If you read more data than the array can hold, a new array of double size is allocated and the bytes copied from one array to the other.

Since you apparently know how much data you're going to need, you can allocated the correct size up front and thus avoid the resizing.

However, once you reach a certain size you will run out of memory. .NET imposes a 2 GB limit on a single object (even on 64 bit), so the internal array in MemoryStream will never be able to grow beyond that. If your image is larger than that you'll get an out of memory exception.

like image 24
Brian Rasmussen Avatar answered Sep 25 '22 19:09

Brian Rasmussen