问题描述
我在C#中有一个小的控制台应用程序,它从.jpg格式的文件夹(包含几百个图像)中获取一对图片,将它们合并到一张图片中,并将结果作为.tif存储在另一个文件夹中。对源文件夹中的所有图片重复此过程。
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. This process is repeated for all pictures in the source folder.
它适用于一些循环迭代,但随后我得到一个未处理的外部异常,带有GDI +泛型错误(尝试保存结果时,内部异常为null)。当我创建用于存储的新文件并且在崩溃之前它用于几张图片时,我不认为这是由某些权限问题或文件锁定引起的。我怀疑这可能是一些内存问题,因为当我使用24位(Format24bppRgb)作为.tif时,它会在大约13次迭代后崩溃,当我使用48位(Format48bppRgb)时它会在大约8次迭代后崩溃。
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. 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.
崩溃时要保存的当前图像已作为空白.tif小于1KB存在于磁盘上。应用程序不是从Visual Studio运行,而是直接从.exe运行(因为我在Visual Studio中运行的应用程序中创建大图像时已经读过内存问题)
The current image to be saved when it crashes is already present on the disk as an empty .tif of less than 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)
源图像的大小范围为800x600到2400万像素。结果大小可以更大,因为当横向方向图像与纵向图像组合时,它产生具有更大宽度和高度的源图像的方形图像。我运行应用程序的机器有8GB的RAM,其中最大约为。使用50%。
The sizes of the source images range from 800x600 to 24 megapixels. 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. 50% is used.
循环如下所示:
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++;
}
这是合并函数:
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;
我对.tif文件使用以下编码:
I use the following encoding for the .tif files:
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;
}
推荐答案
问题似乎是您在每次迭代中反复创建 new 位图,而不会丢弃它们。这将导致GDI或用户对象和内存泄漏。 使用
语句会在代码完成后自动处理对象。
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. The using
statement automatically disposes of objects when your code is done with them.
你需要这样的东西(应该接近):
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);
}
}
}
使用
块 bmp3
稍微有点过分,可以简单地声明,然后 .Dispose()
保存后。这样,它显示了如何使用块在一个中堆叠2个对象。
The
using
block for bmp3
is slight overkill, it could simply be declared and then .Dispose()
after the save. As is, it shows how to "stack" 2 objects in one using
block.
如果有什么东西有
.Dispose()
方法意味着在代码完成后它应该被处理掉。它也意味着您可以在中使用
块在最后自动处理它。
If something has the
.Dispose()
method it means it should be disposed of when your code is done with it. It also means you can use it in a using
block to automatically dispose of it at the end.
另请参阅
这篇关于保存多个图像后,C#GDI +一般错误/外部异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!