当调整大小许多位图时,会出现内存不足异常

5

我需要调整大小5000张图像,并将它们保存在一个单独的文件夹中。我有一段代码可以创建图片的缩小副本(从互联网上找到的):

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;
        }

我有这段代码,首先创建所有必要的目录来保存图片,然后开始更改所有的图片并将它们保存到新的文件夹中。在执行程序的过程中,我的内存开始填充,直到达到大约590MB(1800张图片)的标记,然后抛出OutOfMemoryException错误。

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);
                }
            }
        }

为解决这个问题,我尝试在foreach中添加以下2行代码:
GC.Collect();
GC.WaitForPendingFinalizers();

性能分析器显示,在整个程序操作期间,该进程占用的内存大约为50MB,但是当达到前一个标记(约1800张图像)时,又出现了OutOfMemoryException异常。请问如何解决这个问题。谢谢!


你不应该每次实例化 Bitmap,而是使用同一个实例。这样你只需处理相同的内部缓冲区。因此,你需要重构 ResizeImage 方法,通过直接在循环中使用其代码或将位图实例作为参数传递来进行优化。Graphics 也应该采取同样的做法。 - user12031933
@user2864740,有问题的地方在第二个块中的foreach循环,你可以只关注它。 - fdg fggf
1
@OlivierRogier 好的,我会尝试,但是如果我创建一个对象后立即删除它,那么内存泄漏会发生在哪里? - fdg fggf
例如,查看此处。似乎OutOfMemoryException意味着矩形超出了位图的边界。 - Guilherme
你可以尝试将前1500张图像分离到另一个目录中,然后复制粘贴多次以创建一些数量(比如6000张图像)。然后运行此程序以查看是否会出现错误。如果没有报错,那么你就知道另一个目录中存在有问题的图像。 - Guilherme
显示剩余9条评论
1个回答

3
根据 MSDN 文档,OutOfMemoryException 异常可能在一些非直观的情况下被抛出。例如,Bitmap.Clone 方法中可能会抛出该异常:

OutOfMemoryException: rext 超出源位图边界。

我们可以看到你没有使用 Clone 方法,但你正在使用 Rect 和 Bitmap。由于程序的内存占用量未超过 50MB,我们可以推断问题类似。
为了真正解决这个问题,你应该首先确定有问题的图像,确切地验证发生了什么,然后再相应地处理。

2
出于好奇,如果在有问题的代码中没有调用Bitmap.Clone()方法,你是如何遇到“OutOfMemoryException”异常的? - Durga Prasad
1
我开始不再关注因为50MB的占用而导致的“OutOfMemoryException”的常见原因。然后,我搜索了“bitmap OutOfMemoryException”,并找到了这个。请看第二个答案。 - Guilherme
1
不错,那很聪明。今天学到了解决问题的新方法。 - Durga Prasad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接