はじめに

WordPressでは初期状態だと、GDというライブラリを使って画像のリサイズや画像ファイルそのもののサイズ削減が行われています。

これらはアップロード時に自動で実行されるため、便利な機能ではあるのですが、画像ファイルのサイズ削減に関しては別のツールを使った方がより効果的にファイルサイズを削減することができます。

この記事ではGDライブラリを使いつつ、画像ファイルのサイズ削減にはOxipngやmozjpeg、pngquantといった画像圧縮ツールを使い、そして画像アップロード時にはそれらを使ってリアルタイムに変換を行う方法を紹介します。

サーバのスペックおよび構成は以下の通りです。

  • $ cat /etc/lsb-release
    • DISTRIB_ID=Ubuntu
    • DISTRIB_RELEASE=20.04
    • DISTRIB_CODENAME=focal
    • DISTRIB_DESCRIPTION="Ubuntu 20.04.3 LTS"

Oxipng

Oxipngはマルチスレッドで実行可能な、ロスレス(Lossless)PNG圧縮エンコーダです。

もともとOptiPNGという有名な画像圧縮ツールの代替として作成されたものであり、OptiPNGを改良する際にはマルチスレッドの実装を目標としていました。

しかし、C言語で作られたOptiPNGの既存コードではそれを実現するには困難であったことから、言語をRustに変更し、その後マルチスレッドを実装しています。

Oxipngではマルチスレッドで実行できることから、従来のシングルスレッドのみを使用するツールと比べ、とても高速に処理を行うことができます。

Oxipngのインストール

ではOxipngの導入ですが、まずRustをインストールする必要があるため、$ curl https://sh.rustup.rs -sSf | shを実行して各種Rustパッケージをインストールします(オプションは1Proceed with installationを使用)。

Rustのインストールが完了したら$ source $HOME/.cargo/envで環境変数をセットします。

その後$ cargo --version$ rustc --version$ rustdoc --version$ rustup --versionでそれぞれのバージョンをチェックし、無事インストールできていることを確認します。

それが完了したら$ git clone https://github.com/shssoichiro/oxipng.gitでOxipngのリポジトリを複製します。

そして$ cd oxipngでディレクトリを移動し、/oxipng$ cargo build --releaseによってビルドを実行します。

最後に/oxipng$sudo cp target/release/oxipng /usr/local/binでOxipngを簡単に使えるようにします。

この状態で$ oxipng --versionを実行するとOxipngのバージョンを確認(執筆時点ではv5.0.1)でき、Oxipngが使用可能となりました。

Oxipng使用方法

-o 値は最適化レベルを表し、1~6,maxの順に圧縮レベルが高くなります(デフォルトは2)。

ただし、4を超えた場合の影響は少なく、実行時間に対してより多くを圧縮できることは少ないです。

-i 0 or 1はインターレースの有無を表し、0で削除、1で有効化し、アルゴリズムはAdam7PNGを使用します。

表記無しでは画像のインターレースには何もしません。

--out ファイルパスは出力先の場所およびファイル名を指定します。

--strip safe or allは画像のメタデータを削除する設定です。safeは画像の描画に影響しないメタデータを削除します(-sでも同じ設定)。

allは全てのメタデータを削除します。

ちなみに--zopfliを加えることで、zopfliのアルゴリズムを用いてより効果的な圧縮もできます(ただし処理はかなり遅いのでリアルタイム処理には向いていない)。

なお、コマンドのより詳しい情報は$ oxipng -hを実行することで閲覧できます。

以下は簡単な使用例です。

// 通常圧縮
$ oxipng image.png

// 高度な圧縮
$ oxipng -o 4 -i 0 -s image.png --out output.png

// 最高レベルの圧縮
$ oxipng -o max -i 0 -s image.png --out output.png

mozjpeg

mozjpegはJPEGファイルの圧縮効率および品質の向上を目的とした画像処理ツールです。

jpegtranは可逆圧縮(Lossless)を行い、cjpegは非可逆圧縮(Lossy)を行います。

開発はウェブブラウザのFirefoxで有名なMozillaが行っており、また、mozjpegはlibjpeg-turboからフォークされていることから、コマンドが類似しています。

mozjpegは従来の画像エンコーダーよりも圧縮効率が向上しているにも関わらず、画質の劣化が少ないことから大変便利なツールとなっています。

更にmozjpegで処理したJPEGは大半のJPEGデコーダーと互換性があるため、これまで通り問題なく画像を使用することができます。

mozjpegのインストール

