7

I got some very large building drawings, sometimes 22466x3999 with a bit depth of 24, or even larger. I need to be able to resize these to smaller versions, and to be able to cut out sections of the image to smaller images.

I have been using the following code to resize the images, which I found here:

       public static void ResizeImage(string OriginalFile, string NewFile, int NewWidth, int MaxHeight, bool OnlyResizeIfWider)
        {
            System.Drawing.Image FullsizeImage = System.Drawing.Image.FromFile(OriginalFile);
            if (OnlyResizeIfWider)
            {
                if (FullsizeImage.Width <= NewWidth)
                {
                    NewWidth = FullsizeImage.Width;
                }
            }
            int NewHeight = FullsizeImage.Height * NewWidth / FullsizeImage.Width;
            if (NewHeight > MaxHeight)
            {
                NewWidth = FullsizeImage.Width * MaxHeight / FullsizeImage.Height;
                NewHeight = MaxHeight;
            }
            System.Drawing.Image NewImage = FullsizeImage.GetThumbnailImage(NewWidth, NewHeight, null, IntPtr.Zero);
            FullsizeImage.Dispose();
            NewImage.Save(NewFile);
        }

And this code to crop the images:

public static MemoryStream CropToStream(string path, int x, int y, int width, int height)
        {
            if (string.IsNullOrWhiteSpace(path)) return null;
            Rectangle fromRectangle = new Rectangle(x, y, width, height);
            using (Image image = Image.FromFile(path, true))
            {
                Bitmap target = new Bitmap(fromRectangle.Width, fromRectangle.Height);
                using (Graphics g = Graphics.FromImage(target))
                {
                    Rectangle croppedImageDimentions = new Rectangle(0, 0, target.Width, target.Height);
                    g.DrawImage(image, croppedImageDimentions, fromRectangle, GraphicsUnit.Pixel);
                }
                MemoryStream stream = new MemoryStream();
                target.Save(stream, image.RawFormat);
                stream.Position = 0;
                return stream;
            }
        }

My problem is that i get a Sytem.OutOfMemoryException when I try to resize the image, and that's because I can't load the full image in to FullsizeImage.

So what I would like to know, how do I resize an image without loading the entire image into memory?

Martin M
  • 463
  • 8
  • 20
  • This is not a programming solution but you could try increasing the *virtual memory* size of your machine and see. – Kurubaran Mar 13 '15 at 08:59
  • You should be working with LockBits for such image sizes – Vajura Mar 13 '15 at 08:59
  • @Kurubaran I tried to increase the memory size, which didn't work, and I don't think its the correct solution for a webproject. – Martin M Mar 13 '15 at 10:54

2 Answers2

5

There are chances the OutOfMemoryException is not because of the size of the images, but because you don't dispose all the disposables classes correctly :

  • Bitmap target
  • MemoryStream stream
  • System.Drawing.Image NewImage

are not disposed as they should. You should add a using() statement around them.

If you really encounter this error with just one image, then you should consider switch your project to x64. A 22466x3999 picture means 225Mb in memory, I think it shouldn't be an issue for x86. (so try to dispose your objects first).

Last but not least, Magick.Net is very efficient about resizing / cropping large pictures.

rducom
  • 7,072
  • 1
  • 25
  • 39
  • Thank you, I will add `using()` to the Fullsizeimage and the other places. I did try Magick.Net, but I couldn't get it to work, but i'll give it a try if this doesn't help. – Martin M Mar 13 '15 at 09:14
  • Image Magick.Net must be the way to go, because adding using's still returned a `OutOfMemoryException`. And if Image Magick.Net won't work, i'm going to make a separate service which handles all the image resizing and cropping.. – Martin M Mar 13 '15 at 10:32
  • 1
    Even on large 64bit system you can't create Bitmaps of arbitrary sizes. If he needs to work with really large Bitmaps I'm afraid that using a 3rd party lib is the best option.. – TaW Mar 13 '15 at 10:35
  • I feel like I need to do a followup on this issue, after adding a couple of usings, it ended up working. I think you were right, I was using the method multiple times after each other, and at some point I would get the exception. So thank you! Another problem might have been that its a web-app, so the memory might be limited by the IIS memory cap. – Martin M Feb 15 '18 at 07:20
1

You can also force .Net to read the image directly from disk and stop memory caching.

Use

sourceBitmap = (Bitmap)Image.FromStream(sourceFileStream, false, false);

Instead of

...System.Drawing.Image.FromFile(OriginalFile);

see https://stackoverflow.com/a/47424918/887092

Kind Contributor
  • 17,547
  • 6
  • 53
  • 70