【3D技术宅公社】XR数字艺术论坛  XR技术讨论 XR互动电影 定格动画

 找回密码
 立即注册

QQ登录

只需一步,快速开始

调查问卷
论坛即将给大家带来全新的技术服务,面向三围图形学、游戏、动画的全新服务论坛升级为UTF8版本后,中文用户名和用户密码中有中文的都无法登陆,请发邮件到324007255(at)QQ.com联系手动修改密码

3D技术论坛将以计算机图形学为核心,面向教育 推出国内的三维教育引擎该项目在持续研发当中,感谢大家的关注。

查看: 4486|回复: 2

[体感与外设] Kinect v2.0 for Unity---骨骼帧

[复制链接]
发表于 2016-2-1 15:17:36 | 显示全部楼层 |阅读模式
本帖最后由 ycyipman 于 2016-2-16 15:03 编辑

一、使用方式:
彩色帧获取需要两个脚本,BodySourceManager和BodySourceView,使用时新建一个空物体,并为其添加BodySourceManager,在新建一个plane或者cube,添加BodySourceView作为输出展示载体。BodySourceView需要一个公共变量,将BodySourceManager所在的空物体拖入即可。
二、脚本执行过程:
BodySourceManager脚本:
脚本执行逻辑与其他三种帧数据处理逻辑相同,此处只贴代码,不再做解释。
[mw_shl_code=csharp,true]    private KinectSensor _Sensor;                               //传感器对象
    private BodyFrameReader _Reader;                            //骨骼帧阅读器
    private Body[] _Data = null;                                //骨骼数据存储器,用于返回给BodySourceView使用,数组中每一个元素都是Body对象

    public Body[] GetData()
    {
        return _Data;
    }
   

    void Start ()
    {
        _Sensor = KinectSensor.GetDefault();

        if (_Sensor != null)
        {
            //打开骨骼阅读器
            _Reader = _Sensor.BodyFrameSource.OpenReader();
            
            if (!_Sensor.IsOpen)
            {
                _Sensor.Open();
            }
        }   
    }
   
    void Update ()
    {
        if (_Reader != null)
        {
            //得到最近一帧的骨骼帧
            var frame = _Reader.AcquireLatestFrame();
            if (frame != null)
            {
                if (_Data == null)
                {
                    //创建存储器对象,长度为BodyCount返回值
                    _Data = new Body[_Sensor.BodyFrameSource.BodyCount];
                }
               
                //每一帧刷新数据
                frame.GetAndRefreshBodyData(_Data);
               
                frame.Dispose();
                frame = null;
            }
        }   
    }
   
    void OnApplicationQuit()
    {
        if (_Reader != null)
        {
            _Reader.Dispose();
            _Reader = null;
        }
        
        if (_Sensor != null)
        {
            if (_Sensor.IsOpen)
            {
                _Sensor.Close();
            }
            
            _Sensor = null;
        }
}[/mw_shl_code]

