英特尔的RealSense深度摄像头可以检测到手的骨骼信息,给出各个关节的相对位置。这里我自己定义了一些简单的动态手势,例如上下左右移动和左右旋转等等。如果有需要,程序可以继续进行扩展,加入更多的手势。
注意,程序基于Unity3D平台实现,在运行中需要读取指尖和手掌中心的模型名称。如果官方的SDK对此有修改,需要在代码中更新。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class test : MonoBehaviour {
// Use this for initialization
List<GameObject> objs = new List<GameObject>();
GameObject handCenter;
Vector3[] Tips;
// public GUIText gesStateText;
public GUIText gesCommandText;
enum GestureCommand {
moveLeft ,
moveRight ,
moveForward ,
moveBack ,
rotationRight,
rotationLeft,
none
}
enum HandMotion {
up,
down,
left,
right,
forward,
back,
empty
}
enum GestureState {
palm,
pinch,
empty
}
GestureState currentGestureState= GestureState.empty;
HandMotion currentHandMotion = HandMotion.empty;
GestureCommand currentGestureCommand = GestureCommand.none;
float threshold = 120;
int frameCountX = 0;
int frameCountY = 0;
int frameCountZ = 0;
int frameCountStay = 0;
int countThreshold = 5;
public float stayFlag = 0.1f;
Vector3 origin = Vector3.zero;
Vector3 destination = Vector3.zero;
void Start () {
Tips = new Vector3[5];
// gesStateText.text = currentGestureState.ToString();
// gesStateText.fontSize = 40;
gesCommandText.text = currentGestureCommand.ToString();
gesCommandText.fontSize = 50;
}
// Update is called once per frame
void Update () {
if (handCenter = GameObject.Find("PalmCenter(Clone)"))
{
objs.Clear();
foreach (GameObject go in GameObject.FindObjectsOfType(typeof(GameObject)))
{
if (go.name == "Tip(Clone)")
{
objs.Add(go);
}
}
for (int i = 0; i < objs.Count && i < Tips.Length; i++)
{
Tips[i] = objs[i].transform.position;
}
//Debug.Log("NUM:"+ objs.Count);
//float temp = MeanDistanceOfFive(Tips);
float temp2 = SumOfFiveTipsToOthers(Tips);
if (temp2 < threshold)
{
currentGestureState = GestureState.pinch;
}
else
{
currentGestureState = GestureState.palm;
}
//================
destination = handCenter.transform.position;
int direction = DetectMotionDirection(origin, destination);
//when accumulate to the threshold, change the HandMotin state
switch (direction)
{
case 0:
frameCountStay++;
if (frameCountStay > countThreshold)
{
ClearFrameCount();
currentHandMotion = HandMotion.empty ;
}
Debug.Log("empty");
break;
case 1:
frameCountX++;
if (frameCountX > countThreshold)
{
ClearFrameCount();
currentHandMotion = destination.x - origin.x > 0 ? HandMotion.left : HandMotion.right;
}
Debug.Log("X");
break;
case 2:
frameCountY++;
if (frameCountY > countThreshold)
{
ClearFrameCount();
currentHandMotion = destination.y - origin.y > 0 ? HandMotion.up : HandMotion.down;
}
Debug.Log("Y");
break;
case 3:
frameCountZ++;
if (frameCountZ > countThreshold)
{
ClearFrameCount();
currentHandMotion = destination.z - origin.z > 0 ? HandMotion.forward : HandMotion.back;
}
Debug.Log("Z");
break;
}
}
else
{
currentGestureState = GestureState.empty;
}
//according to the current HandMotion state, compute the speed in the direction
Vector3 speed = destination - origin;
GetFinalCommand(currentGestureState, currentHandMotion, ref currentGestureCommand);
origin = destination; //prepare data for the next cycle
//gesStateText.text = currentGestureState.ToString();
gesCommandText.text = currentGestureCommand.ToString();
}
void OnGUI()
{
}
void GetFinalCommand(GestureState gs, HandMotion hm, ref GestureCommand gc)
{
switch (gs)
{
case GestureState.palm:
switch (hm)
{
case HandMotion.left:
gc = GestureCommand.moveLeft;
break;
case HandMotion.right:
gc = GestureCommand.moveRight;
break;
case HandMotion.forward:
gc = GestureCommand.moveForward;
break;
case HandMotion.back:
gc = GestureCommand.moveBack;
break;
case HandMotion.up:
gc = GestureCommand.none;
break;
case HandMotion.down:
gc = GestureCommand.none;
break;
case HandMotion.empty:
gc = GestureCommand.none;
break;
}
break;
case GestureState.pinch:
switch (hm)
{
case HandMotion.left:
gc = GestureCommand.rotationLeft;
break;
case HandMotion.right:
gc = GestureCommand.rotationRight;
break;
case HandMotion.forward:
gc = GestureCommand.none;
break;
case HandMotion.back:
gc = GestureCommand.none;
break;
case HandMotion.up:
gc = GestureCommand.none;
break;
case HandMotion.down:
gc = GestureCommand.none;
break;
case HandMotion.empty:
gc = GestureCommand.none;
break;
}
break;
case GestureState.empty:
gc = GestureCommand.none;
break;
}
}
void ClearFrameCount()
{
frameCountX = 0;
frameCountY = 0;
frameCountZ = 0;
frameCountStay = 0;
}
//此处的函数不必要,可用Vector3.Distance()代替
float TipsDistance(Vector3 a, Vector3 b)
{
float dis = 0;
dis = Mathf.Sqrt(Mathf.Pow(a.x-b.x,2)+ Mathf.Pow(a.y - b.y, 2)+Mathf.Pow(a.z - b.z, 2));
return dis;
}
float MeanDistanceOfFive( Vector3[] v)
{
float dis =0;
Vector3 v_sum = Vector3.zero;
for (int i = 0; i < v.Length; i++)
{
v_sum = v_sum + v[i];
}
Vector3 v_mean = v_sum / v.Length;
for (int i = 0; i < v.Length; i++)
{
dis = dis + TipsDistance(v_mean,v[i]);
}
return dis/v.Length;
}
float SumOfFiveTipsToOthers(Vector3[] v)
{
float sumDis = 0;
for (int i = 0; i < v.Length; i++)
{
for (int j = 0; j < v.Length; j++)
{
sumDis += TipsDistance(v[i],v[j]);
}
}
return sumDis;
}
int DetectMotionDirection(Vector3 origin, Vector3 destination)
{
Vector3 s = destination - origin;
Vector3 t = new Vector3(Mathf.Abs(s.x), Mathf.Abs(s.y), Mathf.Abs(s.z));
if (t.x + t.y + t.z < stayFlag) return 0; // not more than the stayflag, we consider it in a static state
if (t.x >= t.y && t.x >= t.z)
{
return 1;
}
else if (t.y >= t.x && t.y >= t.z)
{
return 2;
}
else if (t.z >= t.x && t.z >= t.y)
{
return 3;
}
else
{
return 0;
}
}
}