ポケモンのツールを作るうえで、大量の画像をトリミングしないといけないのですが、
流石に全部を手動トリミングしていると1日あっても終わらないので、そういうスクリプトをPythonで組んでみました。
せっかくなので公開します。
目的
画像ファイルを読み込んで、余白部分を切り取りたい。
・・・たぶん何を言っているのか分からないと思うので例をあげると、

こういう画像があるとして、

この緑枠線内にトリミングしたいって感じです。
スクリプト作成
必要モジュールのインストール
まずは画像をいじくるためのモジュールであるPILをインストールします。
# pip3 install pillow
python3で使用したかったので、pip3コマンドでインストール。
アルゴリズム検討
簡単に書くと、
上下左右、一番端っこにある色付きピクセルの座標を記録しトリミングする
感じです。

単純ですが、一番わかりやすい方法だと思います。
というか、頭悪いのでこれ以上の方法が思い浮かばんかったです。
スクリプト内容
実際に作成したスクリプトはこちら。
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 55 56 57 58 | # coding:utf-8 from PIL import Image import sys import os args = sys.argv outputDir = './trim' #outputディレクトリ # トリミングする関数 def trimImage(_image, _filename): img = _image rgbImage = img.convert( 'RGB' ) size = rgbImage.size tPix = 100000000 bPix = - 1 lPix = - 1 rPix = - 1 # ピクセル操作 for x in range (size[ 0 ]): for y in range (size[ 1 ]): r,g,b = rgbImage.getpixel((x, y)) rr,rg,rb = rgbImage.getpixel((size[ 0 ] - x - 1 , size[ 1 ] - y - 1 )) # 色付きのピクセルかどうか(白もしくは白に近しい色を切り抜くため) if (r + g + b) < 600 : if lPix = = - 1 : lPix = x if y < tPix: tPix = y if (rr + rg + rb) < 600 : if rPix = = - 1 : rPix = size[ 0 ] - x if size[ 1 ] - y > bPix: bPix = size[ 1 ] - y print (tPix, lPix, bPix, rPix) try : trimImageFile = img.crop((lPix, tPix, rPix, bPix)) #トリミング trimImageFile.save(outputDir + '/' + _filename, quality = 100 ) #保存 except : print ( 'Error' ) def main(): # 出力先ディレクトリがなければ作成する if not os.path.exists(outputDir): os.mkdir(outputDir) trimImage(Image. open (args[ 1 ]), 'trim_' + args[ 1 ]) if __name__ = = "__main__" : main() |
まずはmain関数から
1 2 3 4 5 6 7 8 9 | def main(): # 出力先ディレクトリがなければ作成する if not os.path.exists(outputDir): os.mkdir(outputDir) trimImage(Image. open (args[ 1 ]), 'trim_' + args[ 1 ]) if __name__ = = "__main__" : main() |
出力先のディレクトリがなければ作成します。
そのあと、後述するトリミングを実行する関数にImageファイルを食わせます。
1 2 3 4 | ef trimImage(_image, _filename): img = _image rgbImage = img.convert( 'RGB' ) size = rgbImage.size |
imageオブジェクトと出力する際のファイル名を引数にして実行
ごちゃごちゃ書いているのは画像をRGB変換したり、画像のサイズを取得したりです。
1 2 3 4 | tPix = 100000000 bPix = - 1 lPix = - 1 rPix = - 1 |
上下左右の端の座標を記録する用の変数
tPixは大きい数字であれば何でもいいです。
9999999999999999999999999
とかでもよいです。
1 2 3 4 | for x in range (size[ 0 ]): for y in range (size[ 1 ]): r,g,b = rgbImage.getpixel((x, y)) rr,rg,rb = rgbImage.getpixel((size[ 0 ] - x - 1 , size[ 1 ] - y - 1 )) |
for文を使用してピクセルを1つずつずらして参照していきます。
r,g,bという変数にその座標のRGB値を代入。
逆側からも同時に参照するためにrr,rg,rbという変数も用意
1 2 3 4 5 6 7 8 9 10 11 12 | # 色付きのピクセルかどうか(白もしくは白に近しい色を切り抜くため) if (r + g + b) < 600 : if lPix = = - 1 : lPix = x if y < tPix: tPix = y if (rr + rg + rb) < 600 : if rPix = = - 1 : rPix = size[ 0 ] - x if size[ 1 ] - y > bPix: bPix = size[ 1 ] - y |
ここで色付きのピクセルかどうかを判定しています。
完全な白であればr+g+bが765になるのですが、今回は幅を持たせて600以上であれば白と判断しました。
色付きのピクセルであると判断された場合はその座標を記録していきます。
1 2 3 4 5 | try : trimImageFile = img.crop((lPix, tPix, rPix, bPix)) #トリミング trimImageFile.save(outputDir + '/' + _filename, quality = 100 ) #保存 except : print ( 'Error' ) |
気持ち程度の例外処理をして、画像をトリミング&保存。
実際に動かしてみる
拾い物のピカチュウの画像を用意しました。

こいつをトリミングしてみます。
# python3 trimming.py pika.jpg
んで出力された画像がこちら

なんかうまくいってそうですね。
まとめ
- 余白を勝手に切り取ってくれるスクリプトを作成しました
あとはfor文なりなんなりで複数の画像を引数にしてあげれば一気にトリミングできますね。
コメント