7

To compress a JPEG image, I can do:

$thumb = new Imagick();
$thumb->readImage("url");
$thumb->setImageCompression(Imagick::COMPRESSION_JPEG);
$thumb->setImageCompressionQuality(80);

However, I need to also compress PNG images (preserving alpha transparency) to keep sizes down. Is there a way to do it with ImageMagick?

frosty
  • 2,779
  • 6
  • 34
  • 63
  • PNG images are implicitly compressed. Any specific code or result deviation you want to let on? – mario Sep 05 '15 at 13:23
  • Implicitly compressed? Not sure what you mean.. I understand that the compression is different in some way though. – frosty Sep 05 '15 at 13:28
  • You can raise the zlib compression level, but that doesn't usually effect output size much. – mario Sep 05 '15 at 13:33
  • 1
    What @mario meant was that PNG files are _already_ compressed. There's no need (nor is it very efficient) to compress them any further. – uri2x Sep 05 '15 at 14:12
  • Then what is https://pngquant.org/ - and how does it compress them so much? – frosty Sep 05 '15 at 14:16
  • @frosty basically pngquant reduces number of colors in PNG file. The standard settings will get 24bpp image reduced to 8bpp. Lower bits per pixel means much less data to compress. – rostok Sep 06 '15 at 21:32

2 Answers2

12

pngquant effectively quantizes, or reduces the number of colours in an image till just before there is a discernible drop in quality. You can try something similar in ImageMagick like this...

First, using the built-in rose: image, check the number of colours in the image - it is 3,019:

convert rose: -format %k info:
3019

and make a PNG of it and check the size - it is 6,975 bytes

convert rose: rose.png
ls -l rose.png
-rw-r--r--@ 1 mark  staff  6975  5 Sep 20:57 rose.png

enter image description here

Now convert the rose to 255 colours and check the size - it is down to 3,691 bytes:

convert rose: -colors 255 rose255.png
ls -l rose255.png
-rw-r--r--  1 mark  staff   3691  5 Sep 21:02 rose255.png

enter image description here

Now convert the rose to 64 colours and check the size - down to 2,361 bytes

convert rose: -colors 64 rose64.png
ls -l rose64.png
-rw-r--r--  1 mark  staff  2361  5 Sep 21:04 rose64.png

enter image description here

Another way of optimising or reducing PNG filesizes is to use -strip to strip out any metadata from images - such as the date and time the picture was taken, the camera and lens model, the name of the program that created the image and the copyright and colour profiles.

Also, worth bearing in mind... normally, the colour of transparent pixels is irrelevant because you can't see them, but uniform things generally compress better. So, it may be a good idea to make all transparent pixels the same colour when saving PNG files, by using -alpha background.

Example

convert -size 512x512 xc:gray +noise random a.png                                      # create an image of random noise
-rw-r--r--@ 1 mark  staff  1576107  6 Sep 11:37 a.png                                  # 157kB

convert -size 512x512 xc:gray +noise random -alpha transparent a.png                   # recreate but make transparent
-rw-r--r--@ 1 mark  staff  1793567  6 Sep 11:38 a.png                                  # 179kB, extra transparency channel

convert -size 512x512 xc:gray +noise random -alpha transparent -alpha background a.png # make all transparent pixels black
-rw-r--r--@ 1 mark  staff  1812  6 Sep 11:38 a.png                                     # Presto!
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • How do I do this in ImageMagick? – frosty Sep 05 '15 at 21:02
  • 6
    `convert` **IS** ImageMagick. – Mark Setchell Sep 05 '15 at 21:06
  • Oh, sorry. So can I do, `convert http://example.com/image.png: -format %k info`? or? – frosty Sep 05 '15 at 21:10
  • 1
    I don't really understand how to do this with PHP.. can you make an example using `http://example.com/image.png`? Thank you. For reference here's what I mean: http://puu.sh/k1a67/0dd24b43e7.png – frosty Sep 05 '15 at 21:53
  • I don't speak much PHP but you can either call `exec('convert ...');` or use something like `$Image=new Imagick( "http://..." );$num_colors = $image->getImageColors();` – Mark Setchell Sep 05 '15 at 21:58
  • `exec('convert http://example.com/image.png -colors 255 test.png');` works like a charm, saving it as `test.png` in the root folder – frosty Sep 05 '15 at 22:10
  • As a follow up, how does pngquant determine the 'just before loss of quality'? So for example if an image has 5000 colours, does it do like 25% of that? Is there a general rule of thumb? – frosty Sep 05 '15 at 22:24
  • The crux of it is how it does the median cut - you can read the relevant source and see the reference to the SIGGRAPH paper here... https://github.com/pornel/pngquant/blob/master/lib/mediancut.c – Mark Setchell Sep 06 '15 at 08:07
  • 1
    Having said that the question was related to PHP and not command line, this answer should clarify why the PHP Imagick does not fit the job. – Valerio Bozz May 07 '20 at 09:38
  • What exactly does `-alpha background` do? When I experiment with that, instead of getting a black background (but with transparent pixels!), all that happens is that I get an empty, broken PNG. Or is that option only for greyscale?... – Gwyneth Llewelyn Nov 19 '22 at 20:05
1

Setting grayscale with -set colorspace Gray won't reduce the PNG's file-size unless these options are also used:

-define png:compression-level=9 -define png:format=8 -define png:color-type=0 -define png:bit-depth=8

This makes 8-bit grayscale with highest PNG compression. Adding those options reduced my image size 3×, because now it's one channel (grayscale) whereas before it was 3 (RGB).

Geremia
  • 4,745
  • 37
  • 43