🦔

opencv+python フォルダ内の全てのjpg画像をforループで処理する

2022/11/03に公開

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でよい。

カラー画像がいらない場合は、imdecodecv2.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については以下で。

https://zenn.dev/alivelimb/articles/0f3f8d61d91d57

https://qiita.com/studio_haneya/items/11c9e825bd8068af7e87

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