.NET GDI+图像大小-文件编解码器限制

6
使用.NET提供的图像文件编解码器,能否对大小进行编码的图像有限制?
我正在尝试对大于4GB的图像进行编码,但是使用.bmp、.jpg、.png或.tif编码器时会出现问题(或者输出的文件无法读取)。当我将图像大小降低到小于2GB时,使用.jpg编码器可以正常工作,但是使用.bmp、.tif或.png编码器则不行。
我的下一个尝试将是尝试libtiff,因为我知道tiff文件适用于大型图像。什么是适用于大型图像的好文件格式?或者我只是遇到了文件格式的限制?
(所有这些都在64位操作系统(WinXP 64)上完成,具有8 GB的RAM,并使用x64架构编译。)
Random r = new Random((int)DateTime.Now.Ticks);

int width = 64000;
int height = 64000;
int stride = (width % 4) > 0 ? width + (width % 4) : width;
UIntPtr dataSize = new UIntPtr((ulong)stride * (ulong)height);
IntPtr p = Program.VirtualAlloc(IntPtr.Zero, dataSize, Program.AllocationType.COMMIT | Program.AllocationType.RESERVE, Program.MemoryProtection.READWRITE);

Bitmap bmp = new Bitmap(width, height, stride, PixelFormat.Format8bppIndexed, p);
BitmapData bd = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);

ColorPalette cp = bmp.Palette;
for (int i = 0; i < cp.Entries.Length; i++)
{
  cp.Entries[i] = Color.FromArgb(i, i, i);
}
bmp.Palette = cp;

unsafe
{
  for (int y = 0; y < bd.Height; y++)
  {
    byte* row = (byte*)bd.Scan0.ToPointer() + (y * bd.Stride);
    for (int x = 0; x < bd.Width; x++)
    {
      *(row + x) = (byte)r.Next(256);
    }
  }
}

bmp.UnlockBits(bd);
bmp.Save(@"c:\test.jpg", ImageFormat.Jpeg);
bmp.Dispose();

Program.VirtualFree(p, UIntPtr.Zero, 0x8000);

我还尝试使用固定的GC内存区域,但是这种方式的限制是小于2GB。

Random r = new Random((int)DateTime.Now.Ticks);

int bytesPerPixel = 4;
int width = 4000;
int height = 4000;         
int padding = 4 - ((width * bytesPerPixel) % 4);
padding = (padding == 4 ? 0 : padding);
int stride = (width * bytesPerPixel) + padding;
UInt32[] pixels = new UInt32[width * height];
GCHandle gchPixels = GCHandle.Alloc(pixels, GCHandleType.Pinned);
using (Bitmap bmp = new Bitmap(width, height, stride, PixelFormat.Format32bppPArgb, gchPixels.AddrOfPinnedObject()))
{
    for (int y = 0; y < height; y++)
    {
        int row = (y * width);
        for (int x = 0; x < width; x++)
        {
            pixels[row + x] = (uint)r.Next();
        }
    }

    bmp.Save(@"c:\test.jpg", ImageFormat.Jpeg);
}
gchPixels.Free();

你使用的处理器架构是什么? - Rowland Shaw
@Rowland - 使用x64编译。因此是64位的。我认为32位对于如此大的图像不足够。我的计算机有8GB的RAM,也许这还不够,尽管它应该足以容纳略大于4GB的图像。我还剩约7GB的空间,当编码器启动时,仍有1-2GB的空闲空间。 - user113476
@roygbiv 只是检查显而易见的 :) - Rowland Shaw
BMP仅限于使用2个有符号16位整数表示图像尺寸,因此假设32 BPP,则最大的BMP不能超过4GB(图像数据需要32767 * 32767 * 4字节)。 - Michael Madsen
3个回答

2
您面临的问题很可能是.NET Framework中以下限制:即使在64位操作系统/构建中,GC Heap中允许的最大对象大小为2GB。 一些参考文献:链接链接链接
根据您的需求,对于像TIFF这样相对简单(未压缩)的格式,生成部分图像并在之后合并部分可能是可行的。 这只是一个想法...

正如我所指出的,我可以使用VirtualAlloc创建内存中大于4GB的位图,但是各种编码器无法将这样的图像写入磁盘。如果您建议编码器本身需要> 2GB的RAM,那么我认为这不是一个非常高效的编码器。 - user113476
1
我快速浏览了一下代码,但你应该测试一下Bitmap.Save是否在将图像写入文件流之前尝试在内存中构建图像(也许它确实这样做)。这可能会解释这种行为... - ChristopheD
我会再次逐步检查,但似乎在保存图像之前它没有重建图像。 - user113476

1

从TIFF规范第2节:http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf中可以了解到TIFF结构:

TIFF是一种图像文件格式。在本文档中,文件被定义为8位字节序列,其中字节编号从0到N。最大可能的TIFF文件长度为2 ** 32字节。

不幸的是,许多TIFF编写器和阅读器在实现中使用有符号整数,并将实际大小减小到2 ** 31。这加剧了驱动程序尝试将整个图像保留在内存中的频繁尝试,正如Cristophe所提到的那样。

您可能会发现在80年代或更早期创建的其他图像格式中存在类似的限制。当磁盘只有几十兆字节时,多千兆字节的文件大小限制并未被视为问题。


1

所以你的目标是将它们编码成jpeg格式?我相信有一种方法可以只拉取你正在编码的部分,然后处理并继续下一部分。如果你打开原始文件并自己解析它,我相信有开源的jpeg编码器可以帮助创建这些部分。这应该允许你编码非常大的文件,但你总是需要克服操作系统和文件系统的障碍。


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