ブログ投稿用に写真を準備

きっかけ

ブログに掲載している写真はツイッターに投稿した画像のURLからひっぱってきているのですが、手元にある他の写真を掲載したい場合もあります。

特にGitHubは事実上容量無制限らしいので、どんどん写真を掲載したい。 2011年の記事を下書きしてた際に追加の画像をiPhoneからインポートすることしました。

Exifに関係する問題

iPhoneからインポートした画像にはExif情報があります。 ここにOrientationという情報があり、写真をどういった向きで表示するのか書かれています。

Orientationの値には1〜8まであり、順に列挙すると下記のようになります。

  1. そのまま
  2. 上下反転
  3. 180°回転
  4. 左右反転
  5. 反時計回りに90°回転
  6. 時計回りに90°回転
  7. 時計回りに90°回転、上下反転
  8. 反時計回りに90°回転

ただ、Exif情報に従ってプレビューするアプリばかりではなく、特にiPhoneやカメラから直接インポートした画像を、ツイッターで写真添付したものと同じに考えていると意図しないかたちで回転したり反転した状態で表示されてしまいます。ツイッター等で写真をアップロードするとJPGで圧縮されExif情報も除去されるから?

とにかく、ExifのOrientationを参照して表示するアプリとそうでないアプリで写真の表示のされ方が違ってしまうのはマズいです。

iPhoneからインポートした画像をブログ投稿用に変換する

iPhoneからインポートした画像はIMG_dddd.JPGのようなファイル名になっています(例えば、IMG_0026.JPG)。

そこで、指定したディレクトリ内のJPG画像をブログ投稿用のjpg画像に変換するスクリプトを作成しました。

使い方は簡単です。例えば”./2011”ディレクトリ内の写真を変換するには、次のように指定します。

1
$ ./photo_util.py ./2011

スクリプトの挙動は以下の通りです。

  • 指定ディレクトリ内の*.JPG画像をあつめる
  • すでに変換されていれば当該ファイルをスキップする
  • Exif[‘Orientation’]情報を参考に写真を回転する
  • 写真を横幅600pxにリサイズする
  • 変換した写真を*_small.jpgの名で保存する

手元の写真十数枚でためしたところ、まずiPhoneで撮影した写真のExif[‘Orientation’]は1, 3, 6のいずれかだったのでスクリプトはこの3パターンのみとなっています(手抜き!……じゃなくて”怠惰”は三大美徳が一)。

また、ブログ掲載用に横幅が600pxになるようリサイズも同時に行います。この段階で写真のExif情報は除去されてしまいます。

スクリプトの実行はRAW画像が大きいためかちょっと時間がかかります(外部グラボ搭載時の旧MBAだと、1枚の写真につき1秒前後)。でも手作業と比較すれば断然ラク。

スクリプトはgistにもあります。gistの埋め込みが上手くいかないのでGitHubの方のスクリプトを貼り付けてあります。

(photo_util.py) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/env python

from PIL import Image
from PIL.ExifTags import TAGS
import os

def get_exif(im):
  ret  = {}
  exif = im._getexif()

  if not exif:
    print(os.path.basename(fn) + ' has no exif and skipped.')
    return ret

  for tag, value in exif.items():
    decoded = TAGS.get(tag, tag)
    ret[decoded] = value
  
  return ret


if __name__ == '__main__':
  import sys, glob

  if len(sys.argv) is not 2:
    print 'Usage: $ python %s dir_name' % sys.argv[0]
    quit()

  dir = sys.argv[1]

  # make a list of already processed photos
  skip = [f[:-10] for f in glob.glob(os.path.join(dir, '*.jpg'))]

  for fn in glob.glob(os.path.join(dir, '*.JPG')):
    name, ext = os.path.splitext(fn)

    if name in skip:
      print(name + ' is already processed and skipped.')
      continue

    print('processing ' + fn + '...'),
    im   = Image.open(fn)
    exif = get_exif(im)
    if len(exif) is not 0:
      if exif['Orientation'] is 1:
        im = im.resize((600, 450), Image.ANTIALIAS)
      elif exif['Orientation'] is 3:
        im = im.rotate(180).resize((600, 450), Image.ANTIALIAS)
      elif exif['Orientation'] is 6:
        im = im.rotate(-90).resize((600, 800), Image.ANTIALIAS)

      out = name + "_small" + ext.lower()
      im.save(out, quality=100)
    print('finished.')