2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

顔の向きを求めてみた

Last updated at Posted at 2022-12-24

はじめに

この記事は、SLP KBIT Advent Calendar 2022の23日目の記事になります。
この記事ではカメラから取得した画像をもとに、dlibやnumpy、openCVを用いて顔の向きを求めます。顔の向きは、線で示しており角度を求めるには追加で計算を行う必要があります。

開発環境

openCV 4.6.0.66
numpy 1.23.1
dlib 19.24.0

解説

コードの要点のみ解説します。

ここでは、カメラの姿勢を計算しています。solvePnPではカメラの位置、姿勢を3次元の座標と2次元の座標の対応から求めれます。

success, vector_rotation, vector_translation = cv2.solvePnP(figure_points_3D, image_points_2D, matrix_camera, distortion_coeffs, flags=0)

projectPointsでは、3次元の点をカメラ視点に投影します。(3次元の点を2次元の点に変換)

nose_end_point2D, jacobian = cv2.projectPoints(np.array([(0.0, 0.0, 1000.0)]), vector_rotation, vector_translation, matrix_camera, distortion_coeffs)

これは、顔のモデル座標を登録しています。projectPointsで投影する点は、3次元で鼻の頂点の正面になります。従って、顔の向きに伴ってnose_end_point2Dに入る点は移動します。

figure_points_3D = np.array([
                        (0.0, 0.0, 0.0),             # Nose tip
                        (0.0, -330.0, -65.0),        # Chin
                        (-225.0, 170.0, -135.0),     # Left eye left corner
                        (225.0, 170.0, -135.0),      # Right eye right corner
                        (-150.0, -150.0, -125.0),    # Left Mouth corner
                        (150.0, -150.0, -125.0)      # Right mouth corner
                    ])

point1は鼻の頂点、point2はnose_end_point2Dに入る点です。その2点を結んだ線を描画します。この線が顔の向きを示すものとなります。

cv2.line(img, point1, point2, (255,255,255), 2)

コード

import cv2
import numpy as np
import dlib
from imutils import face_utils

WIDTH = 1920
HEIGHT = 1080
face_detector = dlib.get_frontal_face_detector()
predictor_path = 'shape_predictor_68_face_landmarks.dat'
face_predictor = dlib.shape_predictor(predictor_path)
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)

while True:
    ret,img = cap.read()
    img_gry = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    size = img.shape
    faces = face_detector(img_gry,1)
    for face in faces:
        landmark = face_predictor(img_gry,face)
        landmark = face_utils.shape_to_np(landmark)

    image_points_2D = np.array([
                        (landmark[30]),  # Nose tip
                        (landmark[8]),  # Chin
                        (landmark[45]),  # Left eye corner
                        (landmark[36]),  # Right eye corner
                        (landmark[54]),  # Left mouth
                        (landmark[48])   # Right mouth
                      ], dtype="double")

    figure_points_3D = np.array([
                            (0.0, 0.0, 0.0),             # Nose tip
                            (0.0, -330.0, -65.0),        # Chin
                            (-225.0, 170.0, -135.0),     # Left eye left corner
                            (225.0, 170.0, -135.0),      # Right eye right corne
                            (-150.0, -150.0, -125.0),    # Left Mouth corner
                            (150.0, -150.0, -125.0)      # Right mouth corner
                        ])

    distortion_coeffs = np.zeros((4,1))
    focal_length = size[1]
    center = (size[1]/2, size[0]/2)
    matrix_camera = np.array(
                         [[focal_length, 0, center[0]],
                         [0, focal_length, center[1]],
                         [0, 0, 1]], dtype = "double"
                         )
    success, vector_rotation, vector_translation = cv2.solvePnP(figure_points_3D, image_points_2D, matrix_camera, distortion_coeffs, flags=0)
    nose_end_point2D, jacobian = cv2.projectPoints(np.array([(0.0, 0.0, 1000.0)]), vector_rotation, vector_translation, matrix_camera, distortion_coeffs)
    for p in image_points_2D:
        cv2.circle(img, (int(p[0]), int(p[1])), 3, (0,0,255), -1)
    point1 = ( int(image_points_2D[0][0]), int(image_points_2D[0][1]))

    point2 = ( int(nose_end_point2D[0][0][0]), int(nose_end_point2D[0][0][1]))

    cv2.line(img, point1, point2, (255,255,255), 2)

    cv2.imshow("Final",img)

    if cv2.waitKey(1) == 27:
        break
    
cap.release()
cv2.destroyAllWindows()

おわりに

今回は顔の向きを求めるコードの解説を行いました。計算内容を調べるのが大変でしたので、自分が参考にしたサイトを置いておきます。興味がある人はのぞいてみてください。

参考

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?