rokka-io / imagine-vips

libvips adapter for php imagine
https://rokka.io
Other
41 stars 8 forks source link

Question regarding PNG quality setting #16

Closed Sebobo closed 3 years ago

Sebobo commented 3 years ago

Imagine uses the standard quality to set the png_compression_level for pngs and uses quality as fallback for all formats.

Now the Vips integration introduces png_quality but doesn't fall back to quality. https://github.com/rokka-io/imagine-vips/blob/86cff5a207a5510a1f6316f189a4fd7413b685ce/lib/Imagine/Vips/Image.php#L55

So when using Imagine with various libraries one easily misses to set png_quality which results in huge images that are much larger than their original when slightly scaled for example.

Setting png_quality leads to a result which is slightly worse than what IMagick can achieves.

So my question is whether the fallback shouldn't be introduced and what quality actually does? I didn't fully understand the consequence.

The final result f.e. should be in my opinion that IM scaled pngs with a quality of 90 should be nearly identical to the same png scaled with Vips and the same quality level.

chregu commented 3 years ago

As far as I can see, png_quality only is for quantisation, we enable that, when png_quality is less than 100. That's why it's much smaller. But results in not perfect PNGs (but usually you don't see a difference with most pictures).

There's no png quality in general, just compression, which we set by default to 7.

See also https://libvips.github.io/libvips/API/8.7/VipsForeignSave.html#vips-pngsave

I'm not sure, what IM does by default when saving PNGs. If I just convert a jpg on the commandline with vips and IM to a png with the default settings, the resulting images are about the same size.

Sebobo commented 3 years ago

Thx for the response!

I created a reduced test script using imagine with all 3 libraries today to see the differences easily (my main usage is in Neos CMS projects): https://gist.github.com/Sebobo/94dd8ea909d2ed6ae813b7df70c20dc1

Without any quality setting Vips and GD have the same resulting file size. And IM is less than 50%. When setting quality to 90 for all. GD and IM get better and Vips stays the same as it ignores the quality. And when setting png_quality to 90. Vips most of the time wins.

When additionally enabling png_compression_level then Vips shines even more. Just the behaviour of the default or quality = 100 confuses me as it leads to huge files.

I mean the result of Vips even with quality 100 should never be larger than the original right?

chregu commented 3 years ago

If the original is differently compressed or quantisazed, the output may well be larger. Vips doesn't compare with the input or something like that.

I hesitate a little to map quality to png_quality. Since png_quality < 100 does quantisation and one maybe doesn't want that. It should be an explicit setting, IMHO.

chregu commented 3 years ago

I don't know what GD or IM do, when setting quality to 90, on the other hand. As I'm soon off to holidays for some time, I don't have the time to investigate that currently

Sebobo commented 3 years ago

Thanks for taking time whenever you can!

From reading the Imagine code ImageMagick only sets png_compression_level and png_compression_filter. And the compression level gets derived from the quality setting (if not set).

So maybe it's just that the setting is confusing as each quality setting does something else and not for every library.

But still I wonder if there is something off with the quality = 100 setting.

I will also do a bit more research.

chregu commented 3 years ago

you can also ask on https://github.com/libvips/libvips (just make an issue). @jcupitt is very helpful in clearing things up, if you have in-depth questions.

jcupitt commented 3 years ago

Hi, libvips just calls libpng with the default settings, you need to turn the knobs yourself.

For photographic sources, libvips and IM are very similar:

john@yingna ~/pics $ vips copy k2.jpg vips.png
john@yingna ~/pics $ convert k2.jpg im.png
john@yingna ~/pics $ ls -l vips.png im.png
-rw-rw-r-- 1 john john 3222699 Feb  5 10:08 im.png
-rw-r--r-- 1 john john 3255372 Feb  5 10:07 vips.png

libvips defaults to compression 6 (the libpng default), perhaps IM defaults a little higher.

For line art, it's more complex. PNG supports palettized images, ie. images with a fixed set of up to 256 RGBA colours, and these will compress quite a bit more. For example, this image:

lion

Can be written as a regular RGBA PNG, or as a palettized image. In fact, it has less than 16 unique colours, so you only need 4 bits per pixel:

john@yingna ~/pics $ vips copy lion.png rgba.png
john@yingna ~/pics $ vips copy lion.png pal.png[palette,bitdepth=4]
john@yingna ~/pics $ convert lion.png im.png
john@yingna ~/pics $ ls -l im.png rgba.png pal.png 
-rw-rw-r-- 1 john john 79342 Feb  5 10:22 im.png
-rw-r--r-- 1 john john 17337 Feb  5 10:18 pal.png
-rw-r--r-- 1 john john 79530 Feb  5 10:17 rgba.png

But this isn't always true -- many line art images contain smooth gradients, and these will often have more than 256 colours. Here you have a choice: use RGBA, or pick the "best" set of 256 and do some dithering. Choosing the best is a complex process and needs a lot of CPU. The Q argument to pngsave sets the amount of CPU that will be spent picking the best 256.

Even if you find a good set of 256 colours, it still might not work well. If you have a lot of alpha and need to present deep shadows on a dark background, or pale shadows on a light background, you can get nasty "sparkling" effects from alpha dithering. WEBP has a great feature where you can use different techniques for different image bands, so you can quantize the main RGB, for example, but keep the alpha smooth.

tldr: picking a good set of PNG save settings is complex and depends both on the image content and how you plan to use it. libvips doesn't attempt to pick a good set automatically, it just sticks to the libpng defaults and leaves these complex choices to the user.

pngcrush is an interesting project that attempts to optimise PNG settings automatically. It's rather a slow and complex process.

Sebobo commented 3 years ago

Hi @jcupitt, thx for the detailed response, I learned a few new things! :) And I see the png quality topic is complex.

Now I'm trying to find good set of defaults for most users of our CMS which can apply to all of their images.

Medium and large projects in my bubble use some post optimisation with various jpeg and png libraries like optipng, pngquant, jpegoptim, etc. or some external service. So in their case the size problem in production is already solved.

But many projects also don't have that step and rely on whatever is installed in their hosting, so in our case GD, IM or VIPS. And some have an editor system where the optimisations are not active and only the publishing to live will trigger them. IM is usually the main choice and doesn't cause many issues, except it's too resource hungry for projects with more images and variants.

So if the conclusion is that the libraries go the "safe" way in terms of quality and the user has to know what they are using and what settings to use, we rather have to come up with some adjusted defaults and some kind of a guide for people setting up a new project and more details on what each setting does depending on the used library.

@chregu I'm fine if you want to close this then.

chregu commented 3 years ago

What we do on rokka.io is to first just output a "default settings" (and quickly generated) version of the image and then optimize them through a queue system with using pngcrush and similar tools for subsequent deliveries through a cache. So best of both worlds: Fast on-demand image generation on first requests, but highly optimized images later.

But that's not really an option for people with just standard hosting installs. Or is getting complex fast.

Thanks for all the inputs. I'll close the issue for now, but of course I'm still open for better default setting proposals or ideas.