【问题标题】:.NET GDI+ image size - file codec limitations.NET GDI+ 图像大小 - 文件编解码器限制
【发布时间】:2010-05-28 20:49:28
【问题描述】:

使用 .NET 提供的图像文件编解码器可以编码的图像大小是否有限制?

我正在尝试对大小大于 4GB 的图像进行编码,但它根本无法使用 .bmp、.jpg、.png 或 .tif 编码器进行编码(或无法正常工作,即写出不可读的文件)。

当我将图像大小降低到

我的下一次尝试是尝试 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 内存区域,但这仅限于

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 - 使用 x64 编译。所以64位。对于这么大的图像,我认为 32 位是不够的。我的机器有 8GB 的​​ RAM,也许这还不够,尽管它应该用于略高于 4GB 的图像。我有大约 7GB 的空闲空间,当编码器启动时,还有大约 1-2GB 的空闲空间。
  • @roygbiv 只是检查显而易见的:)
  • BMP 仅限于用 2 个带符号的 16 位整数表示图像尺寸,因此假设 32 BPP,最大的 BMP 不能是 4GB(图像数据为 32767*32767*4 字节)。

标签: .net gdi+ codec


【解决方案1】:

您面临的问题很可能是 .NET Framework 中的以下限制:GC 堆中允许的最大对象大小为 2GB,即使在 64 位操作系统/构建上也是如此。

一些参考资料:linklinklink

根据您的需要,使用相对简单(未压缩)的格式(如 TIFF)可能可以分段生成图像并在之后合并这些部分。只是一个想法..

【讨论】:

  • 正如我所指出的,我可以使用 VirtualAlloc 在内存中创建大于 4GB 的位图,但是,各种编码器无法将此类图像写入磁盘。如果您建议编码器本身需要 > 2GB 的 RAM,那么我认为这不是一个非常有效的编码器。
  • 我快速浏览了代码,但您应该测试 Bitmap.Save 在将图像写入文件流之前是否尝试(再次)在内存中构建图像(可能会)。这可以解释这种行为......
  • 我会再试一遍,但它似乎在保存之前没有重建图像。
【解决方案2】:

来自 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 年代或更早时期创建的其他图像格式中发现类似的限制。当磁盘只有几十兆字节时,多千兆字节的文件大小限制不被视为问题。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 2010-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-07
      • 2013-04-05
      相关资源
      最近更新 更多