Pillowを使ってアイコン画像・ヒストグラムグラフ画像を作る

2013/03/18 00:47

※ 商品のリンクをクリックして何かを購入すると私に少額の報酬が入ることがあります【広告表示】

Pillow がPython 3.3に対応したのが喜ばしいので、簡単に使い方を書いてみます。

Pillow は PIL を fork して、いくつかの問題を修正したものでかつ、Python 3に対応したものです。

ちなみに、 Pillow 2.0.0 以降は Python2.6, 2.7, 3.2, 3.3 に対応したもので、それより 2.0.0 より小さなバージョンは Python 2.4, 2.5, 2.6, 2.7 に対応したものらしいです。

世の中的にPython 3.0、3.1は無かった事になっているように感じられるので、3.2以降を使ったら良いです。 2013年3月6日にリリースされたBlender 2.66aも搭載されているPythonが3.3になっているようです。

さて、今回は、Pillowを使って(つまり、イメージ的にはPILを使って)、縦横比が1:1のアイコン画像を作るスクリプトと、画像のヒストグラムをグラフにするスクリプトを書いてみました。

縦横比が1:1のアイコン画像を作る。

まぁ、短辺に併せて画像の中央部分を切り取る機能と、リサイズする機能の作り方ですね。

現実的にはJSで任意の箇所をアイコンにさせてあげる事が多いでしょうから少し違う機能になるでしょうが、今回の部分を抑えておけば容易にプログラミングできる事でしょう。

  '''Copyright (c) 2013, www.tsuyukimakoto.com'''
  from math import ceil
  from io import BytesIO
  try:
      import Image
  except ImportError:
      from PIL import Image


  def __calc_rect_position(length, img_width, img_height):
      '''このロジックだと1ピクセルずれる可能性があるけど気にしない

      四角形から正方形を切り取るための座標を返します。
      正方形は四角形の中央部分です。
      '''
      if img_width > img_height :
          w = int((img_width - length) / 2)
          h = 0
      else :
          w = 0
          h = int((img_height - length) / 2)
      return (0 + w, 0 + h, length + w, length + h)


  def __fit_to_shorten(img, length):
      # img.size は、 幅と高さのピクセルがタプルで設定されています
      longer = ceil(max(img.size) * (float(length) / min(*img.size)))
      # img.thumbnailはもとの画像比率を保ってリサイズされるので、長辺の長さを指定しておけば良い
      img.thumbnail((longer, longer), resample=True)


  def make(length, img_filepath=None, img_data=None, outfile=None, format=None):
      '''
      crop_icon.pyというモジュールとして保存されている事を想定

      >>> import crop_icon
      >>> # from filepath
      >>> crop_icon.make(128, 'a.jpg', outfile='out_icon', format='png')
      >>> with open('a.jpg', 'rb') as f:
      ...     crop_icon.make(128, img_data=f.read())
      '''
      # ファイルパスかファイルのバイト列データのどちらかは必須
      assert(img_filepath or img_data)
      if img_data:
          #  readメソッドが無いといけないので、バイト列の場合には BytesIO でくるむ
          #  本当はfileのハンドラを受け取るようにした方が素敵かもしれないけど、バイト列
          #  を受け取った場合のやり方をサンプルとして示してる。
          img = Image.open(BytesIO(img_data))
      else:
          img = Image.open(img_filepath)
      __fit_to_shorten(img, length)
      # img.cropは指定の(x, y, width, height)で切り抜いて新しいImageオブジェクトを返す
      icon_img = img.crop(__calc_rect_position(length, *img.size))
      # img.formatには JPEG や PNG のような画像の種別が格納されています
      # jpegの場合、画像圧縮のクオリティをキーワード引数で指定できるのでしておく
      # qualityは0から100を指定するものだけれども、95より大きく指定しても無視されるとか。
      # いずれにしても、95より大きい指定とかしても結局劣化するので無駄なのです。
      # デフォルトは75です。
      # 某超巨大SNSの保存クオリティが87で、最近ちょっとアレな写真保存サービスが93という噂です。
      icon_img.save(outfile or 'out_icon', format=format or img.format, quality=87)

画像のヒストグラムグラフ画像を作る

画像編集アプリで、表示されるこんなヤツをPillowで作ってみます。

graph of histgram

Imageオブジェクトのhistgramメソッドが返す配列の意味さえ解れば簡単です。

今回は線を延々ひいて面グラフっぽくしています。

  '''Copyright (c) 2013, www.tsuyukimakoto.com'''
  try:
      import Image, ImageDraw
  except ImportError:
      from PIL import Image, ImageDraw


  def create(filepath, outpath):
      img = Image.open(filepath)
      # histogramは、R,G,B(,A)のそれぞれの明るさが何ピクセル画像に含まれているかを示す値の配列です。
      # 24bitのRGB画像の場合は各色8bitなので、
      #  0から255番目にはRed、256番目から511番目にはGreen、512番目から755番目にはBlueが格納されています。
      hist = img.histogram()
      max_height = max(hist)  # 高さを計算するために最大値を取り出します
      # max_height = img.size[0] * img.size[1]  # 総画素数をY軸の最大にする場合
      unit = max_height / 256  # 高さ256ピクセルに表現したいので、最大値を256とした場合の1ピクセルの数値を割り出します
      red, green, blue = (255, 100, 100), (100, 255, 100), (100, 100, 255)  # 線の色
      bg_color = (200, 200, 200)  # 背景
      canvas_size = (256, 256)  #
      out_img = Image.new('RGB', canvas_size, bg_color)  # 書き出し用のイメージオブジェクトを生成します
      canvas_img = ImageDraw.Draw(out_img)  # Draw用のオブジェクトを生成します
      for i, v in enumerate(hist):
          if i < 256 * 1:  # Redのヒストグラム
              color = red
          elif i < 256 * 2:  # Greenのヒストグラム
              color = green
          elif i < 256 * 3:  # Blueのヒストグラム
              color = blue
          else:  # Alphaは無視
              continue
          #canvas_img.point((i%256, (max_height-v)/unit), fill=color)  # 点だと他の色のグラフで隠されることはないけど解りにくい
          x = i % 256  #  各色毎に左端からグラフを書くので
          canvas_img.line((
              x,
              max_height/unit,  # 左上頂点なので、一番下は最大値。for文の外で計算しておくのが本来。
              x,
              (max_height-v)/unit),  # 下から何ピクセルまで線を引くか
              fill=color)
      out_img.save(outpath)


  if __name__ == '__main__':
      import sys
      create(sys.argv[1], sys.argv[2])

Pillow(PILL)は、他にもたくさんの機能が有り、それが故にPythonの標準的な画像ライブラリとして使われています。 スクリプト系言語でGDやImageMagickのラッパーでないライブラリが使われているのは珍しいのではないでしょうか?

Pythonには他にもゲーム用のライブラリや、科学技術計算のライブラリ、プロット用のライブラリと、非常に幅広いライブラリが揃っています。

Python3で動くものもどんどん増えてきています。是非Python3の世界をのぞいてみてください。

Prev Entry

Next Entry