顔認識処理
「Jestson nano」上の顔認識処理を話しましょう。
Jetson nanoについて
MediaRecorderかAudioRecordか
アンドロイドで録音処理を行う場合、MediaRecorderとAudioRecordの2つのクラスが用意されている。
- MediaRecorder:ファイルへの書き込みが前提になっている。
- AudioRecord:音声の入力情報を取得してリアルタイムの処理を行える。ファイルへの保存を行う場合は、自前でフォーマットに合わせた書き込みを行わなければならない。 つまり、書き込みのタイミングを制御できる。
音声認識を処理を行う場合はAudioRecordクラスのリアルタイム処理が処理が向いている。
乃木坂46の顔識別
以下のURLを参照しています。
KerasのCNNで、顔認識AIを作って見た〜スクレイピングからモデルまで〜1.顔認識したい人の名前を複数定義する
以下のコマンドでフォルダを作成します。
$ python3 init.py
init.py
import os # オリジナル画像用のフォルダ os.makedirs("./Original", exist_ok=True) # 顔の画像用のフォルダ os.makedirs("./Face", exist_ok=True) # ImgSizeで設定したサイズに編集された顔画像用のフォルダ os.makedirs("./FaceEdited", exist_ok=True) # テストデータを入れる用のフォルダ os.makedirs("./test", exist_ok=True)
2.スクレイピングでGoogle画像検索から画像を取得
Google画像検索から画像を取得
以下のURLを参照して、Pythonライブラリicrawlerを使い簡単に画像データを集めます。
pythonライブラリicrawlerを使い簡単に画像データを集めるPythonライブラリicrawlerは、以下のようにインストールします。
$ pip3 install icrawler
crawler.py
「crawler.py」は、以下のように「icrawler」を使用してGoogle画像検索から画像を取得します。
from icrawler.builtin import GoogleImageCrawler import sys import os argv = sys.argv if not os.path.isdir(argv[1]): os.makedirs(argv[1]) crawler = GoogleImageCrawler(storage = {"root_dir" : argv[1]}) crawler.crawl(keyword = argv[2], max_num = 1000, file_idx_offset="auto") # neru: 長濱ねる # nanami:橋本奈々未
「crawler.py」は、以下のように「第1パラメータ(argv[1])」が画像を保存するディレクトリを示し、「第2パラメータ(argv[2])」が画像のキーを示します。
$ python3 crawler.py ./Original/neru 長濱ねる $ python3 crawler.py ./Original/nanami 橋本奈々未
上記のコマンドで、「./Original/neru」ディレクトリに「長濱ねる」の画像が取得され、「./Original/nanami」ディレクトリに「橋本奈々未」の画像が取得されます。
3.顔部分を抽出し切り抜く
OpneCV2のソースをダウンロード
顔部分を抽出するのに、OpneCV2を利用します。 下記のgithubから、OpneCV2をダウンロードします。
https://github.com/opencv/opencv
OpneCV2を解凍すると、以下の「haarcascade_frontalface_default.xml」ファイルがある「haarcascades」ディレクトリがあるので、それを現ディレクトリにコピーします。
#cascade_path = './opencv-master/data/haarcascades/haarcascade_frontalface_default.xml'
Python用のOpneCV2をインストール
以下のコマンドで、Python用のOpneCV2をインストールします。
$ pip3 install opencv-python
detect_face.py
「detect_face.py」は、以下のようにOpneCV2を使用して、「Original」ディレクトリから画像を読み込んで顔を切り取って「Face」ディレクトリに保存します。
import cv2 import glob import os """ 「Original」ディレクトリから画像を読み込んで顔を切り取って「Face」ディレクトリに保存. """ #names = ["asuka","nanami","mai","nanase","ikuta"] names = ["neru","nanami"] out_dir = "./Face" os.makedirs(out_dir, exist_ok=True) for i in range(len(names)): #元画像を取り出して顔部分を正方形で囲み、64×64pにリサイズ、別のファイルにどんどん入れてく in_dir = "./Original/" + names[i]+"/*.jpg" in_jpg = glob.glob(in_dir) os.makedirs(out_dir + names[i], exist_ok=True) # print(in_jpg) print(len(in_jpg)) for num in range(len(in_jpg)): image=cv2.imread(str(in_jpg[num])) if image is None: print("Not open:",num) continue image_gs = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cascade = cv2.CascadeClassifier("./haarcascades/haarcascade_frontalface_alt.xml") #cascade = cv2.CascadeClassifier("haarcascades/haarcascade_frontalface_alt.xml") # 顔認識の実行 face_list=cascade.detectMultiScale(image_gs, scaleFactor=1.1, minNeighbors=2,minSize=(64,64)) #顔が1つ以上検出された時 if len(face_list) > 0: for rect in face_list: x,y,width,height=rect image = image[rect[1]:rect[1]+rect[3],rect[0]:rect[0]+rect[2]] if image.shape[0]<64: continue image = cv2.resize(image,(64,64)) #保存 fileName=os.path.join(out_dir+"/"+names[i],str(num)+".jpg") cv2.imwrite(str(fileName),image) print(str(num)+".jpgを保存しました.") #顔が検出されなかった時 else: print("no face") continue print(image.shape)
4.不要な画像を削除(自力で)
Faceフォルダにある画像を確認します。
Google画像検索の上から画像を取得しているので、関係のない写真や、関係ない人の顔画像があります。
また、顔でない部分の写真もあるので、全て削除します。(正直ここが一番めんどくさい)
5.画像処理をして、画像の水増し
ここまでで、Originalフォルダに写真を保存して、Faceフォルダに顔写真を保存できました。
また、不要な写真も除去できました。
ここから、画像を閾値処理、ぼかし処理、回転処理をして水増しを行い、「FaceEdited」フォルダに格納します。
画像の水増し処理
画像の水増し処理は、以下のコマンド(inflation.py)で行います。
$ python3 inflation.py
inflation.py
「inflation.py」は、以下のようにOpneCV2を使用して、「Face」ディレクトリから画像を読み込んで、 画像を閾値処理、ぼかし処理、回転処理をして水増しを行い、「FaceEdited」フォルダに格納します。
import os import cv2 import glob from scipy import ndimage """ Faceディレクトリから画像を読み込んで回転、ぼかし、閾値処理をして「FaceEdited」ディレクトリに保存する. """ #names = ["asuka","mai","nanase","nanami","ikuta"] names = ["neru","nanami"] os.makedirs("./FaceEdited", exist_ok=True) for name in names: in_dir = "./Face/" + name + "/*.jpg" out_dir = "./FaceEdited/" + name os.makedirs(out_dir, exist_ok=True) in_jpg = glob.glob(in_dir) img_file_name_list = os.listdir("./Face/" + name + "/") for i in range(len(in_jpg)): #print(str(in_jpg[i])) img = cv2.imread(str(in_jpg[i])) # 回転 for ang in [-10,0,10]: img_rot = ndimage.rotate(img,ang) img_rot = cv2.resize(img_rot,(64,64)) fileName=os.path.join(out_dir,str(i)+"_"+str(ang)+".jpg") cv2.imwrite(str(fileName),img_rot) # 閾値 img_thr = cv2.threshold(img_rot, 100, 255, cv2.THRESH_TOZERO)[1] fileName=os.path.join(out_dir,str(i)+"_"+str(ang)+"thr.jpg") cv2.imwrite(str(fileName),img_thr) # ぼかし img_filter = cv2.GaussianBlur(img_rot, (5, 5), 0) fileName=os.path.join(out_dir,str(i)+"_"+str(ang)+"filter.jpg") cv2.imwrite(str(fileName),img_filter) print("画像の水増しに成功しました!")
6.トレーニングデータ、テストデータの準備
「FaceEdited」フォルダにある画像の2割をテストフォルダに移行します。
テストデータの準備
テストデータの準備は、以下のコマンド(devide_test_train.py)で行います。
$ python3 devide_test_train.py
devide_test_train.py
「devide_test_train.py」は、以下のように「FaceEdited」ディレクトリから画像を読み込んで、 シャッフル処理を行い、その2割の画像を「test」フォルダに格納します。
# 2割をテストデータに移行 import shutil import random import glob import os #names = ["asuka","nanami","mai","nanase","ikuta"] names = ["neru","nanami"] os.makedirs("./test", exist_ok=True) for name in names: in_dir = "./FaceEdited/" + name + "/*" in_jpg = glob.glob(in_dir) img_file_name_list = os.listdir("./FaceEdited/" + name + "/") #img_file_name_listをシャッフル、そのうち2割を「test」ディテクトリに入れる random.shuffle(in_jpg) os.makedirs('./test/' + name, exist_ok=True) for t in range(len(in_jpg)//5): shutil.move(str(in_jpg[t]), "./test/" + name) print("2割をテストデータに移行しました")
7.Kerasでモデルの構築、学習、評価
ラスマエでは、Kerasでモデルの構築、学習、評価を行います。
モデルの構築、学習、評価は、以下のコマンド(label_and_learn.py)で行います。
$ python3 label_and_learn.py
label_and_learn.py
import os import cv2 import glob import numpy as np import matplotlib.pyplot as plt from keras.utils import np_utils from keras.utils.np_utils import to_categorical # 顔認識する対象を決定(検索ワードを入力) #SearchName = ["名前1","名前2","名前3","名前4"] SearchName = ["neru","nanami"] # 画像の取得枚数の上限 ImgNumber =600 # CNNで学習するときの画像のサイズを設定(サイズが大きいと学習に時間がかかる) ImgSize=(250,250) input_shape=(250,250,3) # 教師データのラベル付け X_train = [] Y_train = [] for i in range(len(SearchName)): img_file_name_list=os.listdir("./FaceEdited/"+SearchName[i]) print("{}:トレーニング用の写真の数は{}枚です。".format(SearchName[i],len(img_file_name_list))) for j in range(0,len(img_file_name_list)-1): n=os.path.join("./FaceEdited/"+SearchName[i]+"/",img_file_name_list[j]) img = cv2.imread(n) if img is None: print('image' + str(j) + ':NoImage') continue else: r,g,b = cv2.split(img) img = cv2.merge([r,g,b]) X_train.append(img) Y_train.append(i) print("教師データのラベル付けを終了") # テストデータのラベル付け X_test = [] # 画像データ読み込み Y_test = [] # ラベル(名前) for i in range(len(SearchName)): img_file_name_list=os.listdir("./test/"+SearchName[i]) print("{}:テスト用の写真の数は{}枚です。".format(SearchName[i],len(img_file_name_list))) for j in range(0,len(img_file_name_list)-1): n=os.path.join("./test/"+SearchName[i]+"/",img_file_name_list[j]) img = cv2.imread(n) if img is None: print('image' + str(j) + ':NoImage') continue else: r,g,b = cv2.split(img) img = cv2.merge([r,g,b]) X_test.append(img) Y_test.append(i) X_train=np.array(X_train) X_test=np.array(X_test) y_train = to_categorical(Y_train) #y_train = np_utils.to_categorical(Y_train,5) y_test = to_categorical(Y_test) #y_test = np_utils.to_categorical(Y_test,5) print("テストデータのラベル付けを終了") from keras.layers import Activation, Conv2D, Dense, Flatten, MaxPooling2D from keras.models import Sequential # モデルの定義 print("モデルの定義") model = Sequential() model.add(Conv2D(input_shape=input_shape, filters=32,kernel_size=(3, 3), strides=(1, 1), padding="same")) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(filters=32, kernel_size=(3, 3), strides=(1, 1), padding="same")) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Conv2D(filters=32, kernel_size=(3, 3), strides=(1, 1), padding="same")) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Flatten()) model.add(Dense(256)) model.add(Activation("sigmoid")) model.add(Dense(128)) model.add(Activation('sigmoid')) # 分類したい人数を入れる model.add(Dense(len(SearchName))) model.add(Activation('softmax')) # コンパイル print("コンパイル") model.compile(optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy']) # 学習 print("学習") history = model.fit(X_train, y_train, batch_size=70, epochs=50, verbose=1, validation_data=(X_test, y_test)) # 汎化制度の評価・表示 print("汎化制度の評価・表示") score = model.evaluate(X_test, y_test, batch_size=32, verbose=0) print('validation loss:{0[0]}\nvalidation accuracy:{0[1]}'.format(score)) #acc, val_accのプロット plt.plot(history.history["acc"], label="acc", ls="-", marker="o") plt.plot(history.history["val_acc"], label="val_acc", ls="-", marker="x") plt.ylabel("accuracy") plt.xlabel("epoch") plt.legend(loc="best") plt.show() #モデルを保存 print("モデルを保存します。") model.save("MyModel.h5") print("モデルを保存しました。")
8.Kerasでモデルを使用した顔認識
最後は、いよいよ、Kerasでモデルのモデルを使用した顔認識を行います。
モデルを使用した顔認識は、以下のコマンド(predict.py)で行います。
$ python3 predict.py
predict.py
import numpy as np import matplotlib.pyplot as plt import cv2 from keras.models import load_model import sys """ spyderで実行するときは実行→ファイルごとの設定からコマンドライン引数を渡す """ def detect_face(image): print("detect_face() 1.image.shape:", image.shape) #opencvを使って顔抽出 image_gs = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cascade = cv2.CascadeClassifier("./haarcascades/haarcascade_frontalface_alt.xml") # 顔認識の実行 face_list=cascade.detectMultiScale(image_gs, scaleFactor=1.1, minNeighbors=2,minSize=(64,64)) #face_list=cascade.detectMultiScale(image_gs, scaleFactor=1.1, minNeighbors=2,minSize=(250,250)) print("detect_face() 2.len(face_list):", len(face_list)) #顔が1つ以上検出された時 if len(face_list) > 0: for rect in face_list: x,y,width,height=rect cv2.rectangle(image, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), (255, 0, 0), thickness=3) img = image[rect[1]:rect[1]+rect[3],rect[0]:rect[0]+rect[2]] if image.shape[0]<64: #if image.shape[0]<250: print("detect_face() 3.too small") continue #img = cv2.resize(image,(64,64)) img = cv2.resize(image,(250,250)) img=np.expand_dims(img,axis=0) name = detect_who(img) cv2.putText(image,name,(x,y+height+20),cv2.FONT_HERSHEY_DUPLEX,1,(255,0,0),2) #顔が検出されなかった時 else: print("detect_face() 4.no face") return image def detect_who(img): #予測 name="" #print(model.predict(img)) nameNumLabel=np.argmax(model.predict(img)) print("detect_who() 1.nameNumLabel: ", nameNumLabel) if nameNumLabel== 0: #name="Saito Asuka" name="nagahama neru" elif nameNumLabel==1: #name="Shiraishi Mai" name="hashimoto nanami" elif nameNumLabel==2: name="Nishino Nanase" elif nameNumLabel==3: name="Hashimoto Nanami" elif nameNumLabel==4: name="Ikuta Erika" print("予測 nameNumLabel:", nameNumLabel, "name: ", name) return name if __name__ == '__main__': model = load_model('./MyModel.h5') if len(sys.argv) != 2: print('invalid argment') sys.exit() else: im_jpg = sys.argv[1] image=cv2.imread("./predict/"+im_jpg) if image is None: print("Not open:") b,g,r = cv2.split(image) image = cv2.merge([r,g,b]) whoImage=detect_face(image) plt.imshow(whoImage) plt.show()
森の小径2