ラズパイでカメラキャプチャから年齢・性別予測
先日Tensorflowにて顔画像から年齢や性別が推定できるコードを動かしてみました。
elsammit-beginnerblg.hatenablog.com
環境構築に手こずりましたが結果はまぁまぁ。
ただ、せっかくなのでカメラからキャプチャした人物の判定を行いたい!!
と思い立ち、今回のコードを改良してキャプチャから動的に年齢・性別が予測できるようにしました。
今回はこちらをまとめておこうと思います!!
■とりあえず動かしてみる
コードは前回と同様にこちらの方のコードを利用します。
こちらのコードは画像判別なので、カメラキャプチャに置き換えました。
img = cv2.imread("input10.jpg")
を
capture = cv2.VideoCapture(0) while(True): ret, frame = capture.read()
に置き換えた感じですね。
ただこちらを実行したところ、カクカクに動く。。。どころの話ではない!!
かなりの時間待たされてやっと1フレーム分の測定結果が表示される感じ🤣
このままでは使い物にならない。。
ということでまずはどこに時間を要しているか確認!!
■現状の処理時間測定
今回の制御は大きく分けると、
①顔検知や顔輪郭収集
②検知した顔から年齢・性別予測
③アウトプット画像生成
の3つになります。
それぞれの時間を測定したところ、、、
N = 3で平均が、
①顔検知や顔輪郭収集 :7.63秒
②検知した顔から年齢・性別予測 :5.20秒
③画像に四角形の輪郭を重畳の上画像表示 :0.06秒
となっておりました。
ということで、①と②に時間を要していました。
まぁ、ほぼほぼ想定通りだのですが、、それにしても合計で12秒越え。。
流石にこのままではカメラキャプチャで動的判定は無理だな🤣
そこで、①、②の時間を短縮させていきたいと思います!!
一旦制御を簡単にするために検知人数は1人にします。
■顔検知や顔輪郭収集時間短縮
今回用いていた制御ではMTCNNを用いていました。
【MTCNNとは】
画像の中から、顔を検出するための深層学習モデルです。
顔領域を四角いフレームで囲います。
3つの畳み込みネットワークを使用して画像内の顔を検出します。
3つの畳み込みネットワークとは、
P-net:顔を検出する
R-net:顔の位置を改善する
O-net:顔器官点(目とか鼻とか口)を検出する
※参考:https://www.souya.biz/blog/2019/04/17/mtcnn%E3%81%A8%E3%81%AF/
結構正確に顔検出できるモデルではあるのですが、、、
処理時間が結構かかってしまうので、精度が落ちるがOpenCVの検出を用いることにしました!!
コードはこちらの通り。
def FaceDetection(FaceCascadePath, InputImg): gry_img = cv2.cvtColor(InputImg, cv2.COLOR_BGR2GRAY) face_cascade = cv2.CascadeClassifier(FaceCascadePath) facerect = face_cascade.detectMultiScale(gry_img,scaleFactor=1.5,minNeighbors=2,minSize=(30,30)) face = None if len(facerect) > 0: x = facerect[0][0] y = facerect[0][1] w = facerect[0][2] h = facerect[0][3] face = InputImg[y:y+h,x:x+w] return face,facerect
こちらのコードは、以前githubに公開した顔検知APIの必要な部分のみを抽出して持ってきました。
https://github.com/Elsammit/ImageAPI
上記関数は引数に、
・FaceCascadePath:顔検知用カスケードパス
・InputImg:キャプチャイメージ
を与えることで、
・face:顔抽出画像
・facerect:顔検知時の4点の座標値
が返ってくる関数になります。
返り値は元のMTCNNと同じにしました。
では、置き換えた後の時間を測定!!
結果、
①顔検知や顔輪郭収集 :0.15秒
となりました!!
検知時間が1/50以下になりましたね。
かなり短縮できました👍
■検知した顔から年齢・性別予測の短縮
では次に年齢・性別予測の短縮です。
予測用のコードはこちらです。
def age_gender_predict(faces): if len(faces) > 0: # モデルの設定 if os.isdir("model") == False: pre_model = "https://github.com/yu4u/age-gender-estimation/releases/download/v0.5/weights.28-3.73.hdf5" modhash = 'fbe63257a054c1c5466cfd7bf14646d6' weight_file = get_file("weights.28-3.73.hdf5", pre_model, cache_subdir="model", file_hash=modhash, cache_dir=str(Path(__file__).resolve().parent)) else: weight_file = "model/weights.28-3.73.hdf5" img_size = np.asarray(faces.shape)[1] model = WideResNet(img_size, depth=16, k=8)() model.load_weights(weight_file) # 予測 results = model.predict(faces) Genders = results[0] ages = np.arange(0, 101).reshape(101, 1) Ages = results[1].dot(ages).flatten() return Ages, Genders
こちらのコードのうち、実際に入力イメージから予測をするコードは、
results = model.predict(faces) Genders = results[0] ages = np.arange(0, 101).reshape(101, 1) Ages = results[1].dot(ages).flatten()
であり、このコードの前は初期化処理になります。
そこで、、、
# 性別・年齢予測モデル初期化. def init_age_gender(): if os.isdir("model") == False: pre_model = "https://github.com/yu4u/age-gender-estimation/releases/download/v0.5/weights.28-3.73.hdf5" modhash = 'fbe63257a054c1c5466cfd7bf14646d6' weight_file = get_file("weights.28-3.73.hdf5", pre_model, cache_subdir="model", file_hash=modhash, cache_dir=str(Path(__file__).resolve().parent)) else: weight_file = "model/weights.28-3.73.hdf5" img_size=64 model = WideResNet(img_size, depth=16, k=8)() model.load_weights(weight_file) return model # 性別・年齢予測 def age_gender_predict(faces, model): if len(faces) > 0: # 予測 results = model.predict(faces) Genders = results[0] ages = np.arange(0, 101).reshape(101, 1) Ages = results[1].dot(ages).flatten() gender = "" if Genders[0][0] < 0.5: gender ="Male" else: gender = "FeMale" return Ages, gender
とし、モデルの初期化と性別・年齢予測を分割しました。
そしてmain関数はこちらの通り、
モデル初期化は事前に実施した後、
キャプチャ画像から年齢・性別予測をループさせることにしました。
capture = cv2.VideoCapture(0) img_size = 64 model = init_age_gender() while(True): ret, frame = capture.read() # resize the window windowsize = (800, 600) frame = cv2.resize(frame, windowsize) face,facerect = FaceDetection("haarcascade_frontalface_default.xml", frame) if len(facerect) > 0: for rec in facerect: cv2.rectangle(frame, tuple(rec[0:2]), tuple(rec[0:2]+rec[2:4]), (0,255,0), 2) aligned = misc.imresize(face, (img_size, img_size), interp='bilinear') test = np.zeros((1, img_size, img_size, 3), dtype = "uint8") test[0, :, :, :] = cv2.cvtColor(aligned, cv2.COLOR_BGR2RGB) Ages, Genders = age_gender_predict(test, model) label = "{}, {}".format(int(Ages[0]), Genders) draw_label(frame, (facerect[0, 0], facerect[0, 1]), label) cv2.imshow('title',frame) if cv2.waitKey(1) & 0xFF == ord('q'): break
こちらで時間測定実行!!
結果、、、
②検知した顔から年齢・性別予測 :1.04秒
となりました。
ちょっと初期化時間伸びましたが、予測実行時間としては1/5以下に出来ました!!
こちらも上々👍
■実際に動かしてみる
変更後の時間は、
①顔検知や顔輪郭収集 :0.15秒
②検知した顔から年齢・性別予測 :1.04秒
③画像に四角形の輪郭を重畳の上画像表示 :0.06秒
となり、合計1.21秒!!
ちょっとカクカクしちゃいますが、、、これぐらいなら使い物になりそうです!!
ということでカメラキャプチャから動的に年齢・性別判定してみます!!
まずは男性。
次は女性。
年齢が少し高めに出てしまっている気が。。🤔
性別はまぁまぁかな?
と思ったのですが、、、ちょっと性別も怪しい感じでした😥
顔検知出来ていない時もありましたが、なにより性別・年齢ともに。。。
全然違う!!
他の写真も試してみましたが、、
恐らく髪の毛が顔にかかっている分判定を誤ってしまっていそうだな。と思いました。
ちょっと調査・検証が必要ですね😅
■最後に
今回は1人のみの検知でしたが、動的に年齢・性別が行えるように出来ました!!
複数人検知できるようにすると時間伸びちゃうと思いますが、、、どの程度伸びるのだろう🤔
もう少し検証してみます。
後は精度を上げるための策を考えてみたいと思います!!