简体   繁体   English

保存多个图像后,C#GDI +一般错误/外部异常

[英]C# GDI+ generic error / external exception after saving multiple images

I have a little console application in C# that takes a pair of pictures from a folder (containing a few hundred images) in .jpg-format, merges them to one picture and stores the result as .tif in another folder. 我在C#中有一个小的控制台应用程序,它从.jpg格式的文件夹(包含几百个图像)中获取一对图片,将它们合并到一张图片中,并将结果作为.tif存储在另一个文件夹中。 This process is repeated for all pictures in the source folder. 对源文件夹中的所有图片重复此过程。

It works fine for a few loop iterations, but then I get an unhandled external exception with an GDI+ generic error (the inner exception is null) when trying to save the result. 它适用于几个循环迭代,但在尝试保存结果时,我得到一个未处理的外部异常,带有GDI +泛型错误(内部异常为null)。 As I create new files for storing and as it works for a few pictures before crashing, I dont think this is caused by some permission problem or a lock on a file. 当我创建用于存储的新文件并且在崩溃之前它为几张图片工作时,我不认为这是由某些权限问题或文件锁定引起的。 I suspect it might be some memory issue, because when I use 24bit (Format24bppRgb) for the .tif, it crashes after approx 13 iterations, when I use 48bit (Format48bppRgb) it crashes after approx 8 iterations. 我怀疑这可能是一些内存问题,因为当我使用24位(Format24bppRgb)作为.tif时,它会在大约13次迭代后崩溃,当我使用48位(Format48bppRgb)时它会在大约8次迭代后崩溃。

The current image to be saved when it crashes is already present on the disk as an empty .tif of less than 1KB. 崩溃时要保存的当前映像已作为空白.tif小于1KB存在于磁盘上。 The application is not run from Visual Studio but directly from the .exe (as I have read of memory problems when creating large images in an application running from within Visual Studio) 应用程序不是从Visual Studio运行,而是直接从.exe运行(因为我在Visual Studio中运行的应用程序中创建大图像时已经读过内存问题)

The sizes of the source images range from 800x600 to 24 megapixels. 源图像的大小范围为800x600到2400万像素。 Result sizes can be larger, as when a landscape oriented image is combined with a portrait oriented picture it results in a square image with the greater width and height of the source images. 结果大小可以更大,因为当横向方向图像与纵向图像组合时,它产生具有更大宽度和高度的源图像的方形图像。 The machine where I run the application has 8GB of RAM, of which at max approx. 我运行应用程序的机器有8GB的RAM,其中最大约为。 50% is used. 使用50%。

The loop looks like this: 循环看起来像这样:

  foreach (var f in files)
        {
            if ((count % 2) == 0)
            {
                bmp1 = new Bitmap(f);
                int index2 = Array.IndexOf(files, f) + 1;
                if (index2 >= files.Length) { break; }
                bmp2 = new Bitmap(files.ElementAt(index2));
                Merge2Rand(bmp1, bmp2,rand).Save(outputDir + "\\0\\out" + count + ".tif", myImageCodecInfo, myEncoderParameters);
                Console.WriteLine(count + " - " + watch.ElapsedMilliseconds / 60000 + "min");
            }
            count++;
        }

This is the merging function: 这是合并功能:

 public static Bitmap Merge2Rand(Bitmap bmp1, Bitmap bmp2, Random rand)
    {
        int newH = Math.Max(bmp2.Height, bmp1.Height);
        int newW = Math.Max(bmp2.Width, bmp1.Width);

        int offsetH1 = 0;
        if (bmp1.Height < newH) offsetH1 = rand.Next(0, (newH - bmp1.Height) + 1);
        int offsetW1 = 0;
        if (bmp1.Width < newW) offsetW1 = rand.Next(0, (newW - bmp1.Width) + 1);
        int offsetH2 = 0;
        if (bmp2.Height < newH) offsetH2 = rand.Next(0, (newH - bmp2.Height) + 1);
        int offsetW2 = 0;
        if (bmp2.Width < newW) offsetW2 = rand.Next(0, (newW - bmp2.Width) + 1);

        Bitmap newBMP = new Bitmap(newW, newH, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
        for (int i = 0; i < newW; i++)
        {
            for (int j = 0; j < newH; j++)
            {
                Color p1;
                Color p2;

                if ((i>=offsetW1 && bmp1.Width+offsetW1 > i+offsetW1 ) && (j>=offsetH1 && bmp1.Height+offsetH1 > j+offsetH1))
                {
                    p1 = bmp1.GetPixel(i, j);
                }
                else
                {

                    p1 = Color.FromArgb(0, 0, 0, 0);
                }
                if ((i>=offsetW2 && bmp2.Width + offsetW2 > i +offsetW2) && (j>=offsetH2 && bmp2.Height + offsetH2 > j +offsetH2))
                {
                    p2 = bmp2.GetPixel(i, j);
                }
                else
                {
                    /
                    p2 = Color.FromArgb(0, 0, 0, 0);
                }
                int rVal = (p1.R + p2.R) / 2;
                int gVal = (p1.G + p2.G) / 2;
                int bVal = (p1.B + p2.B) / 2;
                newBMP.SetPixel(i, j, Color.FromArgb(0, rVal, gVal, bVal));
            }
        }
        return newBMP;

I use the following encoding for the .tif files: 我对.tif文件使用以下编码:

ImageCodecInfo myImageCodecInfo;
        myImageCodecInfo = GetEncoderInfo("image/tiff");
        System.Drawing.Imaging.Encoder myEncoder;
        myEncoder = System.Drawing.Imaging.Encoder.Compression;
        EncoderParameters myEncoderParameters;
        myEncoderParameters = new EncoderParameters(1);
        EncoderParameter myEncoderParameter;
        myEncoderParameter = new EncoderParameter(myEncoder, (long)EncoderValue.CompressionLZW);
        myEncoderParameters.Param[0] = myEncoderParameter;



private static ImageCodecInfo GetEncoderInfo(String mimeType)
        {
            int j;
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }

The problem appears to be that you are repeatedly creating new bitmaps in each iteration and never disposes of them. 问题似乎是您在每次迭代中重复创建新的位图,而不会丢弃它们。 This will result in a leak of GDI or USER Objects and memory as well. 这将导致GDI或USER对象和内存泄漏。 The using statement automatically disposes of objects when your code is done with them. using语句在代码完成后自动处理对象。

You need something like this (should be close): 你需要这样的东西(应该很接近):

foreach (var f in files)
{
    using (Bitmap bmp1 = new Bitmap(f))
    {
        int index2 = Array.IndexOf(files, f) + 1;
        if (index2 >= files.Length) { break; }

        using (Bitmap bmp2 = new Bitmap(files.ElementAt(index2)),
                bmp3 = Merge2Rand(bmp1, bmp2, rand))
        {
            bmp3.Save(outputDir + "\\0\\out" + count + ".tif", myImageCodecInfo, myEncoderParameters);
        }
    }
}

The using block for bmp3 is slight overkill, it could simply be declared and then .Dispose() after the save. bmp3using块有点矫枉过正,可以简单地声明,然后在保存后使用.Dispose() As is, it shows how to "stack" 2 objects in one using block. 就像它一样,它展示了如何using块“堆叠”一个对象。

If something has the .Dispose() method it means it should be disposed of when your code is done with it. 如果某些东西有.Dispose()方法,则意味着当你的代码完成时它应该被处理掉。 It also means you can use it in a using block to automatically dispose of it at the end. 意味着你可以使用它在using块结束时自动处置。

See also C# using statement 另请参见C#using语句

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM