欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

人体姿态识别-左肩和左肘的定位识别

程序员文章站 2022-07-11 23:46:40
...

人体姿态识别-左肩和左肘的定位识别

        对于传统的人体动作识别方法来说,分为三类:基于人体模型的方法;基于全局特征的方法,基于特征的方法,人体动作丰富多样,不同的动作具有不同的含义。这里我选择基于特征的方法来识别人体某个部位的动作,即用一组特征向量来标识这个动作,一旦条件满足这个特征向量,就判定该动作被识别。

        基于kinect硬件设备来说,其功能强大无比,其中骨骼帧的三维立体坐标都被进入在一帧的数据里面,三维坐标的方向如下:

空间坐标原点为深度摄像头的位置,以kinect深度摄像头的右侧为X轴正向,以kinect深度摄像头的上侧为y轴的正向,以kinect深度摄像头的前侧为Z轴的正向:如图一

人体姿态识别-左肩和左肘的定位识别

图一

        明白坐标系的位置及方向之后,就可以对人体25个关节点进行处理了,在这之前,我测试了下kinect的25个关节点的坐标,并将其保存在txt文档中,其数据如下:其中每一个数据都是人体关节点到深度摄像头的距离,每一行代表一个关节点的三维坐标,次序依次为:"头部", "脖子", "左肩", "左肘", "左腕", "左手", "左食指指尖", "右大拇指", "右肩", "右肘", "右腕", "右手", "右食指指尖", "右大拇指","肩部脊椎" ,"脊椎中心","脊椎尾部","左髋骨","左膝盖","左脚踝","左脚","右髋骨","右膝盖","右脚踝","右脚"

-0.123703  0.5555432  1.213775
-0.1285025  0.410982  1.259488
-0.2830234  0.2892215  1.260633
-0.314623  0.04511343  1.274519
-0.3590705  -0.1259917  1.18842
-0.3480108  -0.1867286  1.165522
-0.3350937  -0.2386908  1.130128
-0.2930106  -0.1850984  1.147
0.05812442  0.2823356  1.26813
0.1852455  0.1324079  1.256844
0.2263555  0.3260264  1.083552
0.2250388  0.3909267  1.044129
0.2356904  0.4620195  1.008516
0.1589416  0.3914632  1.121333
-0.1286564  0.335746  1.264128
-0.1283216  0.09956012  1.268763
-0.127224  -0.2318419  1.261028
-0.1983761  -0.2175556  1.219764
-0.1852868  -0.5608031  0.9363578
-0.1887519  -0.6600102  1.225695
-0.2521433  -0.6028567  1.14585
-0.0489066  -0.2327119  1.228814
-0.1435885  -0.5741755  0.9622245
-0.01206919  -0.6697052  1.225347

-0.03818841  -0.5850864  1.135253

        这里我只列出两个关节点之间的距离关系,是相对于深度摄像头的坐标系来说的,关于空间中的两点的距离关系我们可以用欧氏距离公式来求:人体姿态识别-左肩和左肘的定位识别,两点之间的夹角,我们可以用余弦公式来求:

人体姿态识别-左肩和左肘的定位识别

        接下来我们开始定义姿势了:pa= {p1,p2,,人体姿态识别-左肩和左肘的定位识别人体姿态识别-左肩和左肘的定位识别}  ,特征向量,以关节点p1为中心点,关节点p2与X轴上的角度为人体姿态识别-左肩和左肘的定位识别;人体姿态识别-左肩和左肘的定位识别为设定的角度阈值。即人体姿态识别-左肩和左肘的定位识别为理想角度与实际角度的误差,只要不超过这一阈值,都被判定为这一姿势。为此我们定义这三种姿势:

开始姿势: = {180,180,-90,-90,15}

举起左手: = {90,180,-90,-90,15}

左手向右: = {0,180,-90,-90,15}

人体姿态识别-左肩和左肘的定位识别人体姿态识别-左肩和左肘的定位识别人体姿态识别-左肩和左肘的定位识别

        测试的数据表格如下:

动作:阈值15

人数

测试人数

正确识别次数

识别率%

开始姿势

5

10

50

100

左手举起

5

10

50

100

左手向右

5

10

50

100

1

