5

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

user2864740
  • 60,010
  • 15
  • 145
  • 220
fdg fggf
  • 53
  • 4
  • You should not instanciate Bitmap each time but use the same. So you will work only with the same internal buffer. Hence you need to refactor the `ResizeImage` to optimize by using its code directly in your loop or pass the bitmap instance as parameter. You should do the same thing with the Graphics. –  Sep 10 '20 at 15:54
  • @user2864740 the problematic place is foreach in block 2, you can pay attention only to it – fdg fggf Sep 10 '20 at 15:57
  • Shouldn’t disposing of the Bitmap (and thus ideally releasing any system resources) sufficient to avoid any OOM, regardless of efficiency? – user2864740 Sep 10 '20 at 15:58
  • @user2864740 the using construct itself calls the dispose method – fdg fggf Sep 10 '20 at 15:59
  • To reduce the problem: I suspect it’s repeatable without actually doing any resizing: eg. just creating a new image and copying in the data, any maybe not even that.. by eliminating all code not required the problematic code can be identified. – user2864740 Sep 10 '20 at 15:59
  • And yes, I know about using - that was to the previous comment :) – user2864740 Sep 10 '20 at 16:00
  • @asawyer yes, I got all the information about the used memory from it – fdg fggf Sep 10 '20 at 16:02
  • 1
    @OlivierRogier OK, I'll try, but still, where does the memory leak occur, if after creating an object I immediately delete it – fdg fggf Sep 10 '20 at 16:07
  • I believe there is something more obscure involved than a memory leak. 50 MB is very far from OutOfMemoryException, even in 32 bit runtimes. – Guilherme Sep 10 '20 at 16:09
  • For example, take a look [here](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.clone?redirectedfrom=MSDN&view=dotnet-plat-ext-3.1#System_Drawing_Bitmap_Clone_System_Drawing_Rectangle_System_Drawing_Imaging_PixelFormat_). It seems that the OutOfMemoryException means that the rect is out of bounds from the Bitmap. – Guilherme Sep 10 '20 at 16:11
  • You may want to try to separate the first 1500 images in another directory, and then copy and paste it more than one time to create some volume (say 6000 images). Then run this program to see if it will break. If it doesn't throw, then you know there is a problematic image on the other directory. – Guilherme Sep 10 '20 at 16:13
  • @Guilherme I was trying to take the first 100 images and use my code on them. everything works perfectly – fdg fggf Sep 10 '20 at 16:15
  • @Guilherme Thank you very much, it turns out that there was still a problem image. I deleted it and everything worked as it should – fdg fggf Sep 10 '20 at 16:24
  • Sure, I will post an answer so everyone can see it easily. – Guilherme Sep 10 '20 at 16:25

1 Answers1

3

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.

Guilherme
  • 5,143
  • 5
  • 39
  • 60
  • 2
    Out of curiosity, How were you able to land on 'OutOfMemoryException' exception of Bitmap.Clone() if the method was not called anywhere in the troublesome code. – Durga Prasad Sep 10 '20 at 16:48
  • 1
    I started to look away from the common cause of the `OutOfMemoryException` because of the 50MB of footprint. Then, I searched "bitmap OutOfMemoryException" and found [this](https://stackoverflow.com/questions/4318563/c-sharp-out-of-memory-when-creating-bitmap). Look at the second answer. – Guilherme Sep 10 '20 at 16:53
  • 1
    Nice, that was smart. Learnt something new today for problem solving. – Durga Prasad Sep 10 '20 at 16:58