plone / Products.CMFPlone

The core of the Plone content management system
https://plone.org
GNU General Public License v2.0
268 stars 194 forks source link

PNG filesize growth with plone.scale #1550

Open mamico opened 8 years ago

mamico commented 8 years ago

plone.scale increases the file size of images of type PNG.

test https://github.com/mamico/plone.scale/commit/0b6c36f40039cad40de27744c70bbab726a3d517

jensens commented 8 years ago

Well, this is done by PIL (Pillow). Anyway, This might be the case because PIL.Image.ANTIALIAS is always applied. In case of PNG it might be the right setting, depending on the image.

jensens commented 8 years ago

Just found: we may want to pass optimize=True for PNG on save - opinions on this?

pro: smaller file size.

con: needs slightly more memory/computing.

agitator commented 8 years ago

+1 for True

mamico commented 8 years ago

Probably I'm loosing something or or I'm looking on the wrong place... I see optimize=True here: https://github.com/plone/plone.scale/blob/master/plone/scale/scale.py#L63

I did some test here (https://gist.github.com/mamico/21dbd53fa5de68f3720065ed05dd89d1) with latest Pillow (3.3.0) using the image in plone.scale test (https://github.com/plone/plone.scale/blob/master/plone/scale/tests/data/logo.png). Original size: 1671 bytes, with plone.scale (without dimension changes) the size increase to 8901.

I'm not a PIL expert, and I have no solution, but the problem seems to be changing from Palette to RGBA, and using quality parameter on Image.save.

jensens commented 8 years ago

Before latest plone.scale changes the image was transformed to jpeg, which resulted in alpha channel loss. Now png is kept. But because scaling in a indexed palette usually results in crappy results it's transformed to RGBA. I am not sure if going back to indexed after scaling is a good idea. Size wise probably yes. Quality wise I am skeptical. Any thoughts?

davilima6 commented 8 years ago

I talk mostly from my experience with Photoshop, so I may be mistaken, but I believe images wouldn't suffer noticeable damage, specially if they were originally indexed.

Since image quality doesn't really improve when going RGBA, even after scaling removes/creates some pixel (oftenly generating new colors on the way) the dithering algorithms are usually enough to keep imperfections undetectable to the human eye (at least without zooming).

I suggest we define a set of images and test them in a branch. We should aim for a varied mix: flat colors, gradients, drop-shadows, anti-aliased text, line-arts and even natural-objects representations (which would rarely be uploaded as indexed palette but anyway) etc.

hvelarde commented 8 years ago

the main difference between the two formats is compression: JPEG is lossy, while PNG is lossless; JPEG must be used for photographs, while PNG must be used for line art. a PNG must not be transformed into a JPEG or you should end with a crappy image; a JPEG must not be transformed into a PNG or you should end with a bigger file. resuming: scales must be the same image type as the original image uploaded to Plone.

also, maybe is time to use something beyond PIL to deal with scales, as it doesn't do a good job on optimizing the resulting images. seems to me the best modules available are JS based and can be installed via Node.js.

see discussion in: https://github.com/collective/collective.behavior.featuredimage/issues/25

mauritsvanrees commented 8 years ago

@didrix Didn't you do something with this in a branch already?

ghost commented 8 years ago

@mauritsvanrees Ah, yes. This may be because the original image is in Palette (P) mode. When the original image is in P mode (8bit/pix) plone.scale will convert it to RGBA (32bit/pix) because antialiasing can result in an image with more than 256 colors. Now your image will be about 4x the size that you would expect it to be.

In my plone.scale branch (https://github.com/plone/plone.scale/tree/image-mode-optimization) I try to be a little smarter in picking the right image mode. This can help but is definitely not a guarantee that the scaled image is smaller in size than the original. You are running into the limits of the png specification or you have to make a concession in quality (disabled/reduced antialiasing to keep P mode for example, as @davilima6 is suggesting).

davilima6 commented 8 years ago

Just to be clear my point is not to disable/reduce antialiasing but instead that after those are added to an RGBA version that came from an indexed palette, they can be converted/dithered back to P mode without perceivable loss. I put together a few tests in Photoshop.

TL;DR: I think the quality x size relation is indeed worthy for reindexing, still unsure if the RGBA conversion is (but doesn't hurt much either).

Below are PNGs for original's 1x RGBA and indexed versions (click to enlarge):

Now both versions upscaled 4x (I didn't reduce so we could actually see the differences):

Now with RGBA conversion before resizing and, at last, my suggestion (index > rgba > resize > index):

As I said after these tests I'm not sure how much the pre-resize RGBA conversion step really helps. We indeed get different distribution of tints but IMO not enough to justify image size. I see:

hvelarde commented 8 years ago

removing metadata and compressing will also help but... what about trying to learn of what others have done on image optimization before? @keul listed some image optimizers on https://github.com/collective/collective.optimage; if the PR associated with this issue is going to solve only the PNG use case, we can leave the door open for the others.

@davilima6 the image you selected is, IMO, the typical example of when not to use PNG; that image is smaller as a JPEG as a quick test shows: the original is 76K while the JPEG version is 75K (created using the GIMP using the same quality settings as the original, 85% compression).

save_money_with_upcycling___blender_3d__by_tee3d-da642lq

@mamico the image used in plone.scale tests (Simplon's logo) is also a bad example of a PNG: it has a palette of 99 colors instead of just 4 (2 greens, black and white); that could explain why the scales are bigger than the original as they could contain even more colors.

jensens commented 8 years ago

Given the above analysis I would say its save to go back to indexed mode after scaling.

@didrix Is your branch ready for a PR? If so: Go go go :)

@hvelarde if we open plone.scale for further optimizations I'd make it pluggable first, i.e. by looking for adapters providing further functionality.

davilima6 commented 8 years ago

@hvelarde, it's not only your opinion: this image should never be saved as PNG for production. However file size wasn't a goal when making this analysis. Instead I wanted to generate the maximum amount of noise on conversions and resizes over more complicated graphic effects we do find in indexed images - as mentioned, gradients, shadows and edges that would likely receive anti-alias treatment when resizing, thus creating difficult to reindex pixel.

If you still think it was a bad choice for this analysis and that some other kind of image would behave differently, feel free to point me to other samples and I can repeat the tests. Thanks.

hvelarde commented 8 years ago

@davilima6 I though the title of the issue was "PNG filesize growth with plone.scale" ;-)

davilima6 commented 8 years ago

@hvelarde, it seems you missed @jensens question about quality vs reindexing one comment before my first.

hvelarde commented 8 years ago

@davilima6 you're right, now I understood the whole thing; sorry about the noise.