webエンジニアの日常

RubyやPython, JSなど、IT関連の記事を書いています

スマホで撮影した写真が横向きになってしまうときの対処法

こんにちは、エンジニアのさもです

画像処理をwebサービスにしてみたのですが、なぜかスマホで撮影した画像が横向きになって出力されてしまいました。

いろいろ調べたので、対処方法をまとめておきます。

スポンサーリンク

まず、なぜ横向き担ってしまうかというと、そもそも画像が横向きだったから。

スマホでは、「横向き」という情報を画像が持っているので、向きを整えて表示しています。

その情報は、Exifという規格で保存された画像データに含まれていので、画像処理を行う場合は、まずこの情報を見て、画像を回転させておく必要があります。

www.weblio.jp

また、Exifにどんな情報があるのかというと、以下のサイトで一覧を見ることができます。

Exif TAG

とりあえず画像のExif情報を見てみます

from PIL import Image

Image.open("./images/test.jpg")._getexif()
#=> {34665: 38, 274: 1, 40962: 750, 40963: 1334}

こんな感じの辞書型データが出力されます。

キーが上記サイトのTAGIDで、valueがその値になります。

全ての画像で取れるわけではなくて、何も返ってこない、空の辞書が返ってくる、エラーになるなどバリエーションがあります。

_getexif()の出力を順番に見ていくと、

34665 -> Exif タグ

274 -> 画像方向

40962 -> 実効画像幅

40963 -> 実効画像高さ

ということらしいです。欲しい情報は274っぽいですね。orientationという名前の属性らしいです。値は1です。

orientationの意味はこちらのサイトでみれました。

Exif Orientation Page

Value0th Row0th Column
1topleft side
2topright side
3bottomright side
4bottomleft side
5left sidetop
6right sidetop
7right sidebottom
8left sidebottom

上記の表で1をみてみると、0行目がトップで、0列目が左側です。普通の向きの画像ですね。

5のところをみると、0行目が左側、0列目がトップということなので、左右反転して、右に90度向きが変わった画像ということですね。頭の体操になりそうです。

画像の変換は以下のサイトを参考にしました

qiita.com

画像の向きを考慮した実装例は以下のようになります

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),                                    # 反時計回りに90度回転
}

img =Image.open("./images/test.jpg")

try:
  exif = img._getexif()
  if exif:
    orientation = exif.get(0x112, 1)
    img = convert_image[orientation](img)
except AttributeError:
  print(img)

new_img = filter1(img)
new_img.save("./images/new_test.jpg")

変換用の辞書を定義しておいて、画像を読み込み、exif情報があればそれにしたがって向きを変えます。

tryで例外処理を行っているのは、上でも書きましたが、_getexif()でエラーが出る場合があるからです。

png画像や、img.convert(“RGB”)した画像へ._getexif()をするとエラーになりました。

png画像はしょうがないですが、convert(“RGB”)は向きを整えてからにしましょう。

最後に

ずっと画像が横になる問題は気になっていたのですが、やっと解決できました。

railsで画像アップロードしても、特に横向きになったという経験はないのですが、ちゃんとライブラリが内部で変換してくれているのですかね。 ありがたや~

PCで画像を探してきて、それを加工して試してみるだけではこの処理は不要だと思うのですが、webサービスとして公開するときには少し注意が必要ですね。勉強になりました。

読者登録をしていただけると、ブログを続ける励みになりますので、よろしくお願いします。