まずは$ sudo apt install cmake autoconf automake libtool nasm make pkg-config git libpng-devで必要となる各種パッケージをインストールします。

次に$ git clone https://github.com/mozilla/mozjpeg.gitでmozjpegのリポジトリを複製します。

複製後は$ cd mozjpegでディレクトリを移動し、更に/mozjpeg$ mkdir build && cd buildでビルド用ディレクトリに移動します。

次に/mozjpeg/build$ sudo cmake -G"Unix Makefiles" ../を実行してMakefileを作成します。

最後に/mozjpeg/build$ sudo make installによりコンパイルされたファイルをインストールします。

これでmozjpegのインストールは完了しましたが、モジュールは/opt/mozjpeg/に存在しているため、シンボリックリンクを作成することで使い勝手を良くします。

jpegtranの場合は$ sudo ln -s /opt/mozjpeg/bin/jpegtran /usr/bin/mozjpegtran、cjpegでは$ sudo ln -s /opt/mozjpeg/bin/cjpeg /usr/bin/mozcjpegを実行します。

これでシンボリックリンクの作成も完了しました。

mozjpeg使用方法

mozjpegtranの場合、-copy noneはコメント等の画像内に含まれる情報を削除します。

mozcjpegの場合、-qualityは画像の品質を0から100までの間で選択します。デフォルトは75です。

-outfile>を使わずに出力ファイルを指定する方法です。この場合、-outfile 出力後ファイル 対象ファイルの順になっています。

どちらもコマンドの詳細は-hを付けることで閲覧することができます。

以下は簡単な使用例です。

// 可逆圧縮
$ mozjpegtran image.jpg > output.jpg

// 非可逆圧縮
$ mozcjpeg image.jpg > output.jpg

// 高度な可逆圧縮
$ mozjpegtran -copy none -outfile image.jpg output.jpg

// 高度な非可逆圧縮
$ mozcjpeg -quality 80 -outfile image.jpg output.jpg

pngquant

pngquantはPNG画像の形式を8ビットPNG形式に変換することで、ファイルサイズを削減する非可逆圧縮(Lossy)のエンコーダです。

もともとC言語(C99)で作成されていましたが、Rustで書き直された新しいバージョンが存在しており、今回はRust版を使用します。

pngquantで24もしくは32ビットのPNG画像を8ビットに変換させた場合、見た目の悪影響を抑えつつ60~80%ほどファイルサイズを縮小させることが可能です(多彩な画像では見た目の劣化が大きくなる場合があります)。

変換後の互換性も問題が無く、すべてのブラウザおよびOSで正常に描画することができます。

pngquantのインストール

Oxipngの項目でRustは既にインストール済みとみなして進めていきます。

まずは$ git clone --recursive https://github.com/kornelski/pngquant.gitでpngquantのファイルを複製します。

次に$ cd pngquantでディレクトリを移動し、/pngquant$ cargo build --releaseでビルドします。

ビルド後は/pngquant$sudo cp target/release/pngquant /usr/local/binによってpngquantを簡単に使えるようにします。

この状態で$ pngquant --versionを実行することで、現在のバージョンをチェックできます(執筆時点ではv3.0.0-beta.7)。

pngquantの使用方法

--quality 数値は画像の品質を意味しており、0から100までの数値を範囲指定することができます。

--ext 名前.pngは出力されるファイルを拡張子含めカスタマイズします。--ext = .png --forceを使うと入力ファイルごと上書きされるので注意。

--output 名前.pngは指定した場所にファイルを出力します(-oでも可能)。ただし、このオプションを使うと複数の画像を処理できません。

--forceは出力した先に同名のファイルが存在している場合に上書きします。

--speed 数値はエンコードの速度を1から11までの数値で指定します(デフォルトは4)。1は最も遅いが品質は高くてサイズは小さく、11はその逆です。

その他コマンドなどの詳細は$ pngquant -hで閲覧できます。

以下は簡単な使用例です。

// 通常圧縮
$ pngquant image.png

// 高度な圧縮
$ pngquant --quality 80  -o output.png image.png

// 最高レベルの圧縮
$ pngquant --quality=60-70 --speed 1 -o output.png image.png

WordPressでの自動画像処理

では実際にWordPressでこれらエンコーダを使用して圧縮する方法を紹介します。

先述したように、これはWordPressで画像をアップロードした際に自動で圧縮を行う設定です。

これらはすべてfunctions.phpに記載するだけで機能しますが、圧縮率に関してはそれぞれチェックして調整することをおすすめします。

