今回、Dlib,OpenCVを使って頭部の形を抽出か、楕円をモデルとして表示したいと思っています。
環境
・python 3.7.5
・opencv 4.1.2
・dlib 19.18.0
以下のサイトを参考にさせてもらっています。
→https://cppx.hatenablog.com/entry/2017/12/25/231121
顔器官の取得のために以下のサイトから学習済みモデルをダウンロードしました。
http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
今のところできているプログラムは以下の通りです。
head.py
import dlib
import cv2
detector = dlib.get_frontal_face_detector()#顔の検出器
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
#↑予測器(学習済みモデルのパスを指定している)
def get_center(gray_img):#画像の重心を求める ↓opencvがやってくれる
moments = cv2.moments(gray_img, False)
try:
return int(moments['m10'] / moments['m00']), int(moments['m01'] / moments['m00'])
except:
return None
#たまに0除算が発生するので処理している ↑
#これで取得した座標を瞳の位置とする ↑
def is_close(y0, y1):#目が閉じているか
if abs(y0 - y1) < 10:#引数は、上瞼のy座標と、下瞼のy座標
return True #上瞼と、下瞼のy座標の差で判別する
return False
def head_point(img,parts):
heads = [
parts[1],part[16],
]
#瞳座標を取得
def eye_point(img, parts, left=True):
#顔画像と顔器官座標、左目or右目(Trueで左目)
#目玉の座標を取得する↓
if left:
eyes = [
parts[36],
min(parts[37], parts[38], key=lambda x: x.y),
max(parts[40], parts[41], key=lambda x: x.y),
parts[39],
]
#36~39番目に入っているのでその4点抜き出す
else:
eyes = [
parts[42],
min(parts[43], parts[44], key=lambda x: x.y),
max(parts[46], parts[47], key=lambda x: x.y),
parts[45],
]
#目の端と(36,39)、上瞼からより上にある方を(37 or 38)
#下瞼からより下にある方を(40 or 41)
#四角で囲った時に、目玉全体が入るように座標を取得する
#右目は左目の座標に+6にして取得する↑
org_x = eyes[0].x
org_y = eyes[1].y
#↑原点を目玉に合わせるために、座標を保存する
hrg_x = heads[0].x
if is_close(org_y, eyes[2].y):#目が閉じていたら瞳を検知できないので
return None
#目が閉じているかを判明する
#↓目玉をトリミングして二値化する↓
eye = img[org_y:eyes[2].y, org_x:eyes[-1].x]#トリミング
_, eye = cv2.threshold(cv2.cvtColor(eye, cv2.COLOR_RGB2GRAY), 30, 255, cv2.THRESH_BINARY_INV)
#二値化
center = get_center(eye)#重心を求める
if center:
return center[0] + org_x, center[1] + org_y
return center
def p(img, parts, eye):#引数は、顔画像と顔器官座標、瞳の座標(左、右)
if eye[0]:#瞳座標を緑の点、顔器官座標を青の点で顔画像に重ねて表示
cv2.circle(img, eye[0], 3, (255, 255, 0), -1)
if eye[1]:
cv2.circle(img, eye[1], 3, (255, 255, 0), -1)
for i in parts:
cv2.circle(img, (i.x, i.y), 3, (255, 0, 0), -1)
cv2.imshow("face", img)
#カメラから動画を取得する
cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
#ここに処理を追加していく---
#例えば、鼻のてっぺん座標を取得したい場合
#parts[33].x
#parts[33].y
#↓顔器官座標を取得
dets = detector(frame[:, :, ::-1])
if len(dets) > 0:
parts = predictor(frame, dets[0]).parts()
left_eye = eye_point(frame, parts)#瞳の顔器官に加えて、瞳も表示される
right_eye = eye_point(frame, parts, False)
p(frame, parts, (left_eye,right_eye))#frame*0のところで*0しなければ
#元の顔画像に重ねて点が表示される
#
if cv2.waitKey(1) == 27:
break
#キャプチャをリリースして、ウィンドウをすべて閉じる
# ここまで----
cap.release()
cv2.destroyAllWindows()
#カメラがない場合は、
#frame = cv2.imread("画像のパス")
#cv2.imshow("me", frame) # 確認用
# ここに処理を追加していく ---
# ここまで ----
#cv2.waitKey(0)
#cv2.destroyAllWindows()
こちらのプログラムにさらに書き加えて頭部のモデルを作りたいと思っています。抽出できればいいのですが、以下の画像にある68ランドマークの1、17に繋がる楕円モデル(頭部モデル)を作れればと思っています。
いろいろ調べたのですが、上手くいかなかったので分かる方いましたら教えていただきたいです。よろしくお願い致します。