- この課題でつかうファイルはGitHubで管理されているので手元に持ってきてください。テキストや課題は全て含まれています。
- ZIPファイル をダウンロードしてください
- Gitがインストールできている人は
git clone https://github.com/beproud/pyconjp2017-tutorial.gitしてください。
スクリプトを作成する前に、必要なファイルを自分の手元にコピーしましょう。
第3部で必要なファイルは、 codes/3-1/ ディレクトリに収められています。
これを自分が実際に作業できるディレクトリへコピーしてください。
第3部での作業は、このコピー先のディレクトリ内で行います。
Pillowで画像を読み込んで、画像のサイズを表示するスクリプトを書いてみましょう。
Pillowで画像を読み込むには、
PIL.Image モジュールの open() 関数を用います。
また、 画像のサイズは、
open() 関数が返すImageオブジェクトの size 属性から、
(width, height) というint型のタプル形式で取得できます。
# read_image.py
from PIL import Image
with Image.open('./2017.png', 'r') as im:
print(im.size)配布した画像ファイル 2017.png と同じディレクトリに、
read_image.py という名前で上記のファイルを作成してください。
すると下記のようなディレクトリ構成になります。
./ +- 2017.png +- read_image.py
では、このスクリプトを実行してみましょう。
$ python read_image.py (800, 300)
スクリプトを実行すると上記のように画像ファイルのサイズが出力されます。
ここでの「オブジェクト」は、 コンピューター上に存在する「データ」と「データの使い方」をまとめた「モノ」と考えてください。 例えば、 ``Image`` オブジェクトは、 サイズ (``size``) や色などいった値(属性)から構成される「データ」と 「データ」である画像のサイズを変更する (``resize()``) といった「データの使い方」がまとまっています。
「タプル」とは、いくつかの「データ」を並べた「データ」です。 Pythonでは、 ``(123, 'abcdedfg', None)`` といったように ``,`` 区切りの並び(シーケンス)を ``()`` で囲うことで作り出すことができます。 また、リストとはことなり、既存のタプルに要素を追加することはできません。 (ただし、タプルとタプルをつなげて新しいタプルを作ることはできます。) タプルは、各要素が特定の役割を持っていることが多く、 たとえば、 ``Image`` オブジェクトの ``size`` 属性は、 1番目が幅で2番目が高さという役割を担っています。 「タプル」についてより詳しく知りたい方は、 Pythonの公式ドキュメント http://docs.python.jp/3.5/tutorial/datastructures.html#tuples-and-sequences や Miran Lipovaca (2012) 『すごいHaskellたのしく学ぼう!』オーム社を参照してください。
では開いたファイルを保存してみましょう。
画像を保存するには、 Image オブジェクトの save() メソッドを使います。
(メソッド == 関数です。オブジェクトにくっついた関数をメソッドと呼びます)。
save() メソッドの引数は次のようになっています。
| 第1引数: | 保存先のパス。 path/to/image.png など |
|---|---|
| 第2引数: | 画像フォーマット (省略すると第一引数の拡張子から推測したフォーマットが選ばれます) |
今回は開いた PNG ファイルを JPEG 形式で保存してみます。
write_image.py という名前で以下のファイルを作成してください。
# write_image.py
from PIL import Image
with Image.open('./2017.png', 'r') as im:
im.save('./2017.jpg', 'JPEG')$ python write_image.py $
これで画像が書き出されているはずです
Linux/OSXでは ls コマンドを、
Windowsでは dir コマンドを使って確認してみましょう。
Linux/OSX:
$ ls read_image.py write_image.py 2017.jpg 2017.png
Windows:
$ dir /B read_image.py write_image.py 2017.jpg 2017.png
出力が確認できたらPreview、Eye of GnomeやWindows フォトビューアを使って
2017.jpg を開いてみてください。
最後に画像を縮小してみましょう。 ここまでくると、徐々に実用的なプログラムになってきたのではないでしょうか。
resize_image.py という名前で以下のファイルを作成してください。
# resize_image.py
from PIL import Image
RATIO = 0.5
#: 縮小比率
with Image.open('./2017.png', 'r') as im:
# 画像サイズを取得
width, height = im.size
# ``RATIO`` で縮小する
resized = im.resize((
int(RATIO * width),
int(RATIO * height),
))
# 縮小された画像 (``resized``) を保存する
resized.save('./resized-2017.png')ここで width, height = im.size というコードが出てきました。
左辺に変数が2つありますが、ここでは何が起きるのでしょうか。
今、 im.size には、 (800, 300) というintのタプルが入っており、
このコードを実行すると、 width には 800 が height には 300 が代入されます。
Pythonではタプルやリスト(シーケンス)の中身を、 複数の変数へ同時に代入(アンパック)することができます。
つまり次のコードと等価です。
# 以下の2行は ``width, height = im.size`` と等価
width = im.size[0] # 800
height = im.size[1] # 300さて、画像を縮小するには、 Image オブジェクトの resize() メソッドを使います。
resize(size) メソッドは、
縮小後のサイズを (width, height) という int のタプル形式で渡すと、
画像が縮小された Image オブジェクト新しく作って返します。
Note
resize() メソッドを呼び出されたオブジェクトの画像サイズは変わりません
最後に、縮小後の Image オブジェクトの save() メソッドを呼び出して保存します。
コードが書けたらさっそく実行してみましょう。
$ python resize_image.py $
縮小に成功していると、 resized-2017.png という名前で縮小された画像が出力されているので、
無事縮小された画像が出力されていることを確認してみましょう。
これでPillowを使った画像処理の第一歩は終了です。 以下のような便利なプログラムを作ってみましょう。
- 画像の一括変換
- LGTM画像ジェネレーター
- コラージュ壁紙ジェネレーター
先ほどまでの画像の縮小、JPEG変換スクリプトではファイルは1つだけしか変換できませんでした。 ディレクトリー(フォルダー)を指定して、その中の画像ファイルすべてを変換するスクリプトを作りましょう。
codes/3-1/resize_image.py を手元に写経して、 codes/3-1/resize ディレクトリーもコピーしてください。
写経できたら codes/3-1/ ディレクトリーに移動して python resize_all_images.py と実行してください。
resize/resized_alps.jpg、 resize/resized_sky.jpg と2つの画像ができます。
基本的には画像の縮小スクリプトと同じです。 大きな違いは2点あります
os.listdirを使ってディレクトリー以下の画像を取得しているos.path.joinを使って保存先の画像のパスを作っている
os.listdir の使い方は1部で学んだことと同じです。
LGTMはLooks Good To Meの略で、コードレビューの際に「良いね!」「(リリースしても)大丈夫そう」という意味で使うスラングです。 単に文字で「LGTM」とコメントするのは少しツマラナイので、それっぽい(例えばサムズアップしてる)画像に「LGTM」と文字を挿入して画像で返信しましょう。
codes/3-1/lgtm.py を手元に写経してください。
写経できたら codes/3-1 ディレクトリーに移動して python lgtm.py と実行してください。
WindowsやLinuxの方は ImageFont.truetype('Helvetica', ... のフォント指定を好きなフォントに変えてください。
- Windows: Arial
Pillow で何か2次元上の絵を書く場合は ImageDraw を使います。
今回は文字を書きたかったので Draw.text を使っています。
公式ドキュメントの例 も参考になります。
ImageDraw を使えば他にも線を引いたり四角を書いたりできます。
詳しくは ImageDrawの公式ドキュメント を参考にしてください。
画像をいくつか合成してコラージュ画像を作ってみましょう。 例えばお気に入りの旅行の写真を4枚用意して、2 x 2 にマス目状に並べて壁紙にするようなスクリプトです。
codes/3-1/collage.py を手元に写経してください。
写経できたら codes/3-1 ディレクトリーに移動して python collage.py と実行してください。
Note
このスクリプトは数十個の素材画像やアイコン画像を1つの画像にまとめておくのにも使えます。 ゲーム、Web開発中にも使えます。
少しスクリプトは複雑なので、1つ1つ見ていきましょう。
target_image_pathsを作る
os.listdirを使って、コラージュ元画像のディレクトリーTARGET内の画像へのパスを作っています。["./collage/alps.jpg", "./collage/sky.jpg"...]のようなリストになります
- 空の画像
collage_imを作る
- 作りたいコラージュ画像のサイズで空の画像を作っています
- 第一引数
'RGB'はモードの指定で、3色8ビットで色を表す指定をしています。 詳しくは モードの説明 を参照してください。
- マスごとにループ
- 各マスの左上の位置を
grid_xとgrid_yで表しています(grid_x, grid_y)の値は順に (0, 0), (0, 450), (800, 0) (800, 450) になりますrange(0, 1600, 800)は0800を順に返します- 「0から始まって、1600まで、800ずつ増やして返す」という意味です
- 先ほど作った
target_image_pathsから順に画像を読み込み - コラージュの各マスのサイズに画像をリサイズ
collage_im.paste(resized, ...)で画像を埋め込み
.paste()は画像と左上の位置を受け取ります。resizedと(grid_x, grid_y)を渡しています
- 作ったコラージュ画像を保存
以上です。 処理としては複雑に見えますが、順に追って見ていけば理解できると思います。
ループの処理をより見やすくしたバージョンを collage_2.py として用意してみました。
内容はけっこう難しいですがトライしてみましょう。写経してみて、やっていることを理解してみましょう
- itertools.product は「組み合わせ」を作る関数です
- zip は複数のリストを1つにまとめるものです。
zip([1, 2, 3], ["one", "two", "three"])は[(1, "one"), (2, "two"), (3, "three")]になります。
今の collage.py では画像の数がコラージュ中のマスの数より多くある必要があります。
画像が少ない場合は始めの1から繰り返すようにスクリプトを改良してみましょう。
画像の順番はこんなかんじです。