动作:阈值10

人数

测试人数

正确识别次数

识别率%

开始姿势

5

10

45

90

左手举起

5

10

46

92

左手向右

5

10

39

78

2

动作:阈值20

人数

测试人数

正确识别次数

识别率%

开始姿势

5

10

48

96

左手举起

5

10

46

92

左手向右

5

10

44

88

3

代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Kinect;
using System.IO;

namespace 人体姿态识别
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        #region Member Variables
        //体感器设备
        private KinectSensor _KinectDevice;

        //骨骼图像
        //骨骼帧读取变量
        private MultiSourceFrameReader multireader = null;
        private FrameDescription colorframedecrition = null;
        private WriteableBitmap colorbitmap = null;
        private Byte[] colordata;


        //玩家数据
        private Body[] _Bodies;

        private static int k = 0;

        //画骨架图颜色uint[]
        private Brush[] ColorBody = new Brush[]{
            Brushes.Red,Brushes.Green,Brushes.Pink,Brushes.Blue,Brushes.Black,Brushes.Orange//用不同颜色的刷子出人的骨骼
        };

        //关节点两两相连
        private JointType[] _JointType = new JointType[]{
            JointType.Head,
            JointType.Neck,

            JointType.ShoulderLeft,JointType.ElbowLeft,JointType.WristLeft,JointType.HandLeft,JointType.HandTipLeft,JointType.ThumbLeft,

            JointType.ShoulderRight,JointType.ElbowRight,JointType.WristRight,JointType.HandRight,JointType.HandTipRight,JointType.ThumbRight,

            JointType.SpineShoulder,JointType.SpineMid,JointType.SpineBase,

            JointType.HipLeft,JointType.KneeLeft,JointType.AnkleLeft,JointType.FootLeft,

            JointType.HipRight,JointType.KneeRight,JointType.AnkleRight,JointType.FootRight
            
        };
        static string[] jointname = { "头部", "脖子", "左肩", "左肘", "左腕", "左手", "左食指指尖", "右大拇指", "右肩", "右肘", "右腕", "右手", "右食指指尖", "右大拇指","肩部脊椎" ,"脊椎中心","脊椎尾部",
                                 "左髋骨","左膝盖","左脚踝","左脚","右髋骨","右膝盖","右脚踝","右脚"};
        public List<ulong> trackedIds = new List<ulong>();

        #endregion Member Variables

        #region Constructor
        public MainWindow()
        {
            InitializeComponent();
            //获取默认的连接的体感器
            this._KinectDevice = KinectSensor.GetDefault();

            //多种帧读取初始化
            this.multireader = this._KinectDevice.OpenMultiSourceFrameReader(FrameSourceTypes.Color|FrameSourceTypes.Body);

            //触发骨骼帧处理事件
            this.multireader.MultiSourceFrameArrived += multireader_MultiSourceFrameArrived;

            this.colorframedecrition = this._KinectDevice.ColorFrameSource.CreateFrameDescription(ColorImageFormat.Bgra);

            this.colordata = new Byte[this.colorframedecrition.LengthInPixels*4];

            this.colorbitmap = new WriteableBitmap(this.colorframedecrition.Width,this.colorframedecrition.Height,96.0,96.0,PixelFormats.Bgra32,null);

            colorfr.Source = this.colorbitmap;
            //玩家骨骼数组长度为6
            this._Bodies = new Body[6];
            //启动体感器

            this._KinectDevice.Open();
        }

        #endregion Construtor

        #region Methods

        //骨骼帧处理事件
        void multireader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
        {
            Joint leftshoulder, leftelbow, leftwrist, rightshoulder, rightelbow, rightwrist;
            float rad1, rad2, rad3, rad4;
            float temp1,temp2,temp3,temp4;
           
            MultiSourceFrame msf = e.FrameReference.AcquireFrame();
            //获取一帧骨骼
            if (msf != null)
            {
                using (BodyFrame bodyFrame = msf.BodyFrameReference.AcquireFrame())
                {
                    using (ColorFrame colorframe = msf.ColorFrameReference.AcquireFrame())
                    {

                        if (bodyFrame != null&&colorframe!=null)
                        {
                           
                            //玩家骨骼保存到数组里面
                            bodyFrame.GetAndRefreshBodyData(this._Bodies);

                            foreach(Body body in _Bodies){
                                if(body.IsTracked){

                                    Joint headpoint = body.Joints[JointType.Head];
                                    Point headpoint1 = getjointpointscreen(headpoint);
                                    Ellipse headcircle = new Ellipse() { Width = 150, Height = 150, Fill = new SolidColorBrush(Color.FromArgb(255, 255, 0, 0)) };
                                    Layout2.Children.Add(headcircle);
                                    Canvas.SetLeft(headcircle, headpoint1.X - 75);
                                    Canvas.SetTop(headcircle, headpoint1.Y - 75);
                                    
                                    leftshoulder = body.Joints[JointType.ShoulderLeft];
                                    leftelbow = body.Joints[JointType.ElbowLeft];
                                    leftwrist = body.Joints[JointType.WristLeft];
                                    rightshoulder = body.Joints[JointType.ShoulderRight];
                                    rightelbow = body.Joints[JointType.ElbowRight];
                                    rightwrist = body.Joints[JointType.WristRight];

                                    temp1 = ((leftshoulder.Position.Z - leftelbow.Position.Z) * (leftshoulder.Position.Z - leftelbow.Position.Z) + (leftshoulder.Position.Y - leftelbow.Position.Y) * (leftshoulder.Position.Y - leftelbow.Position.Y) + (leftshoulder.Position.X - leftelbow.Position.X) * (leftshoulder.Position.X - leftelbow.Position.X) + (leftshoulder.Position.Z - leftelbow.Position.Z) * (leftshoulder.Position.Z - leftelbow.Position.Z) + (leftshoulder.Position.X - leftelbow.Position.X) * (leftshoulder.Position.X - leftelbow.Position.X) - (leftshoulder.Position.Y - leftelbow.Position.Y) * (leftshoulder.Position.Y - leftelbow.Position.Y)) / (float)(2 * System.Math.Sqrt((leftshoulder.Position.Z - leftelbow.Position.Z) * (leftshoulder.Position.Z - leftelbow.Position.Z) + (leftshoulder.Position.Y - leftelbow.Position.Y) * (leftshoulder.Position.Y - leftelbow.Position.Y) + (leftshoulder.Position.X - leftelbow.Position.X) * (leftshoulder.Position.X - leftelbow.Position.X)) * System.Math.Sqrt((leftshoulder.Position.Z - leftelbow.Position.Z) * (leftshoulder.Position.Z - leftelbow.Position.Z) + (leftshoulder.Position.X - leftelbow.Position.X) * (leftshoulder.Position.X - leftelbow.Position.X)));
                                    if (leftelbow.Position.X < leftshoulder.Position.X) temp1 = -temp1;
                                    rad1 = (float)((float)System.Math.Acos((double)temp1)*180/System.Math.PI);
                                    //tex.Text = "角度为"+ rad1.ToString();
                                    if (System.Math.Abs(180 - rad1) < 15)
                                    {
                                        tex.Text ="左肘和左肩水平";
                                        BitmapImage suit = new BitmapImage();
                                        suit.BeginInit();

                                        suit.UriSource = new Uri(@"D:\c++课程\人体姿态识别\水平.jpg", UriKind.RelativeOrAbsolute);
                                        
                                        
                                        
                                        sta.Source = suit;
                                        suit.EndInit();
                                        
                                    }
                                    else if (System.Math.Abs(90 - rad1) < 15) {
                                        tex.Text = "左肘和左肩垂直";
                                        BitmapImage suit = new BitmapImage();
                                        suit.BeginInit();

                                        suit.UriSource = new Uri(@"D:\c++课程\人体姿态识别\垂直.jpg", UriKind.RelativeOrAbsolute);
                                        
                                       
                                        sta.Source = suit;
                                        suit.EndInit();
                                    }
                                    else if(System.Math.Abs(0 - rad1) < 15){
                                        tex.Text = "左肘和左肩向左";
                                        BitmapImage suit = new BitmapImage();
                                        suit.BeginInit();

                                        suit.UriSource = new Uri(@"D:\c++课程\人体姿态识别\向左.jpg", UriKind.RelativeOrAbsolute);
                                       
                                        sta.Source = suit;
                                        suit.EndInit();
                                    }
                                    else {
                                        tex.Text = " ";
                                        //sta = null;
                                    }
                                }
                            }
                            
                           
                             //骨骼网格清空
                            //判断

                            //匹配
                            //foreach (Body body in this._Bodies)
                            //{
                            //    if (body.IsTracked == true)
                            //    {
                            //        trackedIds.Add(body.TrackingId);
                            //        tt.Text = body.TrackingId.ToString();
                            //    }
                            //}

                            //画骨架
                            colorframe.CopyConvertedFrameDataToArray(this.colordata, ColorImageFormat.Bgra);

                            this.colorbitmap.WritePixels(new Int32Rect(0, 0, this.colorbitmap.PixelWidth, this.colorbitmap.PixelHeight), this.colordata, this.colorbitmap.PixelWidth * 4, 0);
                            Layout.Children.Clear();
                            DrawBodies();
                        }
                    }
                }
            }
        }

        private Point getjointpointscreen(Joint headpoint)
        {
            ColorSpacePoint colorpoint = this._KinectDevice.CoordinateMapper.MapCameraPointToColorSpace(headpoint.Position);
            colorpoint.X = (int)((colorpoint.X * Layout2.Width) / 1920);
            colorpoint.Y = (int)((colorpoint.Y * Layout2.Height) / 1080);
            return new Point(colorpoint.X, colorpoint.Y);
        }

        

        //画骨架,六个人
        private void DrawBodies()
        {
            //遍历6个玩家
            for (int i = 0; i < this._Bodies.Length; i++)
            {
                //如果跟踪到玩家
                if (this._Bodies[i].IsTracked == true)
                {
                    //根据编号选择一种骨架的颜色
                    Brush color = ColorBody[i % 6];

                    Body oneBody = this._Bodies[i]; 
                    //通过循环将关节点两两连接,
                    for (int j = 0; j < this._JointType.Length; j ++)
                    {
                       

                        //起点的屏幕坐标和终点的屏幕坐标
                        Point StartP = GetJointPointScreen(oneBody.Joints[this._JointType[j]]);
                        

                        Ellipse sp = new Ellipse();
                        

                        sp.Width = 15;
                        sp.Height = 15;
                        sp.HorizontalAlignment = HorizontalAlignment.Left;
                        sp.VerticalAlignment = VerticalAlignment.Top;
                        sp.Fill = Brushes.Red;
                       

                        sp.Margin = new Thickness(StartP.X - sp.Width / 2, StartP.Y - sp.Height / 2, 0, 0);
                       

                        //设置起点、终点的坐标
                        

                        //把起点、终点及连接添加到网格中。
                        Layout.Children.Add(sp);
                       
                    }
                    //writedate(_Bodies[i]);//将某一帧的数据写入文件
                }
            }
        }

        //骨骼坐标转化为彩色图像坐标,再转化为屏幕坐标
        private Point GetJointPointScreen(Joint oneJoint)
        {
            //骨骼坐标转化为彩色图像坐标
            ColorSpacePoint colorPoint = this._KinectDevice.CoordinateMapper.MapCameraPointToColorSpace(oneJoint.Position);

            //彩色图像坐标转化为屏幕坐标
            colorPoint.X = (int)((colorPoint.X * Layout.Width) / 1920);
            colorPoint.Y = (int)((colorPoint.Y * Layout.Height) / 1080);

            //返回Point类型变量
            return new Point(colorPoint.X, colorPoint.Y);
        }

        //窗口关闭处理事件
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {

            //骨骼帧关闭处理
            if (this.multireader != null)
            {
                this.multireader.Dispose();
                this.multireader = null;
            }
            //体感器关闭处理
            if (this._KinectDevice != null)
            {
                this._KinectDevice.Close();
                this._KinectDevice = null;
            }
        }
        #endregion Methods
    }
}

运行效果:

人体姿态识别-左肩和左肘的定位识别

人体姿态识别-左肩和左肘的定位识别

 人体姿态识别-左肩和左肘的定位识别

        不过,该实验的骨骼点抖动太大,不知道各位有什么好的方法来消除抖动没有。