paloma blog

NWエンジニアやってます。主に自宅環境のお遊びを書きます。Pythonもちょっと。タイトルは好きなカクテルから。

pythonのPillowライブラリで画像の切り貼り 切り抜き編

前回行ったスリーカードポーカーの実践結果ですが、
キャプチャファイルから画像を切り貼りしたのでその手順を残しておきます。

50枚の結果を、ある部分だけ1枚にまとめたかったのですが、
手動でコピペは面倒くさそうだったのでこれもpythonで解決することにしました。

今回使ったライブラリは画像処理ライブラリのPillowです。

準備

環境

ゲームのキャプチャファイルの関係でメインWindows機で行います。

> $psversiontable

Name                           Value
----                           -----
PSVersion                      5.1.18362.752
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.18362.752
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

ゲーミングPCなので余計なツールは入れないようにしていますが、今回Pillowのためにpythonを入れました。

> python --version
Python 3.8.3
Pillowインスト―ル
> python -m pip install Pillow

特に仮想環境作る必要もないので元のpythonに入れます。
バージョンは7.2.0です。

> python -m pip list
Package    Version
---------- -------
Pillow     7.2.0
pip        19.2.3
setuptools 41.2.0
WARNING: You are using pip version 19.2.3, however version 20.1.1 is available.
You should consider upgrading via the 'python -m pip install --upgrade pip' command.

また、50枚のキャプチャファイルは編集用フォルダにおいて置くものとします。

今回は単発作業のためターミナルで作業します。
最初に以下のモジュールを読み込んでおきます。

>>> import os
>>> from PIL import Image
>>> import glob # 後で使います

切り取り編

ファイルの切り取り

まずは試しに1つだけ切り取る練習です。

ファイル出力

出力する方法はいくつかありますが、osモジュールのlistdir関数にしました。
作業フォルダに対象のファイルしかないので。

>>> os.listdir()
['Grand Theft Auto V 2020-06-27 14-13-01_Moment(10).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(11).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(12).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(13).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(14).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(15).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(16).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(17).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(18).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(19).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(2).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(20).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(21).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(22).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(23).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(24).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(25).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(26).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(27).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(28).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(29).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(3).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(30).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(31).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(32).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(33).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(34).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(35).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(36).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(37).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(38).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(39).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(4).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(40).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(41).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(42).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(43).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(44).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(45).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(46).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(47).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(48).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(49).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(5).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(50).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(51).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(6).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(7).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(8).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(9).jpg']

HDサイズでちゃんと取れてますね。

>>> file = os.listdir()[0]
>>> im = Image.open(file)
>>> im
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1920x1080 at 0xB85B38>
>>> im.show()

f:id:paloma69:20200714223626p:plain

切り取り

特定の範囲だけ切り取りたいのでcrop関数を使います。

:param box: The crop rectangle, as a (left, upper, right, lower)-tuple.

ヘルプを見るとx座標始点、y座標始点、x座標終点、y座標終点の順で書けばいいようですね。

右上のチップ表記を切り取るのですが、200 * 120のサイズがあれば良さそうなので下記で切り取ります。

>>> x, y = imfile.size # yは使いません
>>> chip = im.crop((x-200, 0, x, 120))
>>> chip.show()

f:id:paloma69:20200714223650p:plain

いい感じに切り取れました。
あとは同じ手順を繰り返すだけです。

ファイルを日付順にソート

os.listdir()はどういう順に格納されるかわかりませんが、キャプチャした順では無い様です。
ファイル名前はキャプチャツールの仕様で昇順で採番されますが、sorted関数ではバージョニングソートはされないようです。(調べられてませんがオプションはあるかも)
ここは他のケースにも対応すべく作成日時でソートします。

osモジュールのstat関数でファイル作成日付のパラメータが整数で取れるようです。

>>> os.stat(file).st_mtime
1594538628.4300964

全ファイルのこのパラメータを取得して昇順で並び替えればいいですね。

というわけで以下を実行。
ループでどうにかしようとしましたが、lambdaを使ってすっきり書かれている方がいたのでサイトを参考にしました。

>>> filelist = sorted(os.listdir(), key=lambda f: os.stat(f).st_mtime)
>>> filelist
['Grand Theft Auto V 2020-06-27 14-13-01_Moment(2).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(3).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(4).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(5).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(6).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(7).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(8).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(9).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(10).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(11).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(12).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(13).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(14).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(15).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(16).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(17).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(18).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(19).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(20).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(21).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(22).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(23).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(24).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(25).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(26).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(27).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(28).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(29).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(30).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(31).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(32).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(33).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(34).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(35).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(36).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(37).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(38).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(39).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(40).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(41).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(42).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(43).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(44).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(45).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(46).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(47).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(48).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(49).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(50).jpg', 'Grand Theft Auto V 2020-06-27 14-13-01_Moment(51).jpg']

OKです。さっきと並び順変わってますね。

ループで全ファイルを切り取り

上のfilelistをループでまわして各ファイルを切り取って保存します。
新規イメージファイルを作成して直接貼り付けてもいいんですが、切り分けのため作業をばらします。

n = 1
for a in filelist:
    imfile = Image.open(a)
    x, y = imfile.size
    cropfile = imfile.crop((x - 200, 0, x, 120))
    cropfile.save(''.join(['result_', str(n), '.png']))
    n += 1

これをターミナルに流します。

>>> for a in filelist:
...     imfile = Image.open(a)
...     x, y = imfile.size
...     cropfile = imfile.crop((x - 200, 0, x, 120))
...     cropfile.save(''.join(['result_', str(n), '.png']))
...     n += 1
...
できました!

新しいファイルが出来てしまったので、検索しやすくするためにglobを使います。

>>> glob.glob('result*.png')
['result_1.png', 'result_10.png', 'result_11.png', 'result_12.png', 'result_13.png', 'result_14.png', 'result_15.png', 'result_16.png', 'result_17.png', 'result_18.png', 'result_19.png', 'result_2.png', 'result_20.png', 'result_21.png', 'result_22.png', 'result_23.png', 'result_24.png', 'result_25.png', 'result_26.png', 'result_27.png', 'result_28.png', 'result_29.png', 'result_3.png', 'result_30.png', 'result_31.png', 'result_32.png', 'result_33.png', 'result_34.png', 'result_35.png', 'result_36.png', 'result_37.png', 'result_38.png', 'result_39.png', 'result_4.png', 'result_40.png', 'result_41.png', 'result_42.png', 'result_43.png', 'result_44.png', 'result_45.png', 'result_46.png', 'result_47.png', 'result_48.png', 'result_49.png', 'result_5.png', 'result_50.png', 'result_6.png', 'result_7.png', 'result_8.png', 'result_9.png']

こんな感じでチップだけ切り抜いたファイルがたくさん作れました。

f:id:paloma69:20200714224448p:plain

あとはこれを張り付けていくだけです。

貼り付けまで書きたかったのですが、長くなりそうなので分割します。
次回全ファイルを一枚に貼り付け編です。

使った関数のメモ

  • ライブラリインポート
from PIL import Image
  • 画像ファイル読み込み
imfile = Image.open('filename')
  • 読み込みサイズの取得
imfile.size
  • 画像の範囲を指定して切り取り
cropfile = imfile.crop((x - 200, 0, x, 120)) # x座標始点、y座標始点、x座標終点、y座標終点
  • 画像の保存
cropfile.save('filename')

参考サイト

ディレクトリ内のファイルリストを取得する – Pythonプログラミング物語