Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a huge JPEG that exceeds the physical RAM using Delphi?

Here is the problem. I have a large set of 512x512 pixels JPEG tiles as regular jpg files.

I have written a piece of software that does a bunch of things and does need to stitch all those files into a single huge JPEG at the end.

First of all, I do NOT wish to use ImageMagick to do this, but perform it inside my software !

In Delphi it is not possible to copy a JPG file onto another JPG canvas, so a TBitmap must be created first, then the tiles are copied onto the TBitmap canvas and then the TBitmap is converted to a jpeg picture and saved to a file.

The issue arises when the resulting file dimensions are too big (like 20 000 x 20 000 pixels). When I call TBitmap.SetSize I naturally get an error (out of memory or something like this).

I made some tests using Photoshop on the very same machine and was able to create a complex (non blank) 30 000 x 30 000 file and save it to JPEG.

So the question is, how could I perform the same thing ? Fing some way to stitch all those JPEGS by writing the result directly to the disk or use some other trick ?...

Even though 20k x 20k pixels seems big enough, this value does only apply to my machine (4 GB ram) so a smaller amount of ram would be even more limiting to the software !

Thanks

Edit : To clarify :

What I would like is to find a way of stitching those small JPG images and write the large one without keeping the large image in RAM. Apparently a bitmap stream read/write is possible directly on disk (not sure) but this would result in a VERY large file. So, if the JPG format does not allow to do this, any other compressed format like TIFF or PNG would do. I would also like to avoid too much recompression not to lose the (already compressed) initial JPG quality.

Hence the perfect solution would be a way to directly read the small files and write into the large one somehow. The dimensions of the tiles are 256x256 or 512x512 in case it would help for some alignment on JPEG compression stuff.

like image 677
Alexander Avatar asked Jun 27 '10 10:06

Alexander


1 Answers

Thanks to everyone !

Actually, the answer and possible solution is to proceed as Photoshop does, that is write a bitmap stream from the tiles to a big bmp file on the disk (for example a 20 000 x 30 000 file would be 2.4 Gb) and then use the NativeJpg library to convert this big bitmap to a jpg by feeding the bitmap data stripe by stripe, each of them being 8 pixels high.

It would also be possible to stitch a single line of tiles (512 pixels high) and then feed it to the NativeJpg library 8 by 8 and then move on to the next line of tiles !

Some sample code by 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 library : http://www.simdesign.nl/nativejpg.html

like image 132
Alexander Avatar answered Oct 20 '22 17:10

Alexander