【问题标题】:C# Graphics.DrawImage() throws out of memory exception when trying to combine multiple imagesC# Graphics.DrawImage() 尝试组合多个图像时抛出内存不足异常
【发布时间】:2021-05-01 06:51:29
【问题描述】:

我正在尝试将多张图片合并为一张。这是我现在的代码:

    public static Bitmap Merge(List<Image> imgs)
    {
        int outputImageWidth = 0;
        int outputImageHeight = 0;

        foreach (var img in imgs)
        {
            if(outputImageWidth < img.Width)
            {
                outputImageWidth = img.Width;
            }
            outputImageHeight += img.Height;
        }

        outputImageHeight += 1;

        using (Bitmap outputImage = new Bitmap(outputImageWidth, outputImageHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
        {
            foreach (var img in imgs)
            {
                Graphics graphics = Graphics.FromImage(outputImage);
                graphics.DrawImage(img, new Rectangle(new Point(0, outputImage.Height + 1), img.Size), new Rectangle(new Point(), img.Size), GraphicsUnit.Pixel);
                graphics.Dispose();
                graphics = null;
                img.Dispose();
            }
            foreach (var img in imgs)
            {
                img.Dispose();
            }
            GC.Collect();

            return (Bitmap)outputImage.Clone();
        }
    }

在调试时,我发现,每当我调用graphics.DrawImage(...) 时,都会分配大约 100-300mb 的内存。我希望每当我处理对象时它都会被释放,但是每次迭代时,进程都会分配更多内存,直到我得到内存不足异常(大约在 32 位进程的 30 页之后)。 有什么想法吗?

【问题讨论】:

  • 您对Dispose 的使用对我来说似乎有点奇怪(循环中的双重处理)。我不认为有问题,但很奇怪。另外,为什么要克隆输出图像然后处理它?为什么不直接返回而不克隆(这样可以避免一些内存压力)。
  • 您应该在循环外创建图形对象(并将其放在using(...) 中),然后重用它。
  • 请告诉我们有多少图像以及结果的宽度/高度! - 顺便说一句:源图像来自一个列表,因此它们已经被分配并且很可能不是问题。创建一个拥抱位图通常是浪费/泄漏 GDI 资源..
  • @TaW 除非我误读了算法,否则它会垂直堆叠 N 个图像(它们的高度之和),宽度是所有图像宽度的最大值。
  • 121000 确实很多,可能太多了。它必须被创建为连续内存。 GDI 资源不是你的问题,因为每个位图只会用完一个,所以你有 list.count +1 加上一些额外的资源。一种解决方法可能是创建 2 或 3 个较小的图像,并找到一种无需 GDI 将它们拼接在一起的方法;也许一个库会有所帮助。在 winforms 和 GDI+ 中搜索位图大小限制!

标签: c# .net


【解决方案1】:

试试这个

public static Bitmap Merge(List<Image> imgs)
{
    ...

    var outputImage = new Bitmap(outputImageWidth, outputImageHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb));
    using (var graphics = Graphics.FromImage(outputImage))
    {
        foreach (var img in imgs)
        {
            using (img)
                graphics.DrawImage(img, new Rectangle(new Point(0, outputImage.Height + 1), img.Size), new Rectangle(new Point(), img.Size), GraphicsUnit.Pixel);                 
        }

        return outputImage;
    }
}

这个

  • 具有using 块的特性,允许始终调用Dispose,即使在过程中抛出异常也是如此
  • 在整个过程中使用单个 Graphics
  • 不分配(巨大的)位图两次

当然,一旦完成,您必须在此方法之外处理生成的位图。

【讨论】:

  • 我试过你的解决方案,现在图像似乎创建成功了,但我不得不将它保存为文件,因为使用,返回图像后它被处理了。现在在 outputimage.Save(path);我收到“GDI+ 中的一般错误”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-14
  • 2011-06-18
  • 2015-02-19
  • 1970-01-01
相关资源
最近更新 更多