概要
posenetとopenposeの比較した結果、やはりopenposeを使わなければならないことになりました。
顔認識を書き込む実験に、face_recognitionのlibraryを用いることで、結構簡単なコードで、良く顔を認識できた。
PosenetとOpenposeの比較
PosenetやOpenposeを実装して、それぞれの出力を可視化します。
Openpose
|
Posenet
|
---|
結果は以上の図になります、左側のがOpenpose、右側はPosenetです。
画像の様子を見ると、Posenetのkeypoint検出は不安定と感じる。
動きは小さくても、出力は変わる。また、常に複数の検出できないkeypointがある。
openposeだと、出力は安定だし、keypointもより多く検出できる。
以上より、openposeの効果はposenetより大幅に超えると考えられる。
現在開発しているデモ版では、keypointを基にして、手作りのルールを加わって、人の睡眠状態の判別を行うので、keypointの正しさは非常に重要だと考えられて、Posenetを使わないにします。
ただし、Posenetの利点としては、スビートが速い(fpsは何百を超える)とgpuのramの占有率が低いの二つが挙げられる
顔認識を書き込む
今回顔認識には、face_recognition libraryを使っています。
このlibraryの仕組みはまだ習っていないけど、一応実装して、結果を見たいと思います。
face_recognition
face_recogntionのセットアップはここに参考します
以下のコードを例として、このlibraryの使い方を簡単に説明する。
import face_recognition
import glob
import os
import pickle
import cv2
import numpy as np
import time
from tqdm import tqdm
class Face_wrapper:
def __init__(self,dir):
self.dir = dir
self.known_face_names = []
self.known_face_encodings = []
self.process_this_frame = 0
self._encoding()
def _encoding(self):
print('*** encoding face ***')
image_paths = glob.glob(os.path.join(self.dir,'*.jpg'))
names = [os.path.basename(path)[:-4] for path in image_paths]
encoding_file_path = os.path.join(self.dir,'face_encondings.pkl')
try:
with open(encoding_file_path,'rb') as f:
encoding_dict = pickle.load(f)
except FileNotFoundError as e:
encoding_dict = {}
finally:
for idx,name in tqdm(enumerate(names)):
if name not in encoding_dict:
img = face_recognition.load_image_file(image_paths[idx])
try:
encoding_dict[name] = face_recognition.face_encodings(img)[0]
except IndexError as e:
print("*** can not recognize a face from :{} skipping ***".format(image_paths[idx]))
with open(encoding_file_path,'wb') as f:
pickle.dump(encoding_dict,f)
for key,value in encoding_dict.items():
self.known_face_names.append(key)
self.known_face_encodings.append(value)
def recognize(self,image,image_to_draw,resize_ratio=1):
small_img = cv2.resize(image,(0,0),fx=1,fy=1)
rgb_small_img = cv2.cvtColor(small_img,cv2.COLOR_BGR2RGB)
if self.process_this_frame%3 == 0:
print('detect')
time1=time.time()
self.face_locations = face_recognition.face_locations(rgb_small_img,model='cnn')
print(time.time()-time1)
face_encodings = face_recognition.face_encodings(rgb_small_img,self.face_locations)
self.face_names = []
for face_encoding in face_encodings:
matches = face_recognition.compare_faces(self.known_face_encodings,face_encoding)
name = "Unknown"
face_distances = face_recognition.face_distance(self.known_face_encodings,face_encoding)
best_match_index = np.argmin(face_distances)
if matches[best_match_index]:
name = self.known_face_names[best_match_index].split('_')[0]
self.face_names.append(name)
self.process_this_frame += 1
print(self.face_names)
for (top, right, bottom, left), name in zip(self.face_locations, self.face_names):
cv2.rectangle(image, (left, top), (right, bottom), (0, 0, 255), 2)
# Draw a label image a name below the face
cv2.rectangle(image_to_draw, (left, bottom), (right, bottom+35), (0, 0, 255), cv2.FILLED)
cv2.putText(image_to_draw, name, (left + 6, bottom + 15), cv2.FONT_HERSHEY_DUPLEX, 1.0, (255, 255, 255), 1)
return image_to_draw
まず _encoding()
を使って、認識したい人の画像を長さが128のエンコーディングに変換する。
encoding_dict[name] = face_recognition.face_encodings(img)[0]
そして、認識したい画像を入力とし、recognize()
で顔認識を行う。
入力画像に顔の位置をみつかる。
self.face_locations = face_recognition.face_locations(rgb_small_img,model='cnn')
次に、その位置を利用して、入力画像に存在する顔をエンコーディングに変換する
face_encodings = face_recognition.face_encodings(rgb_small_img,self.face_locations)
そのエンコーディングを既存のエンコーディングと比較して、最も合うのが認識の結果になる。
matches = face_recognition.compare_faces(self.known_face_encodings,face_encoding)
実装結果
10人の顔写真を用意して、実装してみます。
結果は以下のURLで見られる。
https://youtu.be/6W2ULZxxLFg
説明すると、ラベルの数字1から10までは10人の氏名を示して、'me'は本人を示している。
結果を見ると、人が横になる時、顔が非常に小さいと見えても、ほとんど正しく認識できる。
横顔の場合に、誤認識や顔検出できないなどのことがあるけど、実験の結果(載せていない)によって、認識したい人の横顔写真もデータベースに入れると、誤認識の発生率を減らせる。
今後やりたいこと
- デモ版を別の環境で実験する
- いただいた顔認識コードを実行
- 食事している人の検出のAIの検討