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

r1wp621o  于 2022-11-04  发布在  其他
关注(0)|答案(8)|浏览(226)

问题就在这里。我有一大组512 X512像素的JPEG瓦片作为普通jpg文件。
我已经写了一个软件,做了一堆的事情,并确实需要缝合所有这些文件到一个单一的巨大的JPEG在最后。
首先,我不希望使用ImageMagick来做这件事,而是在我的软件内部执行它!
在 Delphi 中,不可能将JPG文件复制到另一个JPG画布上,因此必须首先创建TBitmap,然后将TBitmap复制到TBitmap画布上,然后将TBitmap转换为jpeg图片并保存到文件中。
当生成的文件尺寸太大时(比如20000 x 20000像素),就会出现这个问题。当我调用TBitmap.setSize时,我自然会得到一个错误(内存不足或类似的错误)。
我做了一些测试使用Photoshop在同一台机器上,并能够创建一个复杂的(非空白)30000 x 30000文件,并将其保存为JPEG。
所以问题是,我怎么能执行同样的事情?找一些方法来缝合所有这些JPEGS通过写结果直接到磁盘或使用一些其他的技巧?
即使20 k x 20 k像素似乎足够大,这个值只适用于我的机器(4 GB RAM),所以较小的RAM数量将更加限制软件!
谢谢
编辑:澄清:
我想要的是找到一种方法来拼接这些小的JPG图像,然后写入大的图像,而不需要将大的图像保存在RAM中。(不确定)但是这会导致一个非常大的文件。所以,如果JPG格式不允许这样做,任何其他压缩格式,如TIFF或PNG都可以。我还想避免太多的重新压缩,以免失去(已经压缩的)初始JPG质量。
因此,完美的解决方案将是一种直接读取小文件并以某种方式写入大文件的方法。瓷砖的尺寸是256 x256或512 x512,以防它有助于JPEG压缩材料的一些对齐。

5t7ly7z5

5t7ly7z51#

感谢所有人!
实际上,答案和可能的解决方案是像Photoshop那样进行,即从图块将位图流写入磁盘上的大bmp文件(例如,20 000 x 30 000文件将是2.4 Gb),然后使用NativeJpg库通过逐个条带地提供位图数据(每个条带的高度为8像素)将此大位图转换为jpg。
也可以缝合一行瓷砖(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;

本机JPG库:http://www.simdesign.nl/nativejpg.html

a0x5cqrl

a0x5cqrl2#

这是一个相当坚韧的领域,正如@Peter已经说过的,Photoshop的人从1990年就开始从事这一领域。您可能无法使用编程语言的内置JPG解码库,因为它们可能会在RAM中加载(和解包)整个图像。
我建议寻找处理这个问题的外部库--我不知道是否有免费的像样的库,但很可能是这样。

o8x7eapl

o8x7eapl3#

Delphi 中图像的最大大小(至少在以前的版本中)更多地依赖于Windows图形驱动程序,而不是系统内存的大小
对此进行了一些实验:EFG's computer lab

fkvaft9z

fkvaft9z4#

Photoshop像许多其他面向媒体的程序一样,不得不处理长时间内大于主内存的文件。对于大照片,一种技术是平铺,只处理图像的一部分。
在实践中,这比简单的剪切和粘贴更复杂(双关语)。
我不是 Delphi 程序员,但让我担心的是,当你创建图像时内存不足,当你试图使用该图像时,会不会发生同样的情况?

fhity93d

fhity93d5#

对于像这样的繁重工作,我求助于http://www.graphicsmagick.org/
这里也有一组Pascal单位,http://graphics32.org/,但是它们非常数学化和复杂(因此我还没有让它们工作),但是也是为艰苦的工作而建立的。

bwntbbo3

bwntbbo36#

您可能需要实现一个自定义类来处理此问题。
在视频存储器中,图像(或屏幕缓冲区)是一个线性阵列。水平行按顺序存储,每个像素对应于y * width + x处的阵列偏移量。
因此,在320 x200图像中,5,2处的像素将位于数组索引5*320+2-1,即1601。在硬件加速之前,您需要malloc()一个屏幕大小的缓冲区,并以数学方式执行诸如绘制纹理、形状、灯光效果等操作,然后将缓冲区BLT到视频RAM。
在您的情况下,可以使用内置的位图和图像类来处理适合内存的较小图像,然后将其像素数据复制到一个大型数组或一系列数组中(我忘了虚拟内存是否允许创建大于物理RAM大小的缓冲区)。然后,使用直接在该数组上工作的JPEG库(与安装在机器上的RAM大小无关),您应该能够将阵列放入库中并让它将内容保存到磁盘。LZW压缩是相当简单的,我希望他们会有很多关于在网络上手动实现JPEG压缩的材料。
有一点需要注意:如果你使用的是32位操作系统,你的地址空间应该限制在4GB以内。我能想到的解决这个问题的唯一方法是创建更小的缓冲区(比如说,一次一行),用你要拼接的图像中该行对应的像素数据部分填充数据,保存它,然后循环直到你覆盖了整个图像区域。
我希望你明白了。祝你好运!

sq1bmfud

sq1bmfud7#

你的问题也让我想到了电子表格。当然,这些表格只是稀疏的。你可以试着看看一些图像压缩库,也许会给予你一些想法。
我注意到PixeLook开发组有their PixeLook library,它们是Delphi 6的组件,用于创建图像和数据处理应用程序。
他们声称,大图像和数据矩阵很容易处理,在他们的屏幕截图页面上,他们显示了5200 x 5200图像(26 MB)的显示,他们说,他们的测试也是在图像大小高达220 MB的情况下进行的。
如果你真的需要在你的应用程序中直接处理大的图像,这个包可能会为你工作50美元。如果它很接近,但不太正确(我不知道它是否会加入jpeg),那么你可能会考虑购买299美元的源代码,看看它做什么,并扩展它。
免责声明:我与这家公司没有任何联系。

llycmphe

llycmphe8#

那么部分外部软件/内部调用解决方案呢?IJG(Independent JPEG Group)有一个很棒的命令行工具jpegtran,我在我的查看器中使用它来进行无损旋转。在你自己的代码中使用CreateProcess、WaitForsingleObject来使代码看起来像你自己的代码是没有问题的。你甚至可以将可执行文件打包到你的资源中,并将其临时提取出来
因此,他们也有jpegjoin实用程序(在http://jpegclub.org/jpegtran/中找到),可以以同样的方式使用。更新:此实用程序用于无损连接,因此需要更小的内存/磁盘占用空间

相关问题