---
title: Optimizing PNG images
aliases: /posts/2020-01-20-optimizing-png-images
summary: >
  A quick notice about three very handy tools to optimize PNG images on
  the command line. They work on linux and macOS. Use jpegtran for JPG
  images.
categories: [computerstuff]
tags: [zsh-shell, fish-shell]
date: 2020-01-20T20:34:20+01:00
lastmod: 2024-09-28T23:48:06+0000
---

## Optimizing a bunch of images in a directory

I found these commands here coincidentally on a
[pull request](https://github.com/xianmin/hugo-theme-jane/pull/266) at Github.
I found them quite handy :+1:

```console
$ find . -type f -name "*.png"  -print0 | xargs -0 -I {} optipng -nb -nc "{}"
$ find . -type f -name "*.png"  -print0 | xargs -0 -I {} advpng -z4 "{}"
$ find . -type f -name "*.png"  -print0 | xargs -0 -I {} pngcrush -rem gAMA -rem alla -rem cHRM -rem iCCP -rem sRGB -rem time -ow "{}"
```

## Quickly optimizing only one image

I've set up a function in my `.zaliases` file for this to be done on a single
image aswell:

```zsh
function opti() {
  optipng -nb -nc "$*";
  advpng -z4 "$*";
  pngcrush -rem gAMA -rem alla -rem cHRM -rem iCCP -rem sRGB -rem time -ow "$*";
}
```

To run all three commands on a single image I just call it like that:

```console
$ opti image.png
```

That's all the magic it needs.

## Use jpegtran for JPG images

```console
$ jpegtran -copy none -optimize -progressive -outfile output.jpg input.jpg
```

## Install these tools on your system

On debian or ubuntu

```console
$ sudo apt-get install optipng pngcrush advancecomp
```

On Arch based distros using pamac

```console
$ sudo pamac install optipng pngcrush advancecomp
```

(advancecomp is in AUR)

On macOS

```console
$ sudo port install optipng pngcrush advancecomp
```

or if you use homebrew

```console
$ brew install optipng pngcrush advancecomp
```

You may know other package managers commands, but I only use these.

## An example

### By filesize

The files taken from the snapshot tool on my macbook.

```plain
 33K 00_locales.png
 61K 01_control-software.png
157K 02_mmdvmhost.png
184K 03_general.png
187K 04_dmrconfig.png
 69K 05_exp_mmdvmhost-dmrnetwork.png
212K 06_exp_dmrgw-dmrnetwork1.png
236K 07_exp_dmrgw-dmrnetwork2.png
```

Three to four minutes later (all three commands):

```plain
 17K 00_locales.png
 33K 01_control-software.png
 81K 02_mmdvmhost.png
 98K 03_general.png
 97K 04_dmrconfig.png
 32K 05_exp_mmdvmhost-dmrnetwork.png
127K 06_exp_dmrgw-dmrnetwork1.png
144K 07_exp_dmrgw-dmrnetwork2.png
```

### By view

```plain
 25K opti_01.png
 13K opti_02.png
```

![Original image](opti_01.png "This is the unmodified image: `opti_01.png`") ![Optimized image](opti_02.png "And this is the optimized image: `opti_02.png`")

Do you see much difference?

## Using this with the **fish** shell

I added this to my fishs configuration (when I used fish for a while).

```fish
# file: "~/.config/fish/functions/opti.fish"
function opti --description "Optimizes .png files"
    # Author: Dominic, OE7DRT <dominic@oe7drt.com>
    # 2021-04-17
    set -e missing

    for program in optipng advpng pngcrush
        if \! command -v $program > /dev/null
            set -a missing $program
            continue
        end
    end

    if test -n "$missing"
        echo "Could not find executables: $missing"
        return 1
    end

    if test -z $argv[1]
        echo "usage: opti <files...>"
        return 1
    end

    set count (count $argv)

    for i in (seq 1 $count)
        if test ! -f $argv[$i]
            echo "Could not read file $argv[$i]..."
            continue
        end

        optipng -nb -nc "$argv[$i]";
        advpng -z4 "$argv[$i]";
        pngcrush -rem gAMA -rem alla -rem cHRM -rem iCCP -rem sRGB -rem time -ow "$argv[$i]";
    end
end
```

And I made one for the jpeg version too:

```fish
# file: "~/.config/fish/functions/jopti.fish"
function jopti --description "Optimizes .jpg files"
    # Author: Dominic, OE7DRT <dominic@oe7drt.com>
    # 2021-08-07
    set -e missing

    for program in jpegtran
        if \! command -v $program > /dev/null
            set -a missing $program
            continue
        end
    end

    if test -n "$missing"
        echo "Could not find executables: $missing"
        return 1
    end

    if test -z $argv[1]
        echo "usage: jopti <files...>"
        return 1
    end

    set count (count $argv)

    for i in (seq 1 $count)
        if test ! -f $argv[$i]
            echo "Could not read file $argv[$i]..."
            continue
        end

        jpegtran -copy none -optimize -progressive -outfile "$argv[$i]" "$argv[$i]"
    end
end
```