BodySourceView脚本定义了整个人体的骨骼网络图,其中键为子节点,值为母节点
[mw_shl_code=csharp,true]    private Dictionary<Kinect.JointType, Kinect.JointType> _BoneMap = new Dictionary<Kinect.JointType, Kinect.JointType>()
    {
        { Kinect.JointType.FootLeft, Kinect.JointType.AnkleLeft },
        { Kinect.JointType.AnkleLeft, Kinect.JointType.KneeLeft },
        { Kinect.JointType.KneeLeft, Kinect.JointType.HipLeft },
        { Kinect.JointType.HipLeft, Kinect.JointType.SpineBase },

        { Kinect.JointType.FootRight, Kinect.JointType.AnkleRight },
        { Kinect.JointType.AnkleRight, Kinect.JointType.KneeRight },
        { Kinect.JointType.KneeRight, Kinect.JointType.HipRight },
        { Kinect.JointType.HipRight, Kinect.JointType.SpineBase },

        { Kinect.JointType.HandTipLeft, Kinect.JointType.HandLeft },
        { Kinect.JointType.ThumbLeft, Kinect.JointType.HandLeft },
        { Kinect.JointType.HandLeft, Kinect.JointType.WristLeft },
        { Kinect.JointType.WristLeft, Kinect.JointType.ElbowLeft },
        { Kinect.JointType.ElbowLeft, Kinect.JointType.ShoulderLeft },
        { Kinect.JointType.ShoulderLeft, Kinect.JointType.SpineShoulder },

        { Kinect.JointType.HandTipRight, Kinect.JointType.HandRight },
        { Kinect.JointType.ThumbRight, Kinect.JointType.HandRight },
        { Kinect.JointType.HandRight, Kinect.JointType.WristRight },
        { Kinect.JointType.WristRight, Kinect.JointType.ElbowRight },
        { Kinect.JointType.ElbowRight, Kinect.JointType.ShoulderRight },
        { Kinect.JointType.ShoulderRight, Kinect.JointType.SpineShoulder },

        { Kinect.JointType.SpineBase, Kinect.JointType.SpineMid },
        { Kinect.JointType.SpineMid, Kinect.JointType.SpineShoulder },
        { Kinect.JointType.SpineShoulder, Kinect.JointType.Neck },
        { Kinect.JointType.Neck, Kinect.JointType.Head },
};[/mw_shl_code]

