如何使用Delphi编写一个超过物理RAM大小的巨大JPEG文件?

11

问题在于,我有一组大型的512x512像素的JPEG瓷砖作为常规jpg文件。

我编写了一段软件来执行许多操作,并需要在最后将所有文件拼接成一个巨大的JPEG。

首先,我不希望使用ImageMagick来执行此操作,而是要在我的软件内部执行!

在Delphi中,无法将JPG文件复制到另一个JPG画布上,因此必须首先创建TBitmap,然后将图块复制到TBitmap画布上,然后将TBitmap转换为jpeg图片并保存到文件中。

当生成的文件尺寸太大(例如20,000 x 20,000像素)时,问题就出现了。当我调用TBitmap.SetSize时,自然会出现错误(内存不足或类似的错误)。

我在同一台机器上使用Photoshop进行了一些测试,并能够创建一个复杂的(非空白)30,000 x 30,000文件并将其保存为JPEG。

所以问题是,我该如何执行相同的操作?找到一种方式将所有JPEGS拼接起来,直接将结果写入磁盘或使用其他技巧?...

即使20k x 20k像素似乎已经足够大,但此值仅适用于我的机器(4 GB RAM),因此更少的RAM将进一步限制软件!

谢谢

编辑:为了澄清:

我希望找到一种方法来拼接这些小的JPG图像,并在不将大图像保留在RAM中的情况下编写大图像。显然,在磁盘上可以直接进行位图流读/写(不确定),但这将导致一个非常大的文件。因此,如果JPG格式不允许执行此操作,则任何其他压缩格式,如TIFF或PNG,都可以。我也想避免过多的重新压缩,以不失去(已经压缩的)初始JPG质量。

因此,完美的解决方案是找到一种方法,直接读取小文件并以某种方式写入大文件。如果需要JPEG压缩方面的对齐,图块的尺寸为256x256或512x512。

8个回答

8

感谢大家!

实际上,答案和可能的解决方案是像Photoshop一样进行操作:将瓦片的位图流写入磁盘上的一个大位图文件中(例如20,000 x 30,000的文件大小为2.4 GB),然后使用NativeJpg库通过逐条提供位图数据来将这个大位图转换为jpg,每条数据都是8个像素高。

也可以将一行瓷砖拼接在一起(512像素高),然后将其分成8个8个的块并逐个输入到NativeJpg库中,然后继续下一行瓷砖!

以下是Erik Turner提供的一些示例代码:

procedure GetBitmapTile(BM: TBitmap; Y, X: Integer);
var JpegImage: TJpegImage;
begin
  JpegImage := NIL; // Replace with tile lookup //
  BM.PixelFormat := pf32bit;
  BM.Width := JpegImage.Width;
  BM.Height := JpegImage.Height;
  BM.Canvas.Draw(0, 0, JpegImage);
end;

procedure WriteBitmapFile(TileCountY, TileCountX: Integer; BM_Stm: TStream);
var
  BM: TBitmap;
  TileY: Integer;
  TileX: Integer;
  PixelY: Integer;
begin
  BM := TBitmap.Create;
  for TileY := 0 to TileCountY-1 do
    for TileX := 0 to TileCountX-1 do
    begin
      GetBitmapTile(BM, TileY, TileX);
      for PixelY := 0 to 511 do
        BM_Stm.Write(BM.ScanLine[PixelY]^, 512 * SizeOf(TRGBQuad));
    end;
    BM.Free;
end;

NativeJpg库:http://www.simdesign.nl/nativejpg.html

NativeJpg库是一个用于处理JPEG文件的C ++库。它提供了高效的解码和编码功能,可用于各种IT应用程序中。


1

像许多其他面向媒体的程序一样,Photoshop 长期以来不得不处理处理比主内存更大的文件的问题。对于大型照片,一种技术是将其分割成小块,只处理部分图像。

实际上,这比简单地剪切和粘贴更加复杂(褒义)。

虽然我不是 Delphi 程序员,但令我担忧的是,当你用尽内存创建图像时,当你尝试使用该图像时,同样的情况是否会发生?


我知道这个问题会出现,但是就像之前所说的,我能够在Photoshop中打开一个更大的图像并进行处理。;) - Alexander

1

这是一个相当艰难的领域,正如@Peter所说,Photoshop团队从1990年开始就一直在研究这个问题。你可能无法使用编程语言内置的JPG解码库,因为它们很可能会将整个图像加载(和解压缩)到RAM中。

我建议寻找处理此问题的外部库 - 是否有可用的免费库我不知道,但很可能存在。


