EXIF Orientation問題
最近スマホをGoogle Pixel3aに変更したのですが、ブログ記事の写真をSafariで見ると一部の写真がひっくり返っているというコメントをいただきました。
冒頭の写真はPixel3aの上端(前面カメラのある方)を右側にして「右向き矢印」撮った写真を、はてなブログの「写真で投稿」でアップロードしたものです。Chrome(Windows/Android)でみると右向きに、Safari(iOS13)で見ると左向きに見えると思います。
JPEGファイルにはEXIFという画像のメタ情報を保存するテーブルがあり、その中にOrientationというカメラの向きを表すフィールドがあります。冒頭の写真はスマホ内部では左向き矢印として保存され、スマホの向きからEXIFにはOrientation 3が指定されているため180度回転して、正しい「右向き矢印」として表示されています。
実はHTMLの<img src="">で画像を表示する際にEXIFのOrientattionを解釈するか否かブラウザ毎に挙動が違っており、これが今回の問題の遠因になっています。具体的な対応状況は以下のリンクで調べることができます。
こちらによればChromeはEXIFに対応せずそのまま表示、SafariはEXIFの仕様通り方向を解釈して表示となっています。またCSSのプロパティで指定された場合は回転するものもあります。これではブラウザ毎に見える画像が異なってしまい不便なのでブログサービス側で画像変換をサポートすることが多いようです。
はてなブログでの対応
はてなブログでもブラウザを依らず表示できるようにしているようです。EXIFを解釈しないChromeでも冒頭の写真もきちんと右向き矢印で表示されています。
ところがなぜかJPEGをそのまま表示すれば大丈夫なはずのSafariでEXIFが解釈されずに画像がひっくり返って表示されてしまいます。またひっくり返るのはiOS13のSafariのみで、iOS12やmacOS CatalinaのSafariでは再現しません。前述の確認画像では全てのSafariでEXIF対応して表示されるため、どうもブラウザ側の問題ではなさそうです。
更に詳しく調べてみると、記事に埋め込んであるJPEGファイルが同じリンク先となっていてもブラウザ毎に表示が異なります(JPEGファイルを直接開いた場合はどのブラウザもEXIFを解釈するはずなのですが?)。どうもブラウザのバージョンを見てダウンロードするファイルを変える処理をしているようで、iOS13の時のみに発生しているようです。
対応策
結局こちらで対応出来る問題ではなさそうなので、手間は増えますがアップロード前に画像をEXIF情報を元に回転して元のEXIF情報は削除してしまうことにしました。
ということでEXIFの勉強も兼ねて、こちらの記事を参考にWindowsで以下のスクリプトを作ってみました。
使い方
python3をインストールした上で、rotate.py, rotate.batを同じフォルダに入れて下さい。rotate.batに変換したい画像をdrag & dropすると画像と同じフォルダにrotatedというフォルダを作り、EXIFに基づき回転した画像を保存します。
- rotate.py
import os import sys from PIL import Image convert_image = { 1: lambda img: img, 2: lambda img: img.transpose(Image.FLIP_LEFT_RIGHT), # 左右反転 3: lambda img: img.transpose(Image.ROTATE_180), # 180度回転 4: lambda img: img.transpose(Image.FLIP_TOP_BOTTOM), # 上下反転 5: lambda img: img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Pillow.ROTATE_90), # 左右反転&反時計回りに90度回転 6: lambda img: img.transpose(Image.ROTATE_270), # 反時計回りに270度回転 7: lambda img: img.transpose(Image.FLIP_LEFT_RIGHT).transpose(Pillow.ROTATE_270), # 左右反転&反時計回りに270度回転 8: lambda img: img.transpose(Image.ROTATE_90), } rotdir = os.path.sep + 'rotated' file_paths = sys.argv[1:] for file_path in file_paths: img = Image.open(file_path) exif = img._getexif() if exif: orientation = exif.get(0x112,1) print('Roate ' + str(orientation)+' : ' + file_path) rot_img = convert_image[orientation](img) dirname,fname = os.path.split(file_path) os.makedirs(dirname + rotdir, exist_ok = True) rot_img.save(dirname + rotdir + os.path.sep + fname, quality=95)
- rotate.bat
python %~dp0"rotate.py" %* pause
変換後の画像