Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Out of memory exception when resizing many bitmaps

i need to resize 5000 images and save them in a separate folder. I have a code like this to create a resized copy of a picture (found on the Internet):

Bitmap ResizeImage(System.Drawing.Image image, int width, int height)
       
 {
            var destRect = new System.Drawing.Rectangle(0, 0, width, height);
            var destImage = new Bitmap(width, height);

            destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);

            using (var graphics = Graphics.FromImage(destImage))
            {
                graphics.CompositingMode = CompositingMode.SourceCopy;
                graphics.CompositingQuality = CompositingQuality.HighQuality;
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = SmoothingMode.HighQuality;
                graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

                using (var wrapMode = new ImageAttributes())
                {
                    wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                    graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
                }
            }
            
            return destImage;
        }

and I have this code, which first creates all the necessary directories to save pictures, and then starts changing all the images and saving them to new folders. In the process of executing the program, my memory starts to fill until it reaches the mark of ~ 590mb (~ 1800 images), and then throws an OutOfMemoryException error.

void ResizeAllImages()
        {
            var files = Directory.GetFiles(ImagesDirectory, "*", SearchOption.AllDirectories);

            if (!Directory.Exists(ResizedImagesDirectory)) Directory.CreateDirectory(ResizedImagesDirectory);

            var dirs = Directory.GetDirectories(ImagesDirectory);
 
            foreach(var d in dirs) Directory.CreateDirectory(System.IO.Path.Combine(ResizedImagesDirectory, System.IO.Path.GetFileName(d)));

            foreach(var f in files)
            {
                using(var image = ResizeImage(System.Drawing.Image.FromFile(f), 224, 224))
                {
                    var imgName = System.IO.Path.GetFileName(f);

                    var dirName = System.IO.Path.GetFileName(System.IO.Path.GetDirectoryName(f));

                    image.Save(System.IO.Path.Combine(ResizedImagesDirectory, dirName, imgName), ImageFormat.Png);
                }
            }
        }

To solve this problem, I tried to add the following 2 lines of code to the foreach:

GC.Collect();
GC.WaitForPendingFinalizers();

The profiler started to show that during the entire program operation the process takes ~ 50mb, however, when the previous mark of ~ 1800 images is reached, I get an OutOfMemoryException again. How can I solve this problem. Thank you in advance

like image 501
fdg fggf Avatar asked Oct 15 '22 00:10

fdg fggf


1 Answers

According to MSDN, the OutOfMemoryException can be thrown in some non-intuitive situations. For example, it can be thrown in the Bitmap.Clone method:

OutOfMemoryException: rect is outside of the source bitmap bounds.

We can see that you are not using the Clone method, however you are using a Rect and a Bitmap, and since the memory footprint of your program does not surpass 50MB, we can deduce that the problem is similar.

To really fix this problem, you should first isolate the problematic image, verify exactly what is happening, and then proceed accordingly.

like image 189
Guilherme Avatar answered Oct 19 '22 02:10

Guilherme