また、今回の例ではJPEG画像に限り非可逆圧縮後に可逆圧縮も施していますが、可逆圧縮のみを行いたい場合など、これも好みで変更してください。

functions.php

/** 省略 **/

// 無駄に圧縮されるため、WordPress側の自動圧縮は無効化
// WPによる自動画像圧縮を無効
add_filter('jpeg_quality', function($arg){return 100;});
add_filter( 'wp_editor_set_quality', function($arg){return 100;});

// 画像のアップロード後に画像を圧縮
function compress_images($metadata, $img_id){

    // アップロードした画像のフルパス
    $org_path = get_attached_file( $img_id );

    // アップロードした画像のファイルタイプを取得
    $img_type = get_post_mime_type( $img_id );

        // ファイルタイプが [JPEG(JPG)] の場合
        if( $img_type == 'image/jpeg' ){

            // 元画像を圧縮 START

            // 非可逆圧縮後に可逆圧縮(品質80パーセント)
            $output = shell_exec("mozcjpeg -quality 80 '$org_path' > '$org_path.jpg' && mozjpegtran -optimise -copy none '$org_path.jpg' > '$org_path' && rm '$org_path.jpg'");

            // 元画像を圧縮 END

            foreach ( $metadata['sizes'] as $size => $value) {

                // 元画像に対する全てのサムネイルを圧縮 START

                // 全てのサムネイルのフルパスを取得
                $thumb_path = dirname($org_path).'/'.$value[ 'file' ];
 
                // 全てのサムネイルを非可逆圧縮後に可逆圧縮(品質80パーセント)
                $output = shell_exec("mozcjpeg -quality 80 '$thumb_path' > '$thumb_path.jpg' && mozjpegtran -optimise -copy none '$thumb_path.jpg' > '$thumb_path' && rm '$thumb_path.jpg'");

                // 元画像に対する全てのサムネイルを圧縮 END

            }
        }

        // ファイルタイプが [PNG] の場合
        if( $img_type == 'image/png' ){

            // 元画像を圧縮 START

            // 以下は必要に応じて使いたいものをコメントアウト解除してください

            // 可逆圧縮(効果小)
            $output = shell_exec("oxipng '$org_path'");

            // 可逆圧縮(効果中)
            // $output = shell_exec("oxipng -o 6 -s '$org_path'");

            // 最大レベルの可逆圧縮(効果中~大・負荷大)
            // $output = shell_exec("oxipng -o max -i 0 -s  '$org_path'");

            // 非可逆圧縮(効果中~大、負荷中、品質65~80)
            $output = shell_exec("pngquant --quality=65-80 --speed 1 --ext .png --force '$org_path'");

            // 元画像を圧縮 END

            foreach ( $metadata['sizes'] as $size => $value) {

                // 元画像に対する全てのサムネイルを圧縮 START

                // 全てのサムネイルのフルパスを取得
                $thumb_path = dirname($org_path).'/'.$value[ 'file' ];

                // 以下は必要に応じて使いたいものをコメントアウト解除してください

                // 可逆圧縮(効果小)
                $output = shell_exec("oxipng '$thumb_path'");

                // 可逆圧縮(効果中)
                // $output = shell_exec("oxipng -o 6 -s '$thumb_path'");

                // 最大レベルの可逆圧縮(効果中~大、負荷大)
                // $output = shell_exec("oxipng -o max -i 0 -s  '$thumb_path'");

                // 非可逆圧縮(効果中~大、負荷中、品質65~80)
                $output = shell_exec("pngquant --quality=65-80 --speed 1 --ext .png --force '$thumb_path'");

                // 元画像に対する全てのサムネイルを圧縮 END

            }
        }

        return $metadata;

}
add_filter( 'wp_generate_attachment_metadata', 'compress_images', 10, 2 );

/** 省略 **/

以上の設定により、画像アップロード時に自動で元画像および全てのサムネイル画像が圧縮されます。

おわりに

GDライブラリの代替として、ImageMagickと呼ばれるライブラリがあるのですが、これは多くの脆弱性を指摘されたり、そもそもGDとの差をそこまで感じられないことから今回は採用を見送っています。

初期状態のWordPressでは多くのサムネイルを作成するため、サーバの空き容量が少ない場合は割と逼迫しがちになりますが、紹介したこれらエンコーダを使用することでより多くのスペースを確保することができます。

画像ファイルの削減だけでなく高品質な画像を保ったまま圧縮できることから、ウェブサイトそのものの品質を向上させることができるでしょう。

参考資料