opencv+python フォルダ内の全てのjpg画像をforループで処理する
opencvをpythonで触っていたのでその時に作ったもろもろをメモしていく。
今回は特定のフォルダに入っている複数の画像をforループで開いて、何がしかの処理をするもの。
やることは以下。
- globでフォルダ内のjpgファイル名を取得しリスト化
- リストの配列数分for inでループ
- ファイル名を取得
- 画像をnumpyでバイナリで開く
- 取り込んだバイナリをopencv形式に変更
- opencv形式の画像をグレースケール化
- 実際は最後に何がしかの処理をする(トリミングとか)
ソースコード
import pathlib
import cv2
import numpy as np
input_dir = "images/input"
input_list = list(pathlib.Path(input_dir).glob('**/*.jpg'))
for i in range(len(input_list)):
img_file_name = str(input_list[i])
img_np = np.fromfile(img_file_name, dtype=np.uint8)
img = cv2.imdecode(img_np, cv2.IMREAD_COLOR)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
print("-------------------------------------------")
print(img_file_name)
概要
取得した画像名をターミナルに出力しているが特に意味はない。
opencvではグレースケール化して扱うことが多いので、そこまでの処理を入れている。
numpyのバイナリで読み込んでいるのは、ファイル名が日本語の時にopencvのimread
ではうまく動かないため。それを回避するためにnumpyのバイナリ⇒opencv形式に変換という処理をしている。日本語ファイル名を使わないならopencvのimread
でよい。
カラー画像がいらない場合は、imdecode
でcv2.IMREAD_GRAYSCALE
を設定すれば最初からグレースケール化できる。カラー画像を使いたい場合を考えて例のような形にしている。
pathlibとglobでjpgファイル名をリスト化
os.pathで可能なことはだいたいpathlibでも可能。
今回はglobでワイルドカード検索をするため、globをimportしなくてよいpathlibを使っている。
input_dir = "images/input"
input_list = list(pathlib.Path(input_dir).glob('**/*.jpg'))
以上の記述で、特定のディレクトリ配下にあるjpgファイル名を全てリスト化している。**/*.jpg
の部分をimages/*.jpg
などにすれば、input_dir配下の特定のフォルダを指定できる。そのままならば、配下のフォルダ全てのjpgファイル名を取得する。
pathlibについては以下で。
for i in range(len(input_list)):でループ
for i in range(len(input_list)):
...
input_list
には前述のpathlib
でファイル名の一覧リストが作られている。
len
では配列の数を取得して、for i in range()
でその回数分だけループする。
rangeを使わずに、input_listだけを指定すると、中身を1つずつi
に取り出すループになる。
for内での処理1 ファイル名
img_file_name = str(input_list[i])
ファイル名を取得しているだけ。
文字列にしているのは、pathlibでパスを取得するとWindowsだとpathlib.WindowsPath
クラスが返ってくるため、それを文字列に変換している。
opencvで画像判定した際に、元の画像がどれなのか判定するために、ファイル名があったほうが便利なので取得している。
for内の処理2 np.fromfile
img_np = np.fromfile(img_file_name, dtype=np.uint8)
画像をバイナリとして読み込むのが np.fromfile
。
何故そんなことをしてるいのかというと、opencvのファイル読み込みcv2.imread()
は日本語ファイル名の場合正しく処理されないため。
一度バイナリで読み込んで、その後にopencvの形式に変換している。その場合は日本語ファイル名が使えるようになる。
日本語ファイル名でなければ、img = cv2.imread(img_file_name)
だけでよく、次の変換処理もする必要はない。
dtypeはデータ型。
画像はたいていRGBのおのおのが0~255の8bit符号なし整数で表現されているので、np.uint8
を指定している。
for内の処理3 cv2.imdecode
img = cv2.imdecode(img_np, cv2.IMREAD_COLOR)
バイナリで読み込んだままだとopencvでの操作ができないので、opencvで使える形式に変換する。
前述したが、日本語ファイル名を使わないのであれば、バイナリ読み込みも変換も必要なくimg = cv2.imread(img_file_name)
だけでopencv用の操作ができる。
imdecodeはバイナリデータを画像の形式にデコードしてくれる。
引数の一番目がデータ、2番目が形式。
cv2.IMREAD_COLOR
はカラー情報を保持するがアルファチャンネルは保持しない。cv2.IMREAD_GRAYSCALE
にすればグレースケールとなり、次の処理はいらない。カラー画像が必要ない場合はこの段階でグレースケールで読み込みで良い。cv2.IMREAD_UNCHANGED
はカラーかつアルファチャンネルを保持する。
for内の処理4 cv2.cvtColor
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
opencvで画像処理を行う場合、グレースケールに変換してからのことが多い。
そのためcv2.cvtColor
でグレースケールに変換する。第一引数が画像データ、第二引数が変換形式。
cv2.COLOR_BGR2GRAY
はopencvの画像形式データをグレースケールに変換するため、「BGR」2GRAYを使っている。opencvの画像データはBGRの順番で保存されるため注意が必要。
他の形式(Pillowとか)で読み込むと、画像がRGBの並びのデータになる。その場合はcv2.COLOR_RGB2GRAY
を指定する。
まとめ
今回はただファイルを開いてグレースケールに変換しただけ。
画像データを取り扱う場合、単発のファイルだけに処理をするのではなく、多数の画像を一括処理したいなんてケースも多いので、今回はそれに対応した形。
指定の場所だけトリミングしたいとか、輪郭を抽出したいとか、複数の画像に同じ処理をしたい時に使える。
Discussion