PHPで写真のExif回転に対応する
PHPで写真をアップロードしたり、あるいは既存の写真を読み込んで表示したりする際、スマホで見るときと違う傾きになってしまって困ったことはないでしょうか。
例えば、スマホを縦持ちで撮影して、スマホ上は問題なく表示されているのにPHPを通すと90度傾いて表示される、みたいな。
特にiPhoneでよくあるケースなんですけど、写真の記録方向は常に一定で、表示するときに回転(Exif Orientation)してね、という仕組みになっている場合の話です。Exifに対応していないと記録されたとおりに表示するのでおかしなことになるんですよね。
というわけで、PHPでもそのExif回転情報を読み取って、その指示どおりに画像ファイル自体を回転させちゃおう、というのが今回の目的です。
PHPでExifデータを扱うには
Exifの拡張モジュールが必要です。
- Linuxの場合はconfigure時に ‐‐enable-exif を指定。
- Windowsの場合はphp.iniでextension=exifを有効に。
あと、画像回転にgd2の関数を使うので、そちらも有効に。
(imageflip関数を使うため、PHP5.5以降を想定しています)
PHPが使える最近のレンタルサーバーならどちらも有効になっているハズです。
PHPでExif情報の読み取り
<?php
$exif_data = exif_read_data($filename);
?>
拍子抜けで申し訳ないんですが、Exif情報の読み取りはマジでこの1行だけでOKです。
連想配列にExif情報がたっぷり入ってきますが、回転情報で使うのは $exif_data['Orientation'] のみ。
写真にExif情報が含まれていれば、この $exif_data['Orientation'] に1~8の数値が入ります。
(写真からExif情報が除去されていれば当然なにも入ってきません)
Exif Orientationの番号ごとの回転方向
これが一番の問題というか、Exifの回転情報は1~8までの数字で指定されているのですが、これがとってもわかりづらい。
例えば、右方向(時計回り)に90度ずつ1→2→3→4とか番号が付いていればわかりやすいのだけれど、そうじゃないんですよね。
番号順に覚えようとすると頭がこんがらがってしまうので、もう時計回り固定で覚えることにしちゃいました。
こんな感じ。
Exif Orientationの番号が 1,8,3,6 だったら、時計回りに90度ずつ
2,7,4,5だったら写真を左右反転してから、時計回りに90度ずつ
ということです。
もっとわかりやすい覚え方もありそうですけど、とりあえずぼくはこんな感じ。
PHPでExif情報に従って写真を回転させる例
/************************************************
* Exif情報のOrientationによって画像を回転して保存
*
************************************************/
function imageOrientation($filename, $orientation)
{
//画像ロード
$image = imagecreatefromjpeg($filename);
//回転角度
$degrees = 0;
switch($orientation) {
case 1: //回転なし(↑)
return;
case 8: //右に90度(→)
$degrees = 90;
break;
case 3: //180度回転(↓)
$degrees = 180;
break;
case 6: //右に270度回転(←)
$degrees = 270;
break;
case 2: //反転 (↑)
$mode = IMG_FLIP_HORIZONTAL;
break;
case 7: //反転して右90度(→)
$degrees = 90;
$mode = IMG_FLIP_HORIZONTAL;
break;
case 4: //反転して180度なんだけど縦反転と同じ(↓)
$mode = IMG_FLIP_VERTICAL;
break;
case 5: //反転して270度(←)
$degrees = 270;
$mode = IMG_FLIP_HORIZONTAL;
break;
}
//反転(2,7,4,5)
if (isset($mode)) {
$image = imageflip($image, $mode);
}
//回転(8,3,6,7,5)
if ($degrees > 0) {
$image = imagerotate($image, $degrees, 0);
}
//保存
ImageJPEG($image, $filename);
//メモリ解放
imagedestroy($image);
}
もうね、番号順に書くとこんがらがるから、switch文ですら 1,8,3,6 と 2,7,4,5 の順に書いているくらいですよ。
やっていることはコメントに書いてあるとおりですが、一応解説するとこんな感じです。
1 | 何もしない |
---|---|
8 | imagerotate関数で90度回転 |
3 | imagerotate関数で180度回転 |
6 | imagerotate関数で270度回転 |
2 | imageflip関数で左右反転 |
---|---|
7 | imageflip関数で左右反転&imagerotate関数で90度回転 |
4 | imageflip関数で上下反転 |
5 | imageflip関数で左右反転&imagerotate関数で270度回転 |
4番は本来なら、左右反転してから時計方向に180度なんですけど、それって上下反転と同じ意味なのでそれで済ませています。
まとめ
- PHPでExif情報を読むにはexif_read_data関数を使う
- PHPで画像回転するにはimagerotate関数を使う
- PHPで画像反転するにはimageflip関数を使う
- Exif回転の番号は 1,8,3,6 と 2,7,4,5 のグループに分けて考えるとわかりやすい(かも)
まぁそもそも反転して回転するっていう 2,7,4,5 が設定されている写真データにはお目にかかったことがないので、ぶっちゃけ 1,8,3,6 に対応しているだけで十分かも知れません(;´∀`)