本文へスキップ
画像とメディア

WordPressが画像を品質82%で圧縮してしまうのを止める方法

WordPressは生成するすべてのJPEGを、0〜100スケールの品質82で再エンコードします。このデフォルト値は WP_Image_Editor::get_default_quality() にあり、

公開

WordPressは、生成するすべてのJPEGを0〜100スケールの品質82で再エンコードします。このデフォルト値は WP_Image_Editor::get_default_quality() にあり、アップロードした画像からWordPressが切り出すすべての派生サイズ(サムネイル、中サイズ、medium_large、大サイズ、そしてテーマが登録するすべての追加サイズ)に適用されます。写真であれば、82はファイルサイズとノイズのバランスが取れた妥当な値です。しかしスクリーンショット、UIキャプチャ、グラフ、ロゴ、シャープな文字やフラットな色を含むものについては、82は目に見えて画質を損ないます。文字の周りにモスキートノイズが這い回り、なめらかなグラデーションにバンディングが現れます。

これを変更するには、フィルターを1つ追加してサムネイルを再生成します。特にスクリーンショットについては、そもそもWordPressにJPEGを渡さないようにするのが、たいていより良い解決策です。

82という数字は実際どこから来るのか

WordPressは4.5(2016年)で、ページ容量の最適化としてJPEGのデフォルト値を90から82に引き下げました。この値は wp-includes/class-wp-image-editor.php 内の get_default_quality() が返すもので、有効なエディター(ホストにImagickがあれば WP_Image_Editor_Imagick、なければ WP_Image_Editor_GD)がリサイズ後のファイルを書き出すたびに読み込まれます。

重要なのは、82が1回だけでなく派生サイズごとに適用されるという点です。1枚の画像をアップロードすると、WordPressはそれを何度もエンコーダーに通し、そのたびに品質82で再量子化します。アップロードした元ファイル自体は、ビッグイメージのしきい値(長辺2560px)未満であれば手を加えられません。しきい値を超える場合、WordPressは品質82で再エンコードした -scaled.jpg を生成し、そちらを「フルサイズ」として配信します。つまり「フル解像度の画像はきれいなのに、ページ上のバージョンは粗く見える」という現象は、ほぼ必ず srcset が品質82のサブサイズか -scaled ファイルを選んでいるのが原因です。

品質を変更する方法

フックすべきフィルターは1つです。wp_editor_set_quality が現在の標準的なフィルターで、JPEGとWebPの両方で発火し、MIMEタイプを受け取るので分岐できます。

add_filter( 'wp_editor_set_quality', function ( $quality, $mime_type ) {
    if ( 'image/jpeg' === $mime_type ) {
        return 92; // up from 82
    }
    return $quality;
}, 10, 2 );

これは小さなmu-plugin(wp-content/mu-plugins/)か、サイト固有のプラグインに入れます。functions.php でも動作しますが、テーマを切り替えた途端に設定が失われます。

古い jpeg_quality フィルターが推奨されているのも見かけるでしょう。これも今なお発火します(WordPressは wp_editor_set_quality の直後にJPEG限定で適用します)が、両方をフックする理由はありません。wp_editor_set_quality は古いフィルターがやっていたことをすべてカバーし、さらに多くのことができます。

みんなが忘れがちな点が1つあります。このフィルターは、有効化された後にエンコードされる画像にしか影響しません。 wp-content/uploads にすでに存在するサムネイルはすべて品質82で書き出されており、82のままです。再生成しましょう。

wp media regenerate --yes

WP-CLIを使っていない場合は、Regenerate Thumbnails プラグインが管理画面から同じことをしてくれます。

ある品質値に決める前に、推測に頼るのではなく、その品質が実際に あなたの 画像に何をするのかを見ておく価値があります。当サイトの WordPress JPEG品質ツール は、サンプル画像をブラウザ上であらゆる品質レベルで再エンコードするので、ノイズが現れる様子を目で確認できます。設定ファイルの中の数字では決して分からない形で、スクリーンショットにおける82、90、100の違いを一目瞭然にしてくれます。

スクリーンショットやフラットなグラフィックにはより良い解決策がある

JPEGの数字を上げるのは対症療法です。スクリーンショットの本当の問題は、JPEGがそれらにとって間違ったコーデックだということです。JPEGのDCT圧縮は写真的なグラデーション向けにチューニングされており、文字やUIのクロームのような黒と白のはっきりした境界では破綻します。まさにこれが、品質82が写真よりもスクリーンショットではるかに悪く見える理由です。

代わりに PNG をアップロードしましょう。WordPressはPNGのサブサイズをPNGのまま保持し、PNGは可逆圧縮です。品質フィルターはPNGには手を触れないので、どのサイズでもノイズは生じず、クリーンなリサンプリングだけが行われます。スクリーンショットの場合、よく最適化されたPNGは、同じ画像の高品質JPEGよりも 小さく なることさえよくあります。フラットな色はPNGではよく圧縮され、JPEGではひどく圧縮されるからです。

JPEGのような境界の劣化なしに、非可逆フォーマットの小さいファイルサイズが欲しいなら、WebPが中間の選択肢です。ただしその品質も、上で示したのと同じ wp_editor_set_quality フィルターで、image/webp に分岐させて制御します。

やってはいけないこと

品質をサイト全体で100に設定してはいけません。 これはフォーラムのスレッドの半分で最も票を集めている回答ですが、間違いです。品質100のJPEGは可逆ではありません(依然として量子化されます)が、目に見える利点もなく、サイト上のすべての 写真 を大幅に肥大化させ、LCPとCore Web Vitalsを悪化させます。それを必要とするフォーマットについてだけ数字を上げるか、グラフィックはフォーマットを変えましょう。サイト全体で圧縮を無効化してはいけません。

class-wp-image-editor.php を編集してはいけません。 コア内の get_default_quality() の戻り値を変えても、次の wp core update で何の通知もなく上書きされるまでしか持ちません。フィルターを使いましょう。

「圧縮を避ける」ために画像サイズを無効化してはいけません。 サムネイル生成をオフにしても再エンコードは止まりません。それどころかレスポンシブ srcset を壊してしまい、スマートフォンでもブラウザがフルサイズの画像をダウンロードするようになります。別の問題で、しかもより悪い結果です。

再アップロードすれば既存の画像が直ると思い込んではいけません。 フィルターを設定しても再生成のステップを飛ばせば、古い派生サイズはすべて品質82のままです。フィルターは今後生成されるものにしか効きません。

画像が2560px未満なのに -scaled ファイルのせいにしてはいけません。 そのパスは2560px未満の画像では決して実行されません。もし実際にそのパスに引っかかっていて、手を加えていない元の画像を配信したいなら、それは別の設定です。スケール画像の無効化を参照してください。

それでも解決しない場合は

再生成後も特定のサイズがおかしく見えるなら、ページが実際にどのファイルを配信しているかを確認しましょう。画像を新しいタブで開き、ファイル名が寸法のサフィックス(-1024x768.jpg)や -scaled.jpg で終わっていないかをチェックします。画像サイズインスペクターは、1回のアップロードからWordPressが生成するすべての派生サイズを表示するので、srcset がブラウザに渡しているサイズを正確に把握でき、間違ったファイルをデバッグするのをやめられます。