谢谢Pekka。我不必解压大图像,读取部分很容易。问题在于写入!;) - Alexander
@Alexander,由于JPG格式的特性,它仍然会很复杂:你不能像使用不使用压缩的原始格式那样简单地将字节流拼接在一起。除非你是为了学习效果而这么做,否则我建议使用外部库来完成 :) - Pekka
好的,现在我所做的是将图像分成两半,并编写两个单独的文件,然后必须在 Photoshop 中重新拼接它们,但我肯定会获得学习效果,因为这是一个个人项目 ;) 否则,我想我根本不会问这个问题!如果有另一种格式适用于此,例如 PNG 或其他不同于原始 bmp(导致非常大的文件)的格式... - Alexander

1
Delphi中图片的最大尺寸(至少在早期版本中)更多地依赖于Windows图形驱动程序而不是系统内存的数量。
对此进行了一些实验: EFG的计算机实验室

谢谢Jan,我已经阅读了这个网站,但我实际上更想了解一些技术,可以在不必将整个结果图像存储到内存中的情况下读写JPEG数据。我知道netpbm包可以在其本地格式上执行此操作,甚至可以在不将整个数据加载到RAM中的情况下在某些格式之间进行转换... - Alexander

1

您可能需要实现一个自定义类来处理这个问题。

在视频内存中,图像(或屏幕缓冲区)是一个线性数组。水平行按顺序存储,每个像素对应于y * width + x的数组偏移量。

因此,在320x200的图像中,5,2处的像素将位于数组索引5*320+2-1或1601处。在硬件加速出现之前,您会malloc()一个与屏幕大小相同的缓冲区,并进行数学运算,如绘制纹理、形状、光照效果等,然后将缓冲区BLT到视频RAM中。

在您的情况下,您可以使用内置的位图和图像类来处理适合内存的较小图像,然后将它们的像素数据复制到一个大数组或一系列数组中(我忘记了虚拟内存是否允许您创建>物理RAM大小的缓冲区)。然后,使用直接在该数组上工作的JPEG库(该库独立于计算机上安装的RAM大小),您应该能够将数组馈送到库中,并将其内容保存到磁盘中。LZW压缩非常简单,我希望在网上有很多关于手动实现JPEG压缩的材料。

需要注意的是:如果您使用的是32位操作系统,则地址空间应限制为4GB。我能想到的唯一解决方法是创建较小的缓冲区(例如,每次一个行),用所需拼接图像中该行对应的像素数据填充数据,保存并循环直到覆盖整个图像区域。

希望这很清楚。祝你好运!


我就是喜欢那些出现在回答发布六个月后的踩票。 - 3Dave
12年后:也许是因为“他们的”而不是“那里”的原因? - AmigoJack

1

谢谢,但是关于GraphicsMagick,请阅读我的问题:“首先,我不想使用ImageMagick来完成这个任务,而是在我的软件内部执行它!” - Alexander
1
GraphicsMagick有一个C++库,您可以在构建时使用,并且还有一个OLE接口:http://www.graphicsmagick.org/programming.html(不过,我必须承认,从Delphi消费C++库是另一回事) - Stijn Sanders

0

你的问题也让我想到了电子表格。当然,它们只有很少的填充。你可以尝试查看一些图像压缩库,这可能会给你一些想法。

你还可以看看别人是如何做的。我注意到PixeLook开发组有他们的PixeLook库,这是用于创建图像和数据处理应用程序的Delphi 6组件。

他们声称可以轻松处理大型图像和数据矩阵,并且在他们的屏幕截图页面上,他们展示了一个5200 x 5200像素的图像(26 MB)的显示,并且他们说他们的测试也使用了高达220 MB的图像大小。

如果你真的需要在你的应用程序中直接进行大型图像处理,那么这个软件包可能适合你,价格为50美元。如果它接近但不完全正确(我不知道它是否支持jpeg),那么你可以考虑以299美元的价格购买源代码,看看它的功能,并进行扩展。

声明:我与该公司无关。


220 MB的图像(小于2 GB)和2.4 GB的图像(大于2 GB)完全是不同的级别。你可以将第一张图片加载到内存中。好吧,它很大,但并不是那么大,所以这是可能的。但是对于第二张图片,你不能这样做。因为它的大小超过了你的地址空间。 - Alex

0

关于部分使用外部软件/内部调用解决方案怎么样?IJG(独立JPEG组)拥有出色的命令行工具jpegtran,我在我的图像查看器中用它进行无损旋转。在自己的代码中使用CreateProcess、WaitForsingleObject没有任何问题,可以让它看起来像是你自己的代码。你甚至可以将可执行文件打包到你的资源中,并临时提取它。

他们还有jpegjoin实用程序(在http://jpegclub.org/jpegtran/找到它),可以以相同的方式使用。更新:这个实用程序用于无损连接,因此需要更小的内存/磁盘空间


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