長ったらしいタイトルになってしまったのですが、こういうのに対処する案件が発生してしまったので書きます。

環境


PHP7.3です。

きっかけ


まず下の画像を見てください。これは最近絶賛構築中のフォトギャラリーのとある画像のサムネイルをICCプロファイル別に比較したものです。いつまで残しておくかはわかりませんが、実物はここにあります。

色がね。違いませんか??

このフォトギャラリーではOSSでPHP製です。ファイルアップロード時に元画像からサムネイルを自動生成します。私はAdobe Lightroom ClassicProPhoto RGBというICC(カラープロファイル)で書きだしていた[1]のですが、どうもアップロードしたファイルのサムネイルがくすんでみえる。なんでだろうと思って調べたところsRGBの時のみ変わらない(問題ない)ことに気づきました。

ICCについて


ICCに関しては下記のあたりをが詳しいのでそちらを参照するほうが良いと思います。

Pixive: Pixivにアップロードすると色がおかしくなる問題のまとめ
Google: 色スペース
Adobe: 色に関する難問

Imagick


ImagickはImagemagickのPHP拡張的なやつなので、ようするにImagemagickという認識でいいと思います。このOSS(フォトギャラリー)はPHP製でサムネイル生成にImagickかもしくはGDを使います。(設定で変更可能)

このサムネイル生成時にImagick::stripImageを使って画像のメタ情報を削除しているのがわかりました。

1
2
3
4
5
6
$image = new \Imagick();
$image->readImage($source);
$image->setImageCompressionQuality($this->compressionQuality);

// Remove metadata to save some bytes
$image->stripImage();

Lycheeのコードより

サイズを下げるためにメタ情報を削除するのは普通のことなので特に気にならないのですが、これによってICCが消えてしまいます。どうもImagemagickはICCが存在しなければsRGBとみなすようなのでICCがsRGB以外の画像をstripImageすると画像書き込み時に色がくすんだようになってしまうようです。

修正する


幸いにも前述のImagick::stripImageのサンプルコードにICCだけ残して残りのメタ情報を削除するコードが載っていました。

正確には…

  • 削除前にICC情報を抽出
  • メタ情報を一旦削除 stripImage
  • 抽出しておいたICCだけ再度セット

という処理になります。コードは下記のとおりですが、これは前述のImagick::stripImageのドキュメントのコメントに記載されているものそのままです。

1
2
3
4
5
$profiles = $image->getImageProfiles('icc', true);
$image->stripImage();
if (!empty($profiles)) {
$image->profileImage('icc', $profiles['icc']);
}

この実装をやったところICCがProPhoto RGBでもサムネイルの色のくすみはなくなりました。また、テストで使った画像の場合は1KBだけの増加ですみました。ただ、他の画像で試してないのとそもそも画像周り全くわかってないのでもっと適切なサンプルでのテストみたいなのがいるかもしれません。

というわけで、PRだしました。

imagick: keep ICC profile when crop and scale image

ほぼ間違いなく取り込んでもらえると思うけど、これ取り込んでもらうまで写真のアップロードは中断します。

GDについて


GDというのはlibgdという画像処理のライブラリですが、こちらも同様の問題があるようです。が、どうもこちらは今のところは解決方法がないように見えます。

その他


まずくすんでいるのに気づくまで時間がかかって、その後もフォトギャラリー側がサムネイルに対してそういう効果を付与しているのかな??などと解釈して合計200枚近くアップロードしてしまったのですが、う~ん、もうアップロードしたものに関してはなおさなくてもいいかな…。

こういうことをやった結果、未だに旅行の記事が書けていないとかいう危機的状況なので頑張って記事書こうと思います。おわり。


  1. 特にいじった記憶がないのでデフォルト?? ↩︎