24

I am building a PHP system whereby users can create a visually complex image that has the final requirement of being 50kB or less.

The user can select to have text printed onto one of 37 template images, so the result is a single flat image.

The text can be any colour and one of a number of fonts. This means the final image can be quite visually complex and unpredictable (with the exception of image dimensions).

I then have the requirement of having the final image file size no greater than 50kB (this is an external requirement and cannot be changed).

The last requirement (again, external) is the image format must be .jpeg, .png, or .gif.

I have looked through the GraphicsMagick documentation, but cannot find anywhere that mentions being able to set the filesize and have the compression automatically calculated.

I have considered doing this programatically via a compress->save->test loop, however I can imagine this would be quite processor intensive as I cannot necessarily calculate the filesize based on compression in advance. This is why I am asking to see if the problem has already been solved in GraphicsMagick.

Edit

To be clear as to why there are external requirements:

The user will utilize this system to create a flat image, which they will then save onto their PC. This image is then to be uploaded to Adroll for use in a Retargeting campaign.

Here are Adroll's requirements on the image. My system will only provide the 728x90, 300x250, and 120x600 image sizes.

Edit 27th Nov, 2010

As this does not seem to be possible with GraphicsMagick, I am willing to look into other solutions, such as directly interfacing with compression libraries (libpng, etc) which may be able to provide the functionality.

As a last resort, it my even be feasible to look at algorithms which can accomplish this and implement one myself.


As an analogy, for those who are that way inclined:

I am after what A* is to search: It has a definite starting/ending point, and finds the best possible route in the fastest time.

What I was hoping to avoid was what Breadth/Depth First is to Search: definite starting/ending points, but may not hit on the optimal solution once finding a local minimum, and has the potential to completely blow out computationally.

Jess Telford
  • 12,880
  • 8
  • 42
  • 51
  • What is your target format, JPG? – Pekka Nov 23 '10 at 00:48
  • Apologies; It can be .jpeg, .png, or .gif - updated question to reflect this. – Jess Telford Nov 23 '10 at 01:01
  • Can you give an idea of what the dimensions of the image are going to be? That will put an upper limit on the image size. Also, what are the requirements for image quality? After all a heavily compressed JPEG might not be acceptable to the end user. – martineno Nov 23 '10 at 04:47
  • Image dimensions will be one of: 728x90, 300x250, or 160x600. Image quality is best possible (images are eventually to be used in an advertising context), but the requirement of 50KB overrides any requirement of quality. Even though the size limit is 50KB in this situation, I am mostly interested in the process that can be adapted for any file size restriction. – Jess Telford Nov 23 '10 at 06:40

9 Answers9

12

Since there is no way to compress to a target size that I know of, I suggest looking into an indirect solution:

  1. Do some quick stats (possibly by compressing lots of images with different JPEG compression factors) to find out what the mean compressed size and its standard deviation is for a given quality level.
  2. When accepting an image, try to compress with a suitable quality. If the resulting file is too big, decrease the quality and try again. Optionally, if it turns out too small you can increase the quality too.
  3. Stop when the resulting image is "close" to your target size, but smaller.

To elaborate a bit on the maths in step 2:

If you choose your starting quality such that your mean calculated size + 3 * standard deviation < your target size, then 99.7% of compressions will result in a suitably small file on the first try (assuming a normal distribution of compressed sizes).

You can tweak the starting quality and the logic that increases or decreases it as you wish, balancing between less server load and files closer to your maximum size ("making better use" of your restrictions).

Jon
  • 428,835
  • 81
  • 738
  • 806
  • this is exactly how I do it (not programmatically, using GIMP as you can see by the slider what the filesize will be (if you have preview checked - obviously creates temp files for the preview)) when I need an image to be a certain filesize for site upload limits. – billythekid Nov 27 '10 at 01:42
  • +1 This seems to be the general consensus given that noone knows of a way to do it algorithmically. Whilst a potential solution (and well thought out, thank you!), I'd still prefer to wait and see if anyone else can come up with an algorithmic solution. – Jess Telford Nov 27 '10 at 02:35
  • would you mind give me some advice http://stackoverflow.com/questions/38144543/compress-image-to-specific-width-and-byte thanks – user1775888 Jul 01 '16 at 11:53
9

Have a look at the following package:

http://www.phpclasses.org/package/3810-PHP-Optimize-images-to-fit-in-a-given-file-size-limit.html

Pretty cool - I'd say the author deserves a beer ;)

Example image which is actually 61081 bytes (first ever jpeg image to show file size in image?):

alt text

zaf
  • 22,776
  • 12
  • 65
  • 95
1

Would it be possible to use GraphicMagick's "-limit disk 50mb" in coalition with "-list resource" so you can check this early on in (or indeed polled throughout) the process and make adjustments to suit?

http://www.graphicsmagick.org/GraphicsMagick.html#details-limit

billythekid
  • 441
  • 4
  • 16
0

I do not know of a way to automatically determine the resulting file size of an image - that's task of the library generating the image. Unless you implement the compression yourself, you cannot pre-calculate the resulting size.

What you could do is collect statistical data (image height and width and file size vs. different compression options) and do your estimations based on that data for new images.

Example:

  • 50k jpg, 100x200 is compressed 30k
  • 100k jpg, 100x200 is compressed 60k

-> when you get a 100x202px image with 59k, the compressed size will be roughly estimated 35k.

