如何使用Delphi编写超出物理RAM的巨大JPEG?

时间:2010-06-27 10:27:20

标签: delphi bitmap jpeg tiles image-stitching

这是问题所在。我有一大堆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质量。

因此,完美的解决方案将是一种直接读取小文件并以某种方式写入大文件的方法。瓷砖的尺寸为256x256或512x512,以防有助于JPEG压缩内容的某些对齐。

8 个答案:

答案 0 :(得分:8)

感谢大家!

实际上,答案和可能的解决方案是继续像Photoshop一样,即将磁贴中的位图流写入磁盘上的大bmp文件(例如,一个20 000 x 30 000文件将是2.4 Gb)和然后使用NativeJpg库通过条带馈送位图数据将这个大位图转换为jpg,每个位图数据高8像素。

也可以拼接一行瓷砖(512像素高),然后将它8送到NativeJpg库8,然后继续下一行瓷砖!

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

答案 1 :(得分:1)

像许多其他面向媒体的程序一样,Photoshop必须处理长时间处理比主存大的文件的问题。对于大型照片,一种技术是平铺,只能处理图像的一部分。

在实践中,这比简单的剪切和粘贴更复杂(双关语)。

我不是德尔福程序员,但令我担心的是,当你的内存耗尽创建图像时,当你试图使用那个图像时会不会发生同样的情况?

答案 2 :(得分:1)

这是一个相当艰难的领域,正如@Peter已经说过的那样,Photoshop的人们已经处于这个since 1990。您可能无法使用编程语言的内置JPG解码库,因为它们可能会将整个映像加载(并解包)到RAM中。

我建议寻找能解决这个问题的外部图书馆 - 是否有免费的免费版本我不知道,但情况可能就是这样。

答案 3 :(得分:1)

Delphi中的图像最大尺寸(至少在以前的版本中)更依赖于Windows图形驱动程序而不是系统内存量

对此进行了一些实验: EFG's computer lab

答案 4 :(得分:1)

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

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

因此,在320x200图像中,5,2处的像素将在数组索引5 * 320 + 2-1或1601处。在硬件加速之前的几天,你的malloc()是一个大小的缓冲区你的屏幕并以数学方式执行绘制纹理,形状,灯光效果等操作,然后将缓冲区BLT输出到视频RAM。

在您的情况下,您可以使用内置位图和图像类来处理适合内存的较小图像,然后将其像素数据复制到大型数组或一系列数组中(如果虚拟内存允许,我会忘记你要创建缓冲区>物理RAM的大小)。然后,使用直接在该阵列上工作的JPEG库(与机器上安装的RAM的大小无关),您应该能够将阵列提供到库中并将其保存到磁盘中。 LZW压缩非常简单,我希望它们能够在网络上手动实现JPEG压缩,这是很多材料。

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

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

答案 5 :(得分:1)

这样的重型工作,我诉诸http://www.graphicsmagick.org/

这里还有一组Pascal单位http://graphics32.org/,但它们非常数学和复杂(我因此没有让它们起作用),但也是为了努力工作而构建的。

答案 6 :(得分:0)

您的问题也让我想到电子表格。这当然只是人口稀少。您可以尝试查看一些可能会给您一些想法的图像压缩库。

你还可以做的是看别人怎么做。我注意到PixeLook开发组有their PixeLook library,它们是Delphi 6用于创建图像和数据处理应用程序的组件。

他们声称可以轻松处理大型图像和数据矩阵on their screenshot page,它们显示5200 x 5200图像(26 MB)的显示,他们说他们的测试也是在图像大小高达220的情况下执行的MB。

如果你真的需要在你的应用程序中直接进行大型图像处理,这个软件包可能会以50美元的价格为你服务。如果它接近但不太正确(我不知道它是否会加入jpegs),那么你可以考虑以299美元的价格购买它,看看它的作用,并扩展它。

免责声明:我与该公司没有关系。

答案 7 :(得分:0)

部分外部软件/内部呼叫解决方案怎么样? IJG(Independent JPEG Group)有一个出色的命令行工具jpegtran,我在观察器中用于无损旋转。在您自己的代码中使用CreateProcess,WaitForsingleObject使其看起来像您自己的代码是没有问题的。您甚至可以将可执行文件打包到资源中并临时提取

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