对于获取到的骨骼数据,由于存在几个人的骨骼被同时获取的可能性,因此还需要定义一个键值对,键代表每一个人的ID,值为该ID对应的创建好的骨骼GameObject(注:ID值并没有从0开始,依次增大1的顺序规律,同一个人在离开Kinect视野范围之后再回来时,ID值也会发生变化)
[mw_shl_code=csharp,true]private Dictionary<ulong, GameObject> _Bodies = new Dictionary<ulong, GameObject>();//根据body对象创建的body GameObject的字典集合,键是追踪的ID值,值为创建的body GameObject
[/mw_shl_code]
Update函数通过调用BodySourceManager脚本中的GetData函数得到骨骼数据,再将GetData数组中的每一个body对象赋予一个ID值,当玩家没有离开Kinect视野范围时,该ID值与body对象一一对应,当玩家离开Kinect视野范围时,根据ID删除对应的body对象
[mw_shl_code=csharp,true]    void Update()
    {
        if (BodySourceManager == null)
        {
            return;
        }

        //获得BodySourceManager组件
        _BodyManager = BodySourceManager.GetComponent<BodySourceManager>();
        if (_BodyManager == null)
        {
            return;
        }

        //得到BodySourceManager的最终数据,为Body对象数组
        Kinect.Body[] data = _BodyManager.GetData();
        if (data == null)
        {
            return;
        }

        //trackedIds存放跟踪到的ID的集合
        List<ulong> trackedIds = new List<ulong>();
        foreach (var body in data)
        {
            if (body == null)
            {
                continue;
            }

            if (body.IsTracked)
            {
                trackedIds.Add(body.TrackingId);
            }
        }

        List<ulong> knownIds = new List<ulong>(_Bodies.Keys);

        // First delete untracked bodies
        //删除未追踪到的body GameObject
        foreach (ulong trackingId in knownIds)
        {
            if (!trackedIds.Contains(trackingId))
            {
                Destroy(_Bodies[trackingId]);
                _Bodies.Remove(trackingId);
            }
        }

        //追踪到的body对象绘制body GameObject
        foreach (var body in data)
        {
            if (body == null)
            {
                continue;
            }

            if (body.IsTracked)
            {
                if (!_Bodies.ContainsKey(body.TrackingId))
                {
                    _Bodies[body.TrackingId] = CreateBodyObject(body.TrackingId);
                }

                RefreshBodyObject(body, _Bodies[body.TrackingId]);
            }
        }
}
[/mw_shl_code]
CreateBodyObject函数用于在追踪到body对象时实时创建相应的body GameObject,包括代表关节的cube和连接cube的线
[mw_shl_code=csharp,true]    /// <summary>
    /// 根据ID创建Body对象
    /// </summary>
    /// <param name="id">追踪到的ID值</param>
    /// <returns>Body对象</returns>
    private GameObject CreateBodyObject(ulong id)
    {
        //依据ID给追踪到的body对象起名
        GameObject body = new GameObject("Body:" + id);

        for (Kinect.JointType jt = Kinect.JointType.SpineBase; jt <= Kinect.JointType.ThumbRight; jt++)
        {
            //每个关节处创建一个cube表示
            GameObject jointObj = GameObject.CreatePrimitive(PrimitiveType.Cube);

            //关节与关节连线
            LineRenderer lr = jointObj.AddComponent<LineRenderer>();
            lr.SetVertexCount(2);
            lr.material = BoneMaterial;
            lr.SetWidth(0.05f, 0.05f);

            //大小设置,名字设置,变为body的子物体
            jointObj.transform.localScale = new Vector3(0.3f, 0.3f, 0.3f);
            jointObj.name = jt.ToString();
            jointObj.transform.parent = body.transform;
        }

        return body;
}
[/mw_shl_code]
GetColorForState函数定义了线条的颜色,正常追踪为绿色,未追踪到为红色,默认黑色
[mw_shl_code=csharp,true]    /// <summary>
    /// 设置线条颜色
    /// </summary>
    /// <param name="state">追踪状态</param>
    /// <returns>颜色</returns>
    private static Color GetColorForState(Kinect.TrackingState state)
    {
        switch (state)
        {
            case Kinect.TrackingState.Tracked:
                return Color.green;

            case Kinect.TrackingState.Inferred:
                return Color.red;

            default:
                return Color.black;
        }
}
[/mw_shl_code]
RefreshBodyObject函数根据读取到的body对象动态绘制body GameObject,并且根据不同的追踪状态绘制不同颜色的线条
[mw_shl_code=csharp,true]    /// <summary>
    /// 更新骨骼游戏物体BodyObject
    /// </summary>
    /// <param name="body">Kinect读取到的body对象</param>
    /// <param name="bodyObject">绘制出的body GameObject</param>
    private void RefreshBodyObject(Kinect.Body body, GameObject bodyObject)
    {
        //遍历每一个关节jt,SpineBase序号为0,ThumbRight序号最大
        for (Kinect.JointType jt = Kinect.JointType.SpineBase; jt <= Kinect.JointType.ThumbRight; jt++)
        {
            //sourceJoint为连接线条的起点关节
            Kinect.Joint sourceJoint = body.Joints[jt];
            //targetJoint为连接线条的末点关节
            Kinect.Joint? targetJoint = null;

            //将每次循环到的关节jt的母节点赋值给targetJoint
            if (_BoneMap.ContainsKey(jt))
            {
                targetJoint = body.Joints[_BoneMap[jt]];
            }

            //根据sourceJoint的坐标*10后给遍历到的关节jt设置坐标
            Transform jointObj = bodyObject.transform.FindChild(jt.ToString());
            jointObj.localPosition = GetVector3FromJoint(sourceJoint);

            //获取线条渲染组件
            LineRenderer lr = jointObj.GetComponent<LineRenderer>();
            if (targetJoint.HasValue)
            {
                //线条起点
                lr.SetPosition(0, jointObj.localPosition);
                //线条末点(与起点一样,坐标*10)
                lr.SetPosition(1, GetVector3FromJoint(targetJoint.Value));
                //根据追踪状态设置颜色
                lr.SetColors(GetColorForState(sourceJoint.TrackingState), GetColorForState(targetJoint.Value.TrackingState));
            }
            else
            {
                lr.enabled = false;
            }
        }
}

    /// <summary>
    /// 坐标*10,用于绘制连接线条
    /// </summary>
    /// <param name="joint">关节对象</param>
    /// <returns>*10后的坐标</returns>
    private static Vector3 GetVector3FromJoint(Kinect.Joint joint)
    {
        return new Vector3(joint.Position.X * 10, joint.Position.Y * 10, joint.Position.Z * 10);
    }[/mw_shl_code]
发表于 2016-3-7 21:46:56 | 显示全部楼层

感谢楼主无私的奉献
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|3D数字艺术论坛 ( 沪ICP备14023054号 )

GMT+8, 2024-11-23 15:40

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表