はじめに
こんにちは!「顔認証AI:学習用の実装 前編」 を開いていただきありがとうございます!
前回記述した通り、今回は実際にプログラムを組んでいきます。
今回作るもの
本当は機械学習の部分も実装しようと思ったのですが、思ったよりも長くなってしまいそうなので急遽半分に分けて、もう半分を次回に持ち越すことにしました!
本来この回で描こうとしていたのは、
1. 画像データの水増し
2. 増やした画像を学習データとテストデータに振り分け
3. 機械学習の下準備
4. 機械学習
↑こんな感じのものなのですが、半分に分けて 「1. 画像データの水増し」と「2. 増やした画像を学習データとテストデータに振り分け」 について書いていきます!
実装
import os import cv2 import glob from scipy import ndimage from posix import listdir import shutil import random # 加工前の画像ディレクトリのパス original_dir_path = "./faces/original/" # 加工後の画像ディレクトリのパス augumente_faces_dir_path = "./faces/augumente_faces/" # テストディレクトリのパス test_dir_path = "./faces/test/" # 水増しの準備 def data_augumente_pre(): # 水増しする original_dir = os.listdir("./faces/original/") for name in original_dir: # DS.Storeを取ってきたらcontinue if name is ".DS.Store": continue # 確認用の表示 print(name + "さんの顔を水増しします。") # 水増しする人物の画像データを呼び出す faces = listdir(original_dir_path + name) # 加工した画像を置く場所を確保 os.makedirs(augumente_faces_dir_path + name, exist_ok=True) # 一つずつ加工していく for i in len(faces): # 画像読み込み face_img = cv2.imread(original_dir_path + name + "/" + faces[i]) data_augumente(face_img, name) # 画像を加工して水増しする def data_augumente(face_img, name): rotate_left = -15 rotate_center = 0 rotate_right = 15 # 左回転 img_rotate_left = ndimage.rotate(face_img, rotate_left) cv2.imwrite(augumente_faces_dir_path + name + "/" + name + "_img_rotate_left.jpg", img_rotate_left) threshold(img_rotate_left, rotate_left, name) gaussian_blur(img_rotate_left, rotate_left, name) reverse(img_rotate_left, rotate_left, name) # 正面 img_rotate_center = ndimage.rotate(face_img, rotate_center) cv2.imwrite(augumente_faces_dir_path + name + "/" + name + "_img_rotate_center.jpg", img_rotate_center) threshold(img_rotate_center, rotate_center, name) gaussian_blur(img_rotate_center, rotate_center, name) reverse(img_rotate_center, rotate_center, name) # 右回転 img_rotate_right = ndimage.rotate(face_img, rotate_right) cv2.imwrite(augumente_faces_dir_path + name + "/" + name + "_img_rotate_rigt.jpg", img_rotate_right) threshold(img_rotate_right, rotate_right, name) gaussian_blur(img_rotate_right, rotate_right, name) reverse(img_rotate_right, rotate_right, name) # ぼかし処理で水増し def gaussian_blur(face_img, rotate_value, name): # ぼかし処理 img_filter = cv2.GaussianBlur(face_img, (5, 5), 0) # 保存したファイル名 file_name = "/" + name + "_GaussianBlur_" + rotate_value + ".jpg" # 書き込み cv2.imwrite(augumente_faces_dir_path + name + file_name, img_filter) # 閾値処理で水増し def threshold(face_img, rotate_value, name): # 閾値処理 img_thr = cv2.threshold(face_img, 100, 255, cv2.THRESH_TOZERO)[1] # 保存したファイル名 file_name = "/" + name + "_threshold_" + rotate_value + ".jpg" # 書き込み cv2.imwrite(augumente_faces_dir_path + name + file_name, img_thr) # 反転処理で水増し def reverse(face_img, rotate_value, name): # 反転処理 img_flip = cv2.flip(face_img, 1) # 保存したファイル名 file_name = "/" + name + "_flip_" + rotate_value + ".jpg" # 書き込み cv2.imwrite(augumente_faces_dir_path + name + file_name, img_flip) # 画像データ一覧の中からランダムにテストデータに移行 def random_sort(): random_v = 0.2 names = os.listdir(augumente_faces_dir_path) for name in names: # DS.Storeを取ってきたらcontinue if name is ".DS.Store": continue # testディレクトリに人名のディレクトリを作成 os.mkdir(test_dir_path + name) # 水増しした画像データの一覧取得 imgs = os.listdir(augumente_faces_dir_path + name) # imgsの数を元に、その中の2割の番号を取得 random_numbers = random.sample(range(len(imgs)), k=round(len(imgs)*random_v)) # ランダムに選んだ番号を元に画像を移動 for random_number in random_numbers: shutil.move(augumente_faces_dir_path + name + "/" + imgs[random_number], test_dir_path + name) if __name__ == '__main__': data_augumente_pre() random_sort()
実装の解説
それでは解説していきます。
全体の準備
import os import cv2 import glob from scipy import ndimage from posix import listdir import shutil import random # 加工前の画像ディレクトリのパス original_dir_path = "./faces/original/" # 加工後の画像ディレクトリのパス augumente_faces_dir_path = "./faces/augumente_faces/" # テストディレクトリのパス test_dir_path = "./faces/test/"
↑まずは準備。各種インポートと各ディレクトリのパスを変数にしておきます。
水増しの準備
# 水増しの準備 def data_augumente_pre(): # 水増しする original_dir = os.listdir("./faces/original/") for name in original_dir: # DS.Storeを取ってきたらcontinue if name is ".DS.Store": continue # 確認用の表示 print(name + "さんの顔を水増しします。") # 水増しする人物の画像データを呼び出す faces = listdir(original_dir_path + name) # 加工した画像を置く場所を確保 os.makedirs(augumente_faces_dir_path + name, exist_ok=True) # 一つずつ加工していく for i in len(faces): # 画像読み込み face_img = cv2.imread(original_dir_path + name + "/" + faces[i]) rotate(face_img, name)
↑画像を加工して増やす関数。
# 水増しする original_dir = os.listdir("./faces/original/")
originalディレクトリに保存してある名前全てに適用したいのでoriginal_dirに登録してある名前の一覧を取っておきます。次のループで取得した名前を一つづつ回していきます。
# 水増しする人物の画像データを呼び出す faces = listdir(original_dir_path + name) # 加工した画像を置く場所を確保 os.makedirs(augumente_faces_dir_path + name, exist_ok=True)
名前を取得できたら、その人物の画像データの一覧を取得します。ついでにtestディレクトリにその人物のテストデータを入れるためのディレクトリを作っておきます。
# 画像読み込み face_img = cv2.imread(original_dir_path + name + "/" + faces[i]) data_augumente(face_img, name)
最後にループの中で画像を一つずつ読み込み、data_augumenteで本格的な加工に入る。
水増し
# 画像を加工して水増しする def data_augumente(face_img, name): rotate_left = -15 rotate_center = 0 rotate_right = 15 # 左回転 img_rotate_left = ndimage.rotate(face_img, rotate_left) cv2.imwrite(augumente_faces_dir_path + name + "/" + name + "_img_rotate_left.jpg", img_rotate_left) threshold(img_rotate_left, rotate_left, name) gaussian_blur(img_rotate_left, rotate_left, name) reverse(img_rotate_left, rotate_left, name) # 正面 img_rotate_center = ndimage.rotate(face_img, rotate_center) cv2.imwrite(augumente_faces_dir_path + name + "/" + name + "_img_rotate_center.jpg", img_rotate_center) threshold(img_rotate_center, rotate_center, name) gaussian_blur(img_rotate_center, rotate_center, name) reverse(img_rotate_center, rotate_center, name) # 右回転 img_rotate_right = ndimage.rotate(face_img, rotate_right) cv2.imwrite(augumente_faces_dir_path + name + "/" + name + "_img_rotate_rigt.jpg", img_rotate_right) threshold(img_rotate_right, rotate_right, name) gaussian_blur(img_rotate_right, rotate_right, name) reverse(img_rotate_right, rotate_right, name)
↑今回の水増し方法は元画像を左に回転させたもの、正面のもの、右に回転したものを用意し、それぞれの画像に対して、ぼかし処理、閾値処理、反転処理を行なっていきます。
↑左から、正面の画像、右回転させた画像、左回転させた画像
ぼかし処理
ぼかし処理とは、ある点を周辺の色で平均化する処理のことを言います。今回はガウシアンぼかしと言う方法を使います。
# ぼかし処理で水増し def gaussian_blur(face_img, rotate_value, name): # ぼかし処理 img_filter = cv2.GaussianBlur(face_img, (5, 5), 0) # 保存したファイル名 file_name = "/" + name + "_GaussianBlur_" + str(rotate_value) + ".jpg" # 書き込み cv2.imwrite(augumente_faces_dir_path + name + file_name, img_filter)
少し分かりづらいですが、よく見るとぼかしがかかっているのが分かります。
閾値処理
# 閾値処理で水増し def threshold(face_img, rotate_value, name): # 閾値処理 img_thr = cv2.threshold(face_img, 100, 255, cv2.THRESH_TOZERO)[1] # 保存したファイル名 file_name = "/" + name + "_threshold_" + str(rotate_value) + ".jpg" # 書き込み cv2.imwrite(augumente_faces_dir_path + name + file_name, img_thr)
反転処理
# 反転処理で水増し def reverse(face_img, rotate_value, name): # 反転処理 img_flip = cv2.flip(face_img, 1) # 保存したファイル名 file_name = "/" + name + "_flip_" + str(rotate_value) + ".jpg" # 書き込み cv2.imwrite(augumente_faces_dir_path + name + file_name, img_flip)
ランダムにテストデータへ
# 画像データ一覧の中からランダムにテストデータに移行 def random_sort(): random_v = 0.2 names = os.listdir(augumente_faces_dir_path) for name in names: # DS.Storeを取ってきたらcontinue if name is ".DS_Store": continue # testディレクトリに人名のディレクトリを作成 os.mkdir(test_dir_path + name) # 水増しした画像データの一覧取得 imgs = os.listdir(augumente_faces_dir_path + name) # imgsの数を元に、その中の2割の番号を取得 random_numbers = random.sample(range(len(imgs)), k=round(len(imgs)*random_v)) # ランダムに選んだ番号を元に画像を移動 for random_number in random_numbers: shutil.move(augumente_faces_dir_path + name + "/" + imgs[random_number], test_dir_path + name)
↑今回は加工した画像の中から、2割の画像をテストデータに移すことにします。 はじめに namesに登録してある人名の一覧を取ってきます。
# testディレクトリに人名のディレクトリを作成 os.mkdir(test_dir_path + name) # 水増しした画像データの一覧取得 imgs = os.listdir(augumente_faces_dir_path + name) # imgsの数を元に、その中の2割の番号を取得 random_numbers = random.sample(range(len(imgs)), k=round(len(imgs)*random_v))
人名でループを回し、testディレクトリの中にその人物の画像データを入れるためのディレクトリを作成します。その後、水増し後の画像データの総数を上限としてランダムに要素番号を作成します。
例:データ数が20の場合、0~19の中からランダムに4つの数字(20の2割なので)が選ばれ、random_numberに格納されます。中身は[3, 8, 16, 17]のようになります。
# ランダムに選んだ番号を元に画像を移動 for random_number in random_numbers: shutil.move(augumente_faces_dir_path + name + "/" + imgs[random_number], test_dir_path + name)
前述したランダムな要素番号を持っているデータをtestディレクトリに移します。shutil.move()を使用することで簡単に移動することができます。
最後に
いかがだったでしょうか! 長すぎることもあって、二つに分けさせてもらいました。次回は機械学習の部分を実装していきたいと思うのでお楽しみに!