Pillow(PIL)
前回、Pythonの画像処理ライブラリPillow(PIL)を使って、縦横比を変えずにサイズ変更する方法を解説しました。
今回はこのPillow(PIL)を使って、まず一番やりたいとであるiPhonenのスクリーンショットをトリミングするプログラムを作成していきます。
どこをトリミングしたいかというと、画面上部の時間やらWifiやらが表示されている部分です。
今回はプログラムを組みながらの解説ではなく、最初に全部お見せして、それぞれを解説していきたいと思います。
プログラム全体
<セル1>
import os
import os
from PIL import Image
newname = "test_"
mobile_fix_size = [0, 100, 0, 0, 1000, 0] #[left, upper, right, lower, width, height] width&height 0 = keep ratio
default_path =""
mobile_ss_size = [1125, 2436]
ext_list = [".png", ".PNG"]
<セル2>
def path_set(default_path):
if default_path == "":
path_temp = os.getcwd()
else:
path_temp = default_path
return path_temp
def file_select(default_path, newname, ext_list):
list_temp = []
for file in os.listdir(default_path):
if file[-4:] in ext_list:
if not newname in file:
list_temp.append(file)
return list_temp
def trimming(im, fix_size):
width = im.size[0]
height = im.size[1]
im_trim = im.crop((fix_size[0], fix_size[1], width - fix_size[2], height - fix_size[3]))
return im_trim
<セル3>
if __name__ == '__main__':
default_path = path_set(default_path)
file_list = sorted(file_select(default_path, newname, ext_list))
i = 1
filling = len(str(len(file_list))) + 1
for file in file_list:
im = Image.open(file)
if im.size[0] == mobile_ss_size[0] and im.size[1] == mobile_ss_size[1]:
im_trim = trimming(im, mobile_fix_size)
im_trim.save(newname + str(i).zfill(filling) + ".png")
i = i + 1
このプログラムをプログラムの流れに沿って解説していきます。
フォルダの設定
メインのプログラムは「if __name__ == ‘__main__’:」がある<セル3>ですので、ここから解説をスタートします。
「default_path = path_set(default_path)」で最初にどこのフォルダに画像があるのか、フォルダの設定を行います。
ここには独自の関数「path_set」があり、「default_path」が代入されています。
default_pathは変数として、<セル1>で定義されています。
また関数path_setの中身は<セル2>にあり、こんな感じです。
def path_set(default_path):
if default_path == "":
path_temp = os.getcwd()
else:
path_temp = default_path
return path_temp
なぜ<セル1>で変数default_pathを設定しているのに、再度関数path_setで定義し直しているのか。
これはこのプログラムと同じフォルダに画像がある場合と、他のフォルダに画像がある場合の2パターンを想定しているからです。
関数path_setでは最初に
if default_path == "":
path_temp = os.getcwd()
となっており、<セル1>でdefault_pathを””(空欄)としてセットした場合は、「os.getcwd()」でこのプログラムが起動しているフォルダを取得するようになっています。
またそれ以外の場合、すなわちdefault_pathにフォルダパスが設定されている場合は
else:
path_temp = default_path
となっており、そのパスをそのまま使うようになっています。
こうすることで将来、ダブルクリックで実行する「.py」形式としても使えますし、このプログラムと離れた場所からでも画像の処理をすることができるようになります。
ファイル名リストの取得
次は画像ファイル名をリストとして取得していきます。
「file_list = sorted(file_select(default_path, newname, ext_list))」とあり、ここでも独自の関数「file_select」を使っています。
ここで代入しているdefauto_pathは先ほど定義したdefault_pathです。
newnameとext_listは<セル1>で定義していて、newnameは新しいファイル名の一部、ext_listは画像として認識する拡張子のリストです。
今回はとりあえず.png(大文字の場合もあるため.PNG)に当てはまるもののみ画像として認識させます。
関数file_selectの中身はこんな感じ。
def file_select(default_path, newname, ext_list):
list_temp = []
for file in os.listdir(default_path):
if file[-4:] in ext_list:
if not newname in file:
list_temp.append(file)
return list_temp
最初により分けた画像ファイルの名前を格納するための空リストlist_tempを作成しています。
その後、「for file in os.listdir(default_path):」でdefault_pathのファイル名を一つずつ取得します。
ファイル名の最後4文字が拡張子をまとめたリストext_listにあった場合で、ファイル名に変数newnameが含まれていない場合のみ、リストlist_tempにファイル名を格納します。
何故、「if not newname in file:」でファイル名に変数newnameが含まれていないものをより分けているかというと、より分けていない場合、2回3回連続してこのプログラムを実行すると、トリミングされたファイルをさらにトリミングして新しいファイルを作成してしまうからです。
つまり一度処理したファイルを再度処理しないようにここでストップさせているわけです。
そして「return list_temp」でlist_tempを返し、この関数は終了です。
そして戻ってきた際、「sorted()」を使い、ファイ名順にソートして、リストfile_listに格納しています。
画像をトリミングして保存
次にこのプログラムのメインの部分、画像をトリミングして保存するという処理のプログラムです。
i = 1
filling = len(str(len(file_list))) + 1
for file in file_list:
im = Image.open(file)
if im.size[0] == mobile_ss_size[0] and im.size[1] == mobile_ss_size[1]:
im_trim = trimming(im, mobile_fix_size)
im_trim.save(newname + str(i).zfill(filling) + ".png")
i = i + 1
最初の2行は後で解説します。
「for file in file_list:」で先ほど取得したファイル名を一つずつ取得し、次の「im = Image.open(file)」でpillowで読み込んでいます。
次の「if im.size[0] == mobile_ss_size[0] and im.size[1] == mobile_ss_size[1]:」は、iPhoneの縦のスクリーンショットであるかどうかを判定しています。
私のiPhone Xsの場合、縦のスクリーンショットを撮ると横1125ピクセル、縦2436ピクセルです。
その値を<セル1>の「mobile_ss_size = [1125, 2436]」として定義してあります。
そして読み込んだ画像の縦横のサイズとmobile_ss_sizeで定義した値と比較して、縦のスクリーン書とである場合のみ、次の処理に進めるようにしています。
次の行では「im_trim = trim_mobile(im, mobile_fix_size)」として、独自の関数「trimming」を使っています。
trim_mobileの全体はこんな感じです。
def trimming(im, fix_size):
width = im.size[0]
height = im.size[1]
im_trim = im.crop((fix_size[0], fix_size[1], width - fix_size[2], height - fix_size[3]))
return im_trim
ここではimとしてpillowで読み込んだ画像ファイル、mobile_fix_sizeを読み込んでいます。
mobile_fix_sizeは<セル1>で定義してあり、「mobile_fix_size = [0, 100, 0, 0, 1000, 0]」となっています。
この関数の中では一般化するため、mobile_fix_sizeの部分はfix_sizeになっていることに注意してください。
これは前から、左、上、右、下それぞれからのトリミングするピクセル数、サイズ変更後の横、縦としました。
ちなみにサイズ変更に関しては今回は使いませんが、今度使うので先に定義しておきました。
前にpillowのトリミングを解説した際、左と上は削除するピクセル数であり、右と下はここまでトリミングするというピクセル数となっていました。
これが個人的に分かりにくかったので、今回はそれぞれの端からトリミングするピクセル数に変えてみました。
それがこちらの部分になります。
im_trim = im.crop((fix_size[0], fix_size[1], width - fix_size[2], height - fix_size[3]))
そしてトリミングした画像データを「return im_trim」で返して、この関数は終わります。
メインのプログラムでは「im_trim = trim_mobile(im, mobile_fix_size)」として、トリミングした画像データを変数im_trimに格納し、「im_trim.save(newname + str(i).zfill(filling) + “.png”)」で保存します。
ここで重要なのが、「str(i).zfill(filling)」という部分です。
画像を複数一度にトリミングし、新しい名前で保存したいため、数字を付加するようにしています。
ここで「zfill(数字)」を桁数を揃えるための関数で、足りない桁を0で埋めてくれます。
つまり画像の枚数が10枚だった時、「file_1」、「file_2」、「file_3」…「file_10」とするのではなく、「file_01」、「file_02」、「file_03」…「file_10」というようにできるわけです。
そしてstr(i)の数字はトリミング部分のfor文が始まる前に「i = 1」と定義し、1枚の画像処理が終わるごとに「i = i + 1」で数字を一つずつ上げていっています。
また桁数を決める変数fillingですが、「filling = len(str(len(file_list))) + 1」と定義してあります。
「len(file_list)」で何ファイルあるか数字を取得します。
それに対してstr()とすることで、数字を文字に変えます。
文字に対してlen()とすると文字数を取得でき、言い換えるとファイル数の桁数を取得することになります。
今回の場合はさらに1を足しており、実際のファイル数の桁数よりも一つ大きい桁まで0で埋めています。
プログラムの実行結果
それではこちら三つの画像(iPhone_ex1.png、iPhone_ex2.png、iPhone_ex3.png)をこのプログラムで処理してみましょう。
処理後にはtest_01.png、test_02.png、test_03.pngというファイルが作成されました。
それぞれのファイルを見てみましょう。
確かに上部の時計とかWifiの部分が消えています。
これでトリミングに関してはできました。
次回はさらにサイズ変更をする部分を作成していきましょう。
ということで今回はこんな感じで。
コメント