質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.99%

OpenCVとDlibを用いた顔向き推定

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 2,571

 映像から顔向き角度を算出する方法

現在、私はDlibとOpenCV用いて顔向き角度を算出しています。

映像からDlibを用いて顔を検出し、顔の特徴点と3Dモデル座標を対応させるopenCVのsolvePnPで算出しているのですが正確な顔向き角度を算出することができません。
具体的には右方向や左方向の正負(数値が不適当)は合っているのですが、10、20度のような細かい角度が算出できません。

細かい角度を算出するためには3Dモデルにあたるobject_ptsを正確に設定しなければならないでしょうか?(いい値があるなら教えていただけたら幸いです)

もしくは、他の方法で映像から精度の高い顔向きを算出する方法あるのでしょうか?

 該当のソースコード(一部抜粋)

K = [1.153752569389803966e+03,0.000000000000000000e+00,5.962752942372259213e+02,
    0.000000000000000000e+00,1.211255746170439807e+03,2.448367548714677469e+02,
    0.000000000000000000e+00,0.000000000000000000e+00,1.000000000000000000e+00]
D = [1.174542892072836936e-01, 2.963230480639748032e-01, -6.327104166740647739e-02, -1.601237498454269326e-02, -7.110347802929409822e-01]

cam_matrix = np.array(K).reshape(3, 3).astype(np.float32)
dist_coeffs = np.array(D).reshape(5, 1).astype(np.float32)

object_pts = np.float32([[0.0, 0.0, 0.0],
                         [0.0, -330.0, -65.0],
                         [-225.0, 170.0, -135.0],
                         [225.0, 170.0, -135.0],
                         [-150.0, -150.0, -125.0],
                         [150.0, -150.0, -125.0],
                         ])/4.5

image_pts = np.float32([shape[30], shape[8], shape[36], shape[45], shape[48], shape[54]])

_, rotation_vec, translation_vec = cv2.solvePnP(object_pts, image_pts, cam_matrix, dist_coeffs)

 補足情報

カメラのキャリブレーションは下記のサイトを参考にしました。
カメラキャリブレーション(参考サイト)

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

cv2.solvePnP() で外部パラメータを求めたあと、object_pts を cv2.projectPoints() で画像上の点に変換し、画像に描画したらどう表示されるか確認してみてはどうでしょうか?

  • objectPoints: 画像上に投影する3次元上の点一覧
  • rvec: 回転ベクトル
  • tvec: 並進ベクトル
  • cameraMatrix: カメラ行列
  • distCoeffs: 歪みパラメータ

こちら なども参考にして、 objectPoints を画像上に描画してみてください。


 顔検出のプログラムの意図

  1. カメラキャリブレーションで内部パラメータは推定済みとする。
  2. 顔が写っている画像から顔の特徴点を抽出し、それから画像座標上の点 (image_pts) を作成する。
  3. 画像上の点が対応する世界座標上の点 (object_pts) を、顔の中心が原点となるように作成する。
  4. その対応関係を満たすアフィン変換行列を推定する。
  5. 4 で求めたのは世界座標から見たカメラのポーズである。世界座標の原点が顔の中心になるのだったから、カメラから見て顔がどの方向を向いているかはアフィン変換行列からわかる。

 うまくいかない原因となりうる可能性

  • カメラキャリブレーションが精度よくできていない。
  • 顔検出がうまくいっていない。
  • object_pts と image_pts の対応関係はあっている?object_pts を matplotlib で3Dプロット、image_pts を画像上に描画して確認
  • SolvePnP() の外部パラメータ推定はうまくいっている?こちら を参考に、xyz 軸を描画してみる。object_pts を画像上に描画してみる。

 object_pts、lines を描画してみました。

青の点は object_pts、黒の線は lines を描画したものです。
image_pts をまず検出画像に描画してみて、ちゃんとなっているか見てみるとよいと思います。

イメージ説明

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Line3DCollection

object_pts = np.float32(
    [[0.0, 0.0, 0.0],
     [0.0, -330.0, -65.0],
     [-225.0, 170.0, -135.0],
     [225.0, 170.0, -135.0],
     [-150.0, -150.0, -125.0],
     [150.0, -150.0, -125.0]]) / 4.5
print(object_pts)
# [[  0.         0.         0.      ]
#  [  0.       -73.333336 -14.444445]
#  [-50.        37.77778  -30.      ]
#  [ 50.        37.77778  -30.      ]
#  [-33.333332 -33.333332 -27.777779]
#  [ 33.333332 -33.333332 -27.777779]]

lines = np.float32([[10.0, 10.0, 10.0],
                    [10.0, 10.0, -10.0],
                    [10.0, -10.0, -10.0],
                    [10.0, -10.0, 10.0],
                    [-10.0, 10.0, 10.0],
                    [-10.0, 10.0, -10.0],
                    [-10.0, -10.0, -10.0],
                    [-10.0, -10.0, 10.0]])

line_pairs = [[0, 1], [1, 2], [2, 3], [3, 0],
              [4, 5], [5, 6], [6, 7], [7, 4],
              [0, 4], [1, 5], [2, 6], [3, 7]]

# 描画する。
################################################

# グラフを作成する。
fig = plt.figure(figsize=(6, 6))
axes = fig.add_subplot(111, projection='3d')

# ラベル
axes.set_xlabel("X-axis")
axes.set_ylabel("Y-axis")
axes.set_zlabel("Z-axis")

# 範囲
axes.set_xlim(-80, 80)
axes.set_ylim(-80, 80)
axes.set_zlim(-80, 80)

# object_pts を描画する。
axes.scatter(object_pts[:, 0], object_pts[:, 1], object_pts[:, 2],
             marker='o', color='b', s=30)

# lines を描画する。
for pair in line_pairs:
    p1, p2 = lines[pair]
    axes.add_collection3d(Line3DCollection(
        [[p1, p2]], colors='black', linewidths=2))

# xyz 軸を描画する。
axes.add_collection3d(Line3DCollection(
    [[(0, 0, 0), (50, 0, 0)],
     [(0, 0, 0), (0, 50, 0)],
     [(0, 0, 0), (0, 0, 50)]],
    colors=['r', 'g', 'b'],
    linewidths=2))

plt.grid()
plt.show()

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/09/28 11:27

    2度の丁寧なご回答誠にありがとうございます。
    cv2.solvePnP()算出後、cv2.projectPoints()を行いimagePointsを映像内に表示させたのですが
    xyzの方向が見当外れな方向を向いております。
    この要因として
    ・3Dモデルの対応が合っていない
    ・カメラのキャリブレーションが間違っている
    になるのでしょか?

    キャンセル

  • 2018/09/28 12:09

    回答を追記したので、どこが原因か調べてみてください。
    カメラキャリブレーションもしくは顔検出結果に依存する image_pts が怪しいと思っています。

    キャンセル

  • 2018/10/03 22:55

    返答遅くなり申し訳ごさいません
    丁寧なご回答誠にありがとうございます。
    もう一度一から確認してみます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.99%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る