Kinect for Windows SDK v2.0 开发笔记 (五)骨骼帧与笑面男

转载于:https://blog.csdn.net/dustpg/article/details/38126079

使用SDK: Kinect for Windows SDK v2.0 public preview

这次说说这骨骼帧的获取。嗯,Kinect买来就为这个啊。不然其他数据,买其他产品就行了,Kinect的卖点也是这个。

先看看这次支持的骨骼关节:

[cpp]  view plain  copy
  1. enum _JointType  
  2.     {  
  3.         JointType_SpineBase = 0,  
  4.         JointType_SpineMid  = 1,  
  5.         JointType_Neck  = 2,  
  6.         JointType_Head  = 3,  
  7.         JointType_ShoulderLeft  = 4,  
  8.         JointType_ElbowLeft = 5,  
  9.         JointType_WristLeft = 6,  
  10.         JointType_HandLeft  = 7,  
  11.         JointType_ShoulderRight = 8,  
  12.         JointType_ElbowRight    = 9,  
  13.         JointType_WristRight    = 10,  
  14.         JointType_HandRight = 11,  
  15.         JointType_HipLeft   = 12,  
  16.         JointType_KneeLeft  = 13,  
  17.         JointType_AnkleLeft = 14,  
  18.         JointType_FootLeft  = 15,  
  19.         JointType_HipRight  = 16,  
  20.         JointType_KneeRight = 17,  
  21.         JointType_AnkleRight    = 18,  
  22.         JointType_FootRight = 19,  
  23.         JointType_SpineShoulder = 20,  
  24.         JointType_HandTipLeft   = 21,  
  25.         JointType_ThumbLeft = 22,  
  26.         JointType_HandTipRight  = 23,  
  27.         JointType_ThumbRight    = 24,  
  28.         JointType_Count = ( JointType_ThumbRight + 1 )   
  29.     } ;  

支持这25个关节点,不排除会增加的可能,毕竟近景可以分辨十指。

每个关节的状态用这个结构体描述:

[cpp]  view plain  copy
  1. typedef struct _Joint  
  2.     {  
  3.     JointType JointType;  
  4.     CameraSpacePoint Position;  
  5.     TrackingState TrackingState;  
  6.     }   Joint;  

JointType就是之前的关节编号,Position是Kinect的相机空间坐标,是三维的。TrackingState是目前关节的追踪状态,

有: 未追踪(0),位置是推测的(1),位置是追踪的(2)


值得说明的是这次C++的SDK也提供了判断手的状态:

[cpp]  view plain  copy
  1. enum _HandState  
  2.     {  
  3.         HandState_Unknown   = 0,  
  4.         HandState_NotTracked    = 1,  
  5.         HandState_Open  = 2,  
  6.         HandState_Closed    = 3,  
  7.         HandState_Lasso = 4  
  8.     } ;  

有:未知(0),未追踪(1),摊开(2),握拳(3)以及Lasso(4),Lasso不知道怎么翻译,大概就是处于摊开与握拳之间的状态,

比如:或者甚至这样都能称为Lasso。


使用方法和之前的差不多,说说不同的:

[cpp]  view plain  copy
  1. IBody* ppBodies[BODY_COUNT] = {0};  
  2.   
  3. if (SUCCEEDED(hr))  
  4. {  
  5.     hr = pBodyFrame->GetAndRefreshBodyData(BODY_COUNT, ppBodies);  
  6. }  

这样获取6个IBody接口,用完了记得释放,一个循环释放完


每个接口使用类似下面的代码获取数据:

[cpp]  view plain  copy
  1. for (int i = 0; i < nBodyCount; ++i)  
  2. {  
  3.     IBody* pBody = ppBodies[i];  
  4.     if (pBody)  
  5.     {  
  6.         BOOLEAN bTracked = false;  
  7.         hr = pBody->get_IsTracked(&bTracked);  
  8.   
  9.         if (SUCCEEDED(hr) && bTracked)  
  10.         {  
  11.             Joint joints[JointType_Count];  
  12.             HandState leftHandState = HandState_Unknown;  
  13.             HandState rightHandState = HandState_Unknown;  
  14.   
  15.             pBody->get_HandLeftState(&leftHandState);  
  16.             pBody->get_HandRightState(&rightHandState);  
  17.   
  18.             hr = pBody->GetJoints(_countof(joints), joints);  
  19.             if (SUCCEEDED(hr))  
  20.             {  
  21.                 // XXXXXXXX  
  22.             }  
  23.         }  
  24.     }  
代码很简单,从方法名字就能看出来。这里的可视化方法是用微软提供SDK例子里面的方法,

大概就是相连的关节使用直线连起来之类的,这部分代码相当无聊,有Direct2D基础的同学可以无视,详细请看范例。
效果如下





好了,其实之前的例子SDK提供的例子多少有,这里自然要给个原创的东西(当然创意不是原创的)。

看到标题的同学大概也能猜到了,那就是《攻壳机动队 S.A.C》(Ghost In The Shell: Stand Alone Complex)里面出现的一个人物,这个人物出场使用一个图标挡住了脸:


这里我们就要实现这个效果,算是一个实时打码的软件吧。

这里,我们的那个文字也要像原作一样旋转,为了保证流程,所以我们这次图像API选择的D2D 1.1(能够等在垂直同步)。

D2D 1.1的初始化,说实话,我都不记得,

要用D2D 1.1时,复制过来即可,毕竟不是考试


那么怎么实现那个图标呢,您可是使用图片。但是作为程序猿,使用代码即时生成是一个不错的选择。

Direct2D提供了一个硬件加速的几何体渲染接口,非常方便。旋转的文字渲染需要DirectWrite + Direct2D,学习的范围不在这里,

详细请看范例。


至于这个图标的几何形状怎么表示,当然用目测。。。这是不现实的,我在其他地方找到了一个笑面男的SVG图像,代码如下:

[html]  view plain  copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2.   
  3. <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-160 -160 360 320">    
  4.   <path id="f" d="m123,0a123,123 0,0 1-246,0a123,123 0,0 1 246,0"/>    
  5.   <g fill="#057">   
  6.     <circle r="160"/>    
  7.     <circle r="150" fill="#fff"/>    
  8.     <text font-size="28" font-stretch="condensed" font-family="Impact">  
  9.       <animateTransform type="rotate" from="360 0 0" to="0 0 0" dur="10s" attributeName="transform" repeatCount="indefinite"/>  
  10.       <textPath xlink:href="#f">I thought what I'd do was, I'd pretend I was one of those deaf-mutes</textPath>  
  11.     </text>    
  12.     <circle r="115"/>    
  13.     <circle r="95" fill="#fff"/>    
  14.     <path d="m-8-119h16 l2,5h-20z"/>    
  15.     <circle cx="160" cy="0" r="40"/>    
  16.     <path d="m-95-20v-20h255a40,40 0,0 1 0,80h-55v-20z"/>    
  17.     <path d="m-85 0a85,85 0,0 0 170,0h-20a65,65 0,0 1-130,0z"/>    
  18.     <path d="m-65 20v20h140v-20z"/>    
  19.     <path d="m-115-20v10h25v30h250a20,20 0,0 0 0,-40z" fill="#fff"/>    
  20.     <path d="m-20 10c-17-14-27-14-44 0 6-25 37-25 44 0z"/>    
  21.     <path d="m60 10c-17-14-27-14-44 0 6-25 37-25 44 0z"/>   
  22.   </g>   
  23. </svg>  

现在根据这个代码,可以大概写出下面的代码,部分坐标经过了本人的微调。

[cpp]  view plain  copy
  1. // 创建笑面男相关  
  2. HRESULT ImageRenderer::CreateLaughingMan(){  
  3.     // 基本半径  
  4.     const FLOAT BASE_RADIUS = 135.f;  
  5.   
  6.     HRESULT hr = S_OK;  
  7.     IDWriteTextFormat* pImpactFormat = nullptr;  
  8.     // 创建Impact文本格式  
  9.     hr = m_pDWriteFactory->CreateTextFormat(  
  10.         L"Impact",  
  11.         nullptr,  
  12.         DWRITE_FONT_WEIGHT_NORMAL,  
  13.         DWRITE_FONT_STYLE_NORMAL,  
  14.         DWRITE_FONT_STRETCH_CONDENSED,  
  15.         41.f/96.f*72.f,  
  16.         L"",  
  17.         &pImpactFormat  
  18.         );  
  19.   
  20.     // 创建文本布局  
  21.     if (SUCCEEDED(hr)){  
  22.         pImpactFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);  
  23.         WCHAR* text = L"I thought what I'd do was, I'd pretend I was one of those deaf-mutes";  
  24.         auto length = wcslen(text);  
  25.         hr = m_pDWriteFactory->CreateTextLayout(text, length, pImpactFormat, BASE_RADIUS, BASE_RADIUS, &m_pTextLayoutLaughingMan);  
  26.     }  
  27.     // 创建文本几何路径: 一个圆  
  28.     if (SUCCEEDED(hr)){  
  29.         D2D1_ELLIPSE ellipse;  
  30.         ellipse.point.x = 0.f;  
  31.         ellipse.point.y = 0.f;  
  32.         ellipse.radiusX = BASE_RADIUS;  
  33.         ellipse.radiusY = BASE_RADIUS;  
  34.         hr = m_pD2DFactory->CreateEllipseGeometry(&ellipse, &m_pTextAnimationPath);  
  35.     }  
  36.     // 笑面男路径  
  37.     if (SUCCEEDED(hr)){  
  38.         hr = m_pD2DFactory->CreatePathGeometry(&m_pLaughingManGeometryBlue);  
  39.         // 画线  
  40.         ID2D1GeometrySink* pSink = nullptr;  
  41.         if (SUCCEEDED(hr)){  
  42.             hr = m_pLaughingManGeometryBlue->Open(&pSink);  
  43.         }  
  44.         if (SUCCEEDED(hr)){  
  45.             auto nowPoint = D2D1::Point2F();  
  46.             pSink->SetFillMode(D2D1_FILL_MODE_WINDING);  
  47.             D2D1_ARC_SEGMENT arc;  
  48.             D2D1_BEZIER_SEGMENT bezier;  
  49.             arc.rotationAngle = 0.f;  
  50.             // <path d="m-8-119h16 l2,5h-20z"/>  
  51.             nowPoint.x = -8.f; nowPoint.y = -124.f;  
  52.             pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED);  
  53.             nowPoint.x += 16.f;  
  54.             pSink->AddLine(nowPoint);  
  55.             nowPoint.x += 2.f; nowPoint.y += 5.f;  
  56.             pSink->AddLine(nowPoint);  
  57.             nowPoint.x -= 20.f;  
  58.             pSink->AddLine(nowPoint);  
  59.             pSink->EndFigure(D2D1_FIGURE_END_CLOSED);  
  60.             // <path d = "m-95-20v-20h255a40,40 0,0 1 0,80h-55v-20z" / >  
  61.             nowPoint.x = -105.f; nowPoint.y = -20.f;  
  62.             pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED);  
  63.             nowPoint.y -= 20.f;  
  64.             pSink->AddLine(nowPoint);  
  65.             nowPoint.x += 270.f;  
  66.             pSink->AddLine(nowPoint);  
  67.             nowPoint.y += 80.f;  
  68.             arc.size.height = 40.f;  
  69.             arc.size.width = 40.f;  
  70.             arc.sweepDirection = D2D1_SWEEP_DIRECTION_CLOCKWISE;  
  71.             arc.point = nowPoint;  
  72.             arc.arcSize = D2D1_ARC_SIZE_SMALL;  
  73.             pSink->AddArc(&arc);  
  74.             nowPoint.x -= 55.f;  
  75.             pSink->AddLine(nowPoint);  
  76.             nowPoint.y -= 20.f;  
  77.             pSink->AddLine(nowPoint);  
  78.             nowPoint.x += 55.f;  
  79.             pSink->AddLine(nowPoint);  
  80.             nowPoint.y -= 40.f;  
  81.             arc.size.height = 20.f;  
  82.             arc.size.width = 20.f;  
  83.             arc.sweepDirection = D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;  
  84.             arc.point = nowPoint;  
  85.             pSink->AddArc(&arc);  
  86.             pSink->EndFigure(D2D1_FIGURE_END_CLOSED);  
  87.             // <path d="m-85 0a85,85 0,0 0 170,0h-20a65,65 0,0 1-130,0z"/>  
  88.             nowPoint.x = -85.f; nowPoint.y= 20.f;  
  89.             pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED);  
  90.             nowPoint.x += 170.f;  
  91.             arc.size.height = 90.f;  
  92.             arc.size.width = 90.f;  
  93.             arc.sweepDirection = D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE;  
  94.             arc.arcSize = D2D1_ARC_SIZE_SMALL;  
  95.             arc.point = nowPoint;  
  96.             pSink->AddArc(&arc);  
  97.             nowPoint.x -= 20.f;  
  98.             pSink->AddLine(nowPoint);  
  99.             nowPoint.x -= 130.f;  
  100.             arc.size.height = 70.f;  
  101.             arc.size.width = 70.f;  
  102.             arc.sweepDirection = D2D1_SWEEP_DIRECTION_CLOCKWISE;  
  103.             arc.point = nowPoint;  
  104.             pSink->AddArc(&arc);  
  105.             pSink->EndFigure(D2D1_FIGURE_END_CLOSED);  
  106.             // <path d="m-65 20v20h130v-20z"/>    
  107.             nowPoint.x = -65.f; nowPoint.y = 20.f;  
  108.             pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED);  
  109.             nowPoint.y += 20.f;  
  110.             pSink->AddLine(nowPoint);  
  111.             nowPoint.x += 130.f;  
  112.             pSink->AddLine(nowPoint);  
  113.             nowPoint.y -= 20.f;  
  114.             pSink->AddLine(nowPoint);  
  115.             pSink->EndFigure(D2D1_FIGURE_END_CLOSED);  
  116.             //pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED);  
  117.             // <path d = "m-20 10c-17-14-27-14-44 0 6-25 37-25 44 0z" / >  
  118.             nowPoint.x = -20.f; nowPoint.y = 10.f;  
  119.             pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED);  
  120.             bezier.point1.x = nowPoint.x - 17.f;  
  121.             bezier.point1.y = nowPoint.y - 14.f;  
  122.             bezier.point2.x = nowPoint.x - 27.f;  
  123.             bezier.point2.y = nowPoint.y - 14.f;  
  124.             nowPoint.x -= 44.f;  
  125.             bezier.point3 = nowPoint;  
  126.             pSink->AddBezier(&bezier);  
  127.             bezier.point1.x = nowPoint.x + 6.f;  
  128.             bezier.point1.y = nowPoint.y - 25.f;  
  129.             bezier.point2.x = nowPoint.x + 37.f;  
  130.             bezier.point2.y = nowPoint.y - 25.f;  
  131.             nowPoint.x += 44.f;  
  132.             bezier.point3 = nowPoint;  
  133.             pSink->AddBezier(&bezier);  
  134.             pSink->EndFigure(D2D1_FIGURE_END_CLOSED);  
  135.             // <path d = "m60 10c-17-14-27-14-44 0 6-25 37-25 44 0z" / >  
  136.             nowPoint.x = 60.f; nowPoint.y = 10.f;  
  137.             pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED);  
  138.             bezier.point1.x = nowPoint.x - 17.f;  
  139.             bezier.point1.y = nowPoint.y - 14.f;  
  140.             bezier.point2.x = nowPoint.x - 27.f;  
  141.             bezier.point2.y = nowPoint.y - 14.f;  
  142.             nowPoint.x -= 44.f;  
  143.             bezier.point3 = nowPoint;  
  144.             pSink->AddBezier(&bezier);  
  145.             bezier.point1.x = nowPoint.x + 6.f;  
  146.             bezier.point1.y = nowPoint.y - 25.f;  
  147.             bezier.point2.x = nowPoint.x + 37.f;  
  148.             bezier.point2.y = nowPoint.y - 25.f;  
  149.             nowPoint.x += 44.f;  
  150.             bezier.point3 = nowPoint;  
  151.             pSink->AddBezier(&bezier);  
  152.             pSink->EndFigure(D2D1_FIGURE_END_CLOSED);  
  153.             hr = pSink->Close();  
  154.         }  
  155.         SafeRelease(pSink);  
  156.     }  
  157.     // 笑面男白色部分  
  158.     if (SUCCEEDED(hr)){  
  159.         hr = m_pD2DFactory->CreatePathGeometry(&m_pLaughingManGeometryWhite);  
  160.         // 画线  
  161.         ID2D1GeometrySink* pSink = nullptr;  
  162.         if (SUCCEEDED(hr)){  
  163.             hr = m_pLaughingManGeometryWhite->Open(&pSink);  
  164.         }  
  165.         if (SUCCEEDED(hr)){  
  166.             auto nowPoint = D2D1::Point2F();  
  167.             // <path d = "m-115-20v10h25v30h250a20,20 0,0 0 0,-40z" fill = "#fff" / >  
  168.             nowPoint.x = -125.f; nowPoint.y = -20.f;  
  169.             pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED);  
  170.             nowPoint.y += 10.f;  
  171.             pSink->AddLine(nowPoint);  
  172.             nowPoint.x += 35.f;  
  173.             pSink->AddLine(nowPoint);  
  174.             nowPoint.y += 30.f;  
  175.             pSink->AddLine(nowPoint);  
  176.             nowPoint.x += 260.f;  
  177.             pSink->AddLine(nowPoint);  
  178.             nowPoint.y -= 40.f;  
  179.             D2D1_ARC_SEGMENT arc = { nowPoint, D2D1::SizeF(20.f, 20.f), 0.f, D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE, D2D1_ARC_SIZE_SMALL };  
  180.             pSink->AddArc(&arc);  
  181.             pSink->EndFigure(D2D1_FIGURE_END_CLOSED);  
  182.             hr = pSink->Close();  
  183.         }  
  184.     }  
  185.     // 笑面蓝  
  186.     if (SUCCEEDED(hr)){  
  187.         hr = m_pD2DDeviceContext->CreateSolidColorBrush(D2D1::ColorF(0x005577), &m_pLaughingBlueBrush);  
  188.     }  
  189.     // 笑面白  
  190.     if (SUCCEEDED(hr)){  
  191.         hr = m_pD2DDeviceContext->CreateSolidColorBrush(D2D1::ColorF(0xFFFFFF), &m_pLaughingWhiteBrush);  
  192.     }  
  193.     SafeRelease(pImpactFormat);  
  194.     return hr;  
  195. }  

反正很蛋疼




是的,我们这次使用的是D2D 1.1。之前说过,所以这里就使用轮询模式。

这里要使用彩色数据流 + 骨骼数据流,那么是否需要使用复源帧?

使用复源帧是为了保证数据同步,轮询模式下异步的效果很及时,所以这里不使用复源帧。


大致过程如下:

刷新:

    获得彩色数据 -> 复制到位图

    获得骨骼数据 -> 检查并更新头部位置,根据远近设置相对缩放率(实现近大远小,大致即可,不用精确)

渲染:

    渲染彩色帧

    渲染笑面男


Kinect2支持6人骨骼追踪,所以数据要准备6份。这样6人同时出现也能一起打码得意


效果如下:Kinect2获得的骨骼数据默认就已经平滑过了,不需要像1代那样设置平滑参数,也说明精度的提高。

为了模拟原作的抖动,只能自己手动模拟了。



范例下载地址:点击这里


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值