幕の内はフレームワークにZope3を使っていますが、Zope3はPythonで作られています。そのため、Pythonの豊富なライブラリを利用することで、Webアプリケーションの機能を豊富に提供することができるようになります。最近はeasy-installを使って色々なライブラリを簡単に利用できるようになりました。
最近手がけた複数の案件で画像変換が必要になり、Pythonのライブラリを使って対応しました。Pythonで画像を扱う一般的な方法として PIL(Python Imaging Library) を使う方法があります。このライブラリは機能が豊富で大抵のことは出来てしまいますが、抽象度があまり高くないのでそれなりに画像のことを知らないと扱いづらい側面もあります。
それでは早速やってみましょう。
インストール
PIL(Python Image Library)の導入 ― JZUG を参照してください。以上っ。
画像を縮小する
とりあえず200x300の大きさのGIF画像を用意してみました。これを100x100サイズに縮小します。縮小には Image クラスの thumbnail メソッドを使用します。
>>> img = PIL.Image.open('ball.gif')
>>> img.thumbnail((100,100), PIL.Image.ANTIALIAS)
>>> img.show()
GUI環境なら縮小された画像が表示されると思います。 名前の通りサムネイルを作成するメソッドなので、200x300という非正方形を100x100指定で縮小した場合、縦横比を保ったまま100x100に収まるサイズ 66x100 に縮小してくれました。
(66, 100)
ついでに拡大してみます。
>>> img.size
(66, 100)
だめでした。残念。thumbnailはあくまで縮小画像作成用ということなんでしょう。 ところで、 PILのヘルプ を眺めると似たような処理をするメソッドに resize というものがあります。これなら拡大も出来るようですが、一つ問題が。まずは先ほど失敗した66x100 を 200x200 に引き延ばしてみます。
>>> img2.size
(200, 200)
縦横比を無視して引き延ばされてしまいました。thumbnailとresizeの2つを使った時点で扱いづらい雰囲気があります。自己破壊型だったり返値を返さなかったり縦横比維持しなかったり‥‥。
| method名 | 拡大縮小 | 縦横比維持 | 対象 | 返値 |
|---|---|---|---|---|
| thumbnail | 縮小のみ | 維持する | 自己破壊型 | None |
| resize | 両方可能 | 維持しない | 変換画像を返す | 変換画像 |
とはいえ、縦横比維持する値を自分で計算すればthumbnailと同じ処理は自分で書けます。thumbnail関数はめんどくさい比率計算をしてくれるサポート関数なんだと思うことにします。なんだかんだ言ってPILは便利です。
画像のフォーマットを変換する
フォーマットの変換はかなり手軽に行えます。読み込んだgifファイルをjpegフォーマットに変換してみます。
>>> img.mode
'P'
>>> img2 = img.convert('RGBA')
>>> img2.mode
'RGBA'
>>> img2.save('ball2.jpg', 'jpeg')
これで変換できました。2行目が無ければもっと簡単なのですが‥‥。PILは画像のデータの持ち方を保持していて、勝手に変換はしてくれません。ここではモード P を変換して RGBA に変換してから'jpeg'形式で保存しました。
ファイルフォーマット毎のmode対応については Test Page. Yoshifumi Tanaka を参考にしました。'P'(8bitカラー)ではjpeg保存出来ないから'RGB',あるいは'RGBA'(24bitカラーalpha付き)に変換しましょうという事のようです。
このへんは、画像をきめ細かく操作できると見るべきか、面倒ととらえるべきか。ちょっとした用途ならPILのマニュアルよりも PIL Cook Book が欲しくなります。
JPEG画像へのコメント埋め込み
最後に、jpeg画像ファイルにコメント文字列を埋め込んでみます。DoCoMoとauの仕様によると、画像のコピー不可設定をするにはjpeg画像フォーマットでサポートしているコメント文字列埋め込みでコピーコントロールに対応するようです。
PILを使ってjpegファイルにコメントが埋め込まれている画像を読み込むと、以下のようにしてその内容を確認することが出来ます。
>>> img.applist
[('APP0', 'JFIF..........'), ('COM', 'Dive into Python'), ]
では、jpeg画像を作って上記のような値をapplistに設定すればいいかというと、残念ながらうまくいきませんでした。色々と調べてみたのですが、PIL経由でのコメント保存は出来ないのではないかと思います。
そこで、データを直接操作してみます。
data = img.tostring()
index = data.find('\xff\xdb')
if index > 2:
l = len(comment) + 2
length = chr(l/256) + chr(l%256)
data = data[:index] + '\xff\xfe' + length + comment + data[index:]
これでdata変数に望みのデータ列を得ることが出来ました。バイナリ操作なのでPythonの struct モジュールを使う方が良かったかもしれません。ためしにstructを使って最後の行を書き換えてみます。
今回の例では、structの恩恵が少ないせいか、あまり便利になったようには見えませんが、 chr(l/256) + chr(l%256) という計算が不要でフォーマットが明示化されるのは良いところです。
なにはともあれ、あとはコメント文字列として kddi_copyright=on,copy="NO" を設定すれば完成です。このコメント文字列の意味についてはググれば詳しい解説がいくつも見つかるので割愛します。
まとめ
Pythonで動的に生成する画像にコピー制限を設定する方法はなかなか見つけられなかったので自分で作ってみましたが、(PILを諦めてからは)それほど時間をかけずに作ることが出来ました。Pythonでバイナリ操作する事は利用者全体からみれば少ないかもしれませんが、こういったことも知っておいて損は無いと思います。
今回はPILの良い面があまり出てきませんでしたが、本当はイイヤツなんです。合成したり変換したり色々出来ます。だれかCookBook作ってくれないかなあ。