Unity上で顔認識 ~Webカメラでモーションキャプチャーへの道~ Part1

どうも!洋梨です!🍐

先日、Facerig + Live2D を用いてWebカメラで2Dモデルを動かしているのを見て、Webカメラで3Dモデルを動かすことはできないのか?と考えました。

※(Live2Dについてはこちら Facerigはこちら をご覧ください。)

ここで私は

1.顔を画像から探す
2.顔から下を人物と特定。
3.人物から体のポーズを取得。
4.ポーズデーターを3Dモデルに反映させる。

これが出来れば動かせるようになるのではないか?と自分は考えたのでこれから順々に作っていこうと思います。Unityはまだ使い始めて半年のためWebカメラは使用したことが無かったので、そこから順に書いていきたいと思います。

画像から顔を取得する

まずは画像から顔を取得できるようにしたいと思います。
これには定番OpenCV」を使うことも考えたのですがOpenCVを使ったほうが結果よさそうです。

・せっかくなら最初から作ってみたい
・Phython向き
・そもそも高度正確な抽出は必要なく、顔の位置がわかる程度でいい
・背景は部屋で撮るため単色
・余計な処理を除いて軽くしたい

という点から作ることにしました。

抽出方法についてなのですが手軽に軽く済ましたかったので今回は背景差分法RGB(HSV)を用いて肌を検出、及び顔を抽出してみました。

UnityでWebカメラから画像を取得

この部分は他にかなり参考になるサイトがあると思うので簡単な説明です。

詳しい説明が欲しい方が多いようなのでWebカメラから画像を取得する方法はこちらをご覧ください~


ソースコード

float camNumber = 0; //使用するカメラ番号
List<string> cameraList = new Lsit<string>(); // 接続中のカメラリスト
WebCamTexture webCamTex;

WebCamDevice[] camDevices = WebCamTexture.devices; //接続中カメラ取得
foreach (WebCamDevice w in camDevice) cameraList.Add(w.name);
webCamTex = new WebCamTexture(cameraList[camNumber], 320, 240, 30);
webCamTex.Play(); // キャプチャを開始

WebCamTexture を使ってカメラから画像を取得します。
また、今回は画質はを求めていないため、320×240を指定しています。


※ちなみに今回私が買った・使っているWebカメラはこちら


LOGICOOL ウェブカム HD画質 120万画素 C270

費用を抑えたかったのでかなり安いやつを選びました。私はモーションキャプチャの研究用に買ったので大丈夫ですが、「配信したい」など画質を求めて買うのはやめましょう

しかしデスクトップPCなどWebカメラがついてない方は今後の為にも購入をお勧めします。(値段もそこまでしないので)


WebCamTexture を RawImageのTextureにセットし、実行します。

Webカメラのデーターを取得できるとこのように表示することができます。

※IndexOutOfRangeExceptionなどエラーが発生する場合、カメラ番号に値するカメラが存在していない可能性があります。Windows 10に標準搭載されているカメラアプリなどで表示できるか確認してみてください。

画像から肌を検出

Color32 mask; //肌の基本色 
        Color32[] base32; //取得した画像データを入れる用 
        byte[,] pixelData;//抽出用 
        webCamTex.GetPixels32(base32); //Webカメラの画像データを代入 
        for (int x = 0; x < width; x++){
            for (int y = 0; y < height; y++){
                Color32 c32 = base32[x + y * webCamTex.width];
                if (Near_Color_Chack_RGB(c32)) pixelData[x, y] = 1; //そのピクセルが肌に近ければ1をマーク   
                else pixelData[x, y] = 0;
            }
        }

上のソースコードをUpdate()などに書きフレーム毎呼び出します。

Near_Color_Chack_RGB() は 入力された色と一定範囲内の色だった場合、Trueを返す自作関数です。

Near_Color_Chack_RGB(Color 32)

bool Near_Color_Chack_RGB(Color32 c){ // RGB判定
 if (Byte_Distance(c.r, mask.r) < rgb_aria){
  if (Byte_Distance(c.g, mask.g) < rgb_aria){
   if (Byte_Distance(c.b, mask.b) < rgb_aria) return true; 
  }
 }
 return false;
}

これで肌の色に近いピクセルを抽出することが出来ました。

また、ここにさらにHSVによる判定も加えて「PixelData[,]」からノイズを減らし、精度を多少上げます。


この段階で「pixelData[,]」をマスクとしてレイヤーを重ねて表示すれば肌の色だけ取得できているのが確認できると思います。

※なお、今回はUnityでよく使われる「Color」クラスでなく「Color32」を使っています。理由はこれにより速度を大幅に上昇させることができるからです。

これで今回は顔のマスクデーターを作成するところまで出来ました。
では次回はこのマスクデーターをさらに管理しやすくしていきたいと思います!

次へ「モーションキャプチャへの道 Part 2」


ここまで読んでくれてありがとうございます!

ホームページでは他にも

・様々な記事や作った作品および過程
・ソースコード、素材ファイル
・あらゆる”モノ”の作り方

などなど随時、記事や作品を新規公開・更新していますので是非見ていってくださいね!見ていただけると本当に嬉しいです!

また、「このアプリの作り方を知りたい。この部分どうなってるの?」「身の回りのこんなもの作れるの?」などなどご意見何でも受け付けていますので是非連絡くださいね!