顔認識処理

「Jestson nano」上の顔認識処理を話しましょう。

Jetson nanoについて

MediaRecorderかAudioRecordか

アンドロイドで録音処理を行う場合、MediaRecorderとAudioRecordの2つのクラスが用意されている。

  • MediaRecorder:ファイルへの書き込みが前提になっている。
  • AudioRecord:音声の入力情報を取得してリアルタイムの処理を行える。ファイルへの保存を行う場合は、自前でフォーマットに合わせた書き込みを行わなければならない。 つまり、書き込みのタイミングを制御できる。

音声認識を処理を行う場合はAudioRecordクラスのリアルタイム処理が処理が向いている。

OpenVXとVisionWorks

Amazon Echoは、Bluetoothスピーカである


Amazon Echo

Alexa Skills Kit

乃木坂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()

todo



森の小径2