cweiske
  • 30,033
  • 14
  • 133
  • 194
  • The problem being, the images could vary wildly. When we come to jpeg compression width/height/compression ratio has no close relation to the final compressed size. – Jess Telford Nov 23 '10 at 10:37
  • This is why you could collect data about original file size and height/width vs. the compressed file size. I expect the original file size to be an indicator for the compressed one. – cweiske Nov 23 '10 at 10:39
  • The file size of the image is not exactly proportional to its dimension because the amount of compression is dependent on its complexity. Although, you can get upper bounds of this equation from one of the most complex images but, you loose some KBs by always considering the worst case. – nuaavee Nov 24 '10 at 19:39
  • Yes, that's why I said "estimate" :) – cweiske Nov 28 '10 at 17:35
0

So, if I understand this correctly - You are building a system which allows users to write some text on one of the template images that you provide. If that is correct, why are you saving the image at all? You can easily remove the 50Kb size limit by saving the user actions itself. You might be able to do something like - save the text (along with its properties and location) and the template it is on.

nuaavee
  • 1,336
  • 2
  • 16
  • 31
  • You are correct in that that is possible, however the 50kB limit is an external one which cannot be changed - the flattened image must be saved, and must be < 50kB to then be used externally. – Jess Telford Nov 24 '10 at 21:23
  • Can you please elaborate on the use case of the project? If you can give us the functional requirements (from the end user's point of view), you may get a better solution. What is the external tool you referred to? Is it something you developed or any picture viewer? – nuaavee Nov 24 '10 at 22:35
  • I have updated the question with a description of the requirements. – Jess Telford Nov 25 '10 at 02:42
0

There's only three ways to reduce the final size of any given image:

  1. reduce resolution
  2. reduce color depth
  3. reduce image complexity

The first two are under your control. If the uploaded image comes out with a file size over the limit, you can try shrinking it to the next smaller available size and see if it fits into that. Given you've only got 3 target resolutions, this wouldn't be too expensive. But if you need the "large" size to be available, then you're left with option 3.

Reducing image complexity is a nasty beast. You can try to reduce inter-pixel "noise" to produce larger areas of the same color, which will compress in GIF/PNG images very nicely. A simple blur filter could accomplish this, but it might also destroy the legibility of any fine print/text within the image. For a JPG target, you can try to lower the compression quality, but again, this can trash images if you take the quality down too low. If simple server-side transforms can't handle this, the image would have to be redone by the artist who created it in the first place.

The only practical method I can see to automate this is the compress-save-test loop you mention. Given that the final images are relatively small, this won't be that large a burden on the server. Saving a gif/png is a lightweight operation. JPG compression takes more CPU power, but again, with small images, a modern server shouldn't have any trouble doing 10 or 20 test images within a second or two.

Marc B
  • 356,200
  • 43
  • 426
  • 500
  • That's mostly a description of the alternative solution (which I have considered since asking the question). What I was hoping was instead of 'guessing' by reducing the image complexity, color depth, etc, there was an algorithmic way to push the compression toward a target file-size. Somewhat like what A* is to search compared with breadth-first or depth first and simply stopping at the first valid result. Thank you for your comments, though! – Jess Telford Nov 26 '10 at 04:23
0

Whilst the programmatic option of looping until the target filesize is reached seems to be the popular answer, there are two ways to do this with .jpeg compression:


There is a patented method by Kuo, Chun-ming, so I'm not sure of the commercial viability of utilizing it:

Method and electronic device for adjusting compression ratio of jpeg image

Which is based on this formula:

log(NSF) = (log(SF1 / SF2) / log(FileSize1 / FileSize2)) * log(Target / FileSize1) + log(SF1)

Where

SF1 is a first compression parameter
SF2 is a second compression parameter
FileSize1 is the size of the image compressed with SF1
FileSize2 is the size of the image compressed with SF2
Target is the target file size
NSF is the target compression parameter.

It's not clear if SF1, SF2, and NSF are in the range 0-1, or 0-100, etc, and if the FileSize1, FileSize2, and Target are in Bytes, KiloBytes, etc. Experiment with the correct combination here to find out the correct units.


The second method comes from Ricky D. Nguyen at MIT:

Rate control and bit allocations for JPEG transcoding

He suggests varying the data that is used for the compression while it's happening. This option may not be as feasible to implement as it requires modifications to the actual compression code itself.


From both of these examples, it is certainly possible to save out a .jpeg file with a certain target file size.

Jess Telford
  • 12,880
  • 8
  • 42
  • 51
0

Try 24-bit .png first. If that fits it will be the best quality, and you're done. Alternatively you could test some typical images and if none of them fit, you could eliminate the format from consideration altogether.

For the .gif and .jpg you'll need to search for the best fit; neither one can be predicted with enough certainty, and neither algorithm is suitable for constant bit rate encoding. You can use a binary search to find the best fit. You can choose compression factors from a predetermined list to limit the number of test compressions you need to do; for example if your list of .jpg compression factors is 4, 6, 8, 12, 18, 27, 44, 66, you would need to do 4 test compressions at most.

.gif and paletted .png are similar enough that you should just pick one and forget about the other.

It will be tough to choose between .gif/.png and .jpg based on the compression results; the artifacts introduced by each process are completely different. Again you might be best off by compressing a number of test images to your target size, and eliminating one format or the other based on eyeball testing.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
0

Purhaps you are looking at the problem from the wrong angle. By selecting to use PNG and minimizing the metadata stored in the image you will minimize the file size. This is because PNG is a Bitmap structure. So long as GMagick does not store the text as metadata it will have no impact on the file size. Only the color depth (which you can also control) will impact the size of the file. Non-interlaces without filtering the file size should be essentially the same as the template size. So long as the templates are less than 50Kb you should be alright.

Pat O
  • 1,344
  • 3
  • 12
  • 27