Jump to content
bobD

memory usage of TJPGImage

Recommended Posts

Posted (edited)

I'm working on a quick photo browsing app, using a TObjectDictionary<string, TJPEGImage> as a cache device. Images are requested for display using the path to the jpg file like this:

procedure TJpgStore.LoadPicture(aPicture: TPicture; const aSource: string);
var
  img : TJPEGImage;
begin
  if not FDict.TryGetValue(aSource, img) then
  begin
    img := TJPEGImage.Create;
    img.Scale := jsEighth;
    img.Performance := jpBestSpeed;
    img.LoadFromFile(aSource);
    FDict.Add(aSource, img);
  end;
  aPicture.Assign(img);
end;

This all seems to work fine, but two questions (since I'm not really a graphics guy):

  1. Am  using Scale and Performance correctly? IOW, does it make any difference being here or after the LoadFromFile call?
  2. How much memory does a loaded TJPGImage take up? ('m thinking about a least-recently-used cache size governor. The jpgs are commonly 15-25MB each, and a source directory might contain 1-5,000 picture, so caching them all doesn't seem realistic...)  

Those are specific questions, but glad to entertain other comments/suggestions/alternative approaches.

Thanks,

bobD

Edited by bobD

Share this post


Link to post

If you are going to cache them, cache the jpeg stream rather than the decoded image. Then decode the image on demand with LoadFromStream. 

Share this post


Link to post
59 minutes ago, David Heffernan said:

If you are going to cache them, cache the jpeg stream rather than the decoded image. Then decode the image on demand with LoadFromStream. 

I can do that. Is that suggested for performance or memory footprint?

It bugs me that I can't see what I'm doing. InstanceSize returns 100 bytes for the img, 24 bytes for the stream. Obviously neither really accounts for the data.

bobD

Share this post


Link to post
7 hours ago, bobD said:

performance or memory footprint?

Memory footprint. 

 

InstanceSize is just the instance and not the dynamically allocated data it owns. 

Share this post


Link to post
Posted (edited)
13 hours ago, bobD said:

I can do that. Is that suggested for performance or memory footprint?

It bugs me that I can't see what I'm doing. InstanceSize returns 100 bytes for the img, 24 bytes for the stream. Obviously neither really accounts for the data.

To get the size of a memory stream, add InstanceSize and Capacity. Unfortunately capacity is usually larger than the actual size of the data and of I remember correctly, TMemoryStream does not allow setting the capacity beforehand. But you can easily add that functionality. I think I've got such a class in my dzlib. Setting the size also sets the capacity, so If you know the expected size beforehand, set it first before writing to the stream to avoid allocating more memory than necessary.

 

I have no idea how to calculate the memory used by a TJpegImage.

 

If you want better performance, don't use TJpegImage for decoding but LibJpeg-turbo.

I haven't measured it, but subjectively the difference is staggering.

Edited by dummzeuch

Share this post


Link to post
21 hours ago, bobD said:

How much memory does a loaded TJPGImage take up? ('m thinking about a least-recently-used cache size governor. The jpgs are commonly 15-25MB each, and a source directory might contain 1-5,000 picture, so caching them all doesn't seem realistic...)  

Save an image as bitmap, then you'll get an idea how much it takes in memory.

Share this post


Link to post
Posted (edited)

The TJPGImage has a TBitmap in it which will take the memory i think. This is a simple calculation, width,height,bits/pixel or am i  mistaken?

mistaken 🙂

 

Edited by mvanrijnen

Share this post


Link to post
6 hours ago, mvanrijnen said:

The TJPGImage has a TBitmap in it which will take the memory i think. This is a simple calculation, width,height,bits/pixel or am i  mistaken?

mistaken 🙂

 

In what regard?

 

What I know so far is this: the lines

    img.Scale := jsEighth;
    img.Performance := jpBestSpeed;

speed up the load/display of the jpg file as advertised

Using the following code,

    bmp := TBitMap.Create(img.Width, img.Height);
    bmp.Assign(img);
    bmp.SaveToFile('C:\Users\bobd\Desktop\TestBMP.bmp');

and starting with a jpg file of 15.4 MB results in a bmp file of about 1.14 MB if Scale and Performance are so specified, or with the lines commented out 73MB (!). 

That suggests to me that there's no direct calculation of the JPGImage size from the bitmap it produces. You can see either a > 10x reduction, or a 4-5x expansion.

Interestingly though, the TJPGImage object saving itself to file after the scale and performance are reduced for load results in a file the same size as the original. So we can't assume that the TJPGImage so loaded is any smaller in memory that one loaded without the jsEighth reduction. That's interesting.

 

It appears to me that the most memory efficient cache (and thus the one with the largest practical capacity) for this app is going to be loading at jsEighth, then creating and caching the bitmap. Since the original image can be 6192 x 4192, I don't really need all that resolution anyway.

Unfortunately, this presumes something not in evidence: that the size of the TBitmap in memory has a direct relationship to its file size. As with the source TJPGImage, it doesn't have an accessible size-in-memory property.

 

Share this post


Link to post
Posted (edited)
9 hours ago, bobD said:

It appears to me that the most memory efficient cache (and thus the one with the largest practical capacity) for this app is going to be loading at jsEighth, then creating and caching the bitmap.

If you only need thumbnails, why not explicitly scale your picture to a bitmap of whatever size you need and cache that? Of course that takes time.

Edited by dummzeuch

Share this post


Link to post
Posted (edited)
9 hours ago, bobD said:

It appears to me that the most memory efficient cache (and thus the one with the largest practical capacity) for this app is going to be loading at jsEighth, then creating and caching the bitmap. Since the original image can be 6192 x 4192, I don't really need all that resolution anyway.

Unfortunately, this presumes something not in evidence: that the size of the TBitmap in memory has a direct relationship to its file size. As with the source TJPGImage, it doesn't have an accessible size-in-memory property.

Solving similar issue in my map control,. I came to 3-level cache:

- Hot: Tbitmaps

- Medium: PNGImages

- Cold: TMemoryStreams

Capacity of all levels is limited by memory and GDI handles.

Comments in OSM.TileStorage.pas unit explain decisions I made

Edited by Fr0sT.Brutal
  • Like 1

Share this post


Link to post
13 hours ago, Fr0sT.Brutal said:

Solving similar issue in my map control,. I came to 3-level cache:

- Hot: Tbitmaps

- Medium: PNGImages

- Cold: TMemoryStreams

Capacity of all levels is limited by memory and GDI handles.

Comments in OSM.TileStorage.pas unit explain decisions I made

That encourages me that I'm on the right track with bitmaps. 

I'll report back with some further testing.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×