Dyl*_*est 0 c# asp.net tiff image-processing out-of-memory
首先,我将 tiff 的帧保存到位图列表中:
public Bitmap SaveTiffAsTallPng(string filePathAndName)
{
byte[] tiffFile = System.IO.File.ReadAllBytes(filePathAndName);
string fileNameOnly = Path.GetFileNameWithoutExtension(filePathAndName);
string filePathWithoutFile = Path.GetDirectoryName(filePathAndName);
string filename = filePathWithoutFile + "\\" + fileNameOnly + ".png";
if (System.IO.File.Exists(filename))
{
return new Bitmap(filename);
}
else
{
List<Bitmap> bitmaps = new List<Bitmap>();
int pageCount;
using (Stream msTemp = new MemoryStream(tiffFile))
{
TiffBitmapDecoder decoder = new TiffBitmapDecoder(msTemp, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
pageCount = decoder.Frames.Count;
for (int i = 0; i < pageCount; i++)
{
System.Drawing.Bitmap bmpSingleFrame = Worker.BitmapFromSource(decoder.Frames[i]);
bitmaps.Add(bmpSingleFrame);
}
Bitmap bmp = ImgHelper.MergeImagesTopToBottom(bitmaps);
EncoderParameters eps = new EncoderParameters(1);
eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 16L);
ImageCodecInfo ici = Worker.GetEncoderInfo("image/png");
bmp.Save(filename, ici, eps);
return bmp;
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后我将该位图列表传递给一个单独的函数来进行实际的组合:
public static Bitmap MergeImagesTopToBottom(IEnumerable<Bitmap> images)
{
var enumerable = images as IList<Bitmap> ?? images.ToList();
var width = 0;
var height = 0;
foreach (var image in enumerable)
{
width = image.Width > width
? image.Width
: width;
height += image.Height;
}
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format16bppGrayScale);
Graphics g = Graphics.FromImage(bitmap);//gives Out of Memory Exception
var localHeight = 0;
foreach (var image in enumerable)
{
g.DrawImage(image, 0, localHeight);
localHeight += image.Height;
}
return bitmap;
}
Run Code Online (Sandbox Code Playgroud)
但我通常会收到内存不足异常,具体取决于 tiff 中的帧数。即使只有 5 张大约 2550 像素 x 3300 像素的图像也足以导致错误。这只有大约 42MB,最终保存为 png,总共 2,550 像素乘 16,500 像素,磁盘上只有 1.5MB。我什至在我的 web.config 中使用这个设置:<gcAllowVeryLargeObjects enabled=" true" />其他详细信息:我正在使用 16GB RAM 的 64 位 Windows 7(我通常以大约 65% 的 ram 使用率运行),并且我的代码在 Visual Studio 2013 中的 asp.net MVC 项目中运行。我正在以 32 位运行该项目,因为我使用的是仅在 32 位中可用的 Tessnet2。尽管如此,我认为我应该有足够的内存来一次处理 5 张以上的图像。有没有更好的方法来解决这个问题?如果可以的话,我宁愿不必求助于付费框架。我觉得这是我应该能够免费使用开箱即用的 .net 代码完成的事情。谢谢!
TL; 博士
Graphics.FromImage 的文档说:如果图像具有以下任何像素格式,此方法也会引发异常: Undefined, DontCare, Format16bppArgb1555, Format16bppGrayScale。所以,使用另一种格式或尝试另一种图像库(它不是建立在 GDI+ 之上,如Emgu CV)
详细的
您收到 OutOfMemoryException,但这与内存不足无关。这就是Windows GDI+ 的工作方式以及它如何封装在 .NET 中。我们可以看到Graphics.FromImage 调用 GDI+:
int status = SafeNativeMethods.Gdip.GdipGetImageGraphicsContext(new HandleRef(image, image.nativeImage), out gdipNativeGraphics);
Run Code Online (Sandbox Code Playgroud)
internal static Exception StatusException(int status)
{
Debug.Assert(status != Ok, "Throwing an exception for an 'Ok' return code");
switch (status)
{
case GenericError: return new ExternalException(SR.GetString(SR.GdiplusGenericError), E_FAIL);
case InvalidParameter: return new ArgumentException(SR.GetString(SR.GdiplusInvalidParameter));
case OutOfMemory: return new OutOfMemoryException(SR.GetString(SR.GdiplusOutOfMemory));
case ObjectBusy: return new InvalidOperationException(SR.GetString(SR.GdiplusObjectBusy));
.....
}
}
Run Code Online (Sandbox Code Playgroud)
你的场景中的 status 等于3,所以为什么它会抛出 OutOfMemoryException 。
为了重现它,我只是尝试从 16x16 位图创建图像:
int w = 16;
int h = 16;
Bitmap bitmap = new Bitmap(w, h, PixelFormat.Format16bppGrayScale);
Graphics g = Graphics.FromImage(bitmap); // OutOfMemoryException here
Run Code Online (Sandbox Code Playgroud)
我使用windbg + SOSEX 进行 .NET 扩展进行分析,在这里我可以看到:
