📱

各種スマートフォンからブラウザ経由で画像をuploadするときのExif.Orientationでハマった

2021/12/05に公開

エアークローゼット21新卒エンジニアの小林(@helicoir)です。
airClosetアドベントカレンダー4日目の記事となる本記事では、先日公開された「ファッションモデルになろう」のコンテンツページを制作した際に、Android, iOS端末にてアップロードする画像のExif.Orientationの対処でハマったときのことについてお話します!

「ファッションモデルになろう」とは

「ファッションモデルになろう」とは、自分の写真を投稿すると、AIが服の色味などからどのようなコーディネートなのかを判別し、あなたのお洋服にピッタリの見出しがついたファッション誌の表紙風画像をつくってくれるサービスです。

https://corp.air-closet.com/fashion-model/index.html#/
ぜひ遊んでみてください!

事の発端

画像のアップロード機能を作成中、iPhoneからのアップロードでは下のような画面が確定前に表示され、画像サイズを軽くできるのですが、fileReader()を用いて実際に反映された画像を確認すると、縦向きでとった写真が必ず横向きで表示される、という現象に遭遇。
それ以外の向きで撮影しても、画像サイズを変更すると必ずこの向きで表示されてしまいます。

調べてみると、画像のメタデータ(Exif)に「写真の向き(Orientation)」を示すデータが存在する場合があり、iOS端末におけるこのような画像圧縮では、この情報をもとに向き修正を行ったあとExifの大部分を削除しているようでした。また、Androidの一部端末においては、ExifのOrientation補正値に不正な値が入っていることも確認できました。
今回の記事ではその詳細と、実際に「ファッションモデルになろう」ではどのように対処したかを簡単に書きます。

Exifについて

この規格は、デジタルスチルカメラ、及びデジタルカメラで撮影される画像ファイル又は音声ファイルを取り扱うシステムにおいて、画像、音声及びタグのフォーマットを規定する。

http://www.cipa.jp/std/documents/j/DC-008-2012_J.pdf

こちらの仕様からも読み取れるように、Exifは映像・画像・音声を扱うファイルにおけるメタデータであり、この中に撮影機材・場所の情報から撮影設定(静止画で言えばISO感度やF値、画像の向きなど)など、様々な情報が格納されています。
特に画像の向き(Orientation)に関しては、以下のような数値で画像の向きが指定されます(こちらの記事からお借りしました)。

%orientation = (
    1 => 'Horizontal (normal)',
    2 => 'Mirror horizontal',
    3 => 'Rotate 180',
    4 => 'Mirror vertical',
    5 => 'Mirror horizontal and rotate 270 CW',
    6 => 'Rotate 90 CW',
    7 => 'Mirror horizontal and rotate 90 CW',
    8 => 'Rotate 270 CW',
);

Exif.Orientationをバイナリデータから確認するには、Hex Fiendなどのバイナリエディタを用いると便利です。
Exifの存在する画像かどうかは、最初の5-8文字目のコードがFFE1FFE0なのかで判別できます。※こちらの記事を参考にさせていただきました。

何が起こったか & その対処

①iOSの画像圧縮ではExifが削除される

原因

iOS端末のカメラには、デフォルトの向きが存在します。
縦持ち状態から左に90°傾ける、この状態です↓

この状態からどのくらい回転させるかをExifで保存すると、写真アプリやその他様々なところでこれを読みとってくれて、最終的に「撮影したときの見え方」で表示される、という段取りです。

ここで本記事冒頭の例が登場するのですが、iOS端末の主要ブラウザ上で画像をアップロードするとき、画像を圧縮してしまうことで、このExifが削除されてしまいます。
実際に先ほどの例にて横向きになってしまっているプレビュー画像を保存してHex Fiendで見てみると、5-8文字目がFFE0、つまりExifが存在していないことがわかります。

対処

Exifが消えてしまうと、撮影者が意図している角度を読み取ることは不可能です。そのため、アップロード時のプレビュー画面に画像を回転させることができる機能を用意し、正しい角度までユーザーに戻してもらい、回転数と画像データをリクエストに含めることで、api上で本来の画像の向きに戻せるようになりました。

②Androidの一部端末で撮影した画像は、Exif.Orientationが0

原因

Androidの一部端末ではどのような角度で撮影してもExif.Orientationが0になってしまいます。
Samsung Galaxy Note 4の報告がweb上には多く、今回の例でもGalaxy端末でこの現象が発生していました。

対処

Exif.Orientation=0は最初のほうで触れたExifの仕様にない値のため、仕様に沿った処理の書き方だとやり方によってはハマります。
ただし、正しい向きがそのまま保存されるため、画像の向きを変える必要はありません。仕様上「デフォルトの向き」とされるOrientation=1と同様に扱えるため、アップロードされる画像のExif.Orientationを1に変更すれば問題ありませんでした。

おわりに

実機検証で考慮事項がめちゃくちゃ増えて一時はどうなることかと思いましたが、無事無理のない形に落ち着けることができて安心しました。特にiOSで画像圧縮をかけた画像についての技術記事がほぼ存在せず、調査にかなりの時間を費やしました、、、
同じ事象で悩んでいる方の助けになれば幸いです。ありがとうございました!

参考

http://dqn.sakusakutto.jp/2009/02/jpegexiforientaion.html
https://qiita.com/zaru/items/0ce7757c721ebd170683#exif-をパースする
https://gist.github.com/xl1/55aa630747d7eb4fa97361baa80bab63
https://www.digitalboo.net/post/7518/a-note-about-exiftool

Discussion