Kinect for Windows SDK v2.0 开发笔记 (五)骨骼帧与笑面男
来源:互联网 发布:英雄联盟评分算法 编辑:程序博客网 时间:2024/05/17 02:20
(转载请注明出处)
使用SDK: Kinect for Windows SDK v2.0 public preview
这次说说这骨骼帧的获取。嗯,Kinect买来就为这个啊。不然其他数据,买其他产品就行了,Kinect的卖点也是这个。
先看看这次支持的骨骼关节:
enum _JointType { JointType_SpineBase= 0, JointType_SpineMid= 1, JointType_Neck= 2, JointType_Head= 3, JointType_ShoulderLeft= 4, JointType_ElbowLeft= 5, JointType_WristLeft= 6, JointType_HandLeft= 7, JointType_ShoulderRight= 8, JointType_ElbowRight= 9, JointType_WristRight= 10, JointType_HandRight= 11, JointType_HipLeft= 12, JointType_KneeLeft= 13, JointType_AnkleLeft= 14, JointType_FootLeft= 15, JointType_HipRight= 16, JointType_KneeRight= 17, JointType_AnkleRight= 18, JointType_FootRight= 19, JointType_SpineShoulder= 20, JointType_HandTipLeft= 21, JointType_ThumbLeft= 22, JointType_HandTipRight= 23, JointType_ThumbRight= 24, JointType_Count= ( JointType_ThumbRight + 1 ) } ;
支持这25个关节点,不排除会增加的可能,毕竟近景可以分辨十指。
每个关节的状态用这个结构体描述:
typedef struct _Joint { JointType JointType; CameraSpacePoint Position; TrackingState TrackingState; } Joint;
JointType就是之前的关节编号,Position是Kinect的相机空间坐标,是三维的。TrackingState是目前关节的追踪状态,
有: 未追踪(0),位置是推测的(1),位置是追踪的(2)
值得说明的是这次C++的SDK也提供了判断手的状态:
enum _HandState { HandState_Unknown= 0, HandState_NotTracked= 1, HandState_Open= 2, HandState_Closed= 3, HandState_Lasso= 4 } ;
有:未知(0),未追踪(1),摊开(2),握拳(3)以及Lasso(4),Lasso不知道怎么翻译,大概就是处于摊开与握拳之间的状态,
比如:或者甚至这样都能称为Lasso。
使用方法和之前的差不多,说说不同的:
IBody* ppBodies[BODY_COUNT] = {0}; if (SUCCEEDED(hr)) { hr = pBodyFrame->GetAndRefreshBodyData(BODY_COUNT, ppBodies); }
这样获取6个IBody接口,用完了记得释放,一个循环释放完
每个接口使用类似下面的代码获取数据:
for (int i = 0; i < nBodyCount; ++i) { IBody* pBody = ppBodies[i]; if (pBody) { BOOLEAN bTracked = false; hr = pBody->get_IsTracked(&bTracked); if (SUCCEEDED(hr) && bTracked) { Joint joints[JointType_Count]; HandState leftHandState = HandState_Unknown; HandState rightHandState = HandState_Unknown; pBody->get_HandLeftState(&leftHandState); pBody->get_HandRightState(&rightHandState); hr = pBody->GetJoints(_countof(joints), joints); if (SUCCEEDED(hr)) { // XXXXXXXX } } }代码很简单,从方法名字就能看出来。这里的可视化方法是用微软提供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图像,代码如下:
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-160 -160 360 320"> <path id="f" d="m123,0a123,123 0,0 1-246,0a123,123 0,0 1 246,0"/> <g fill="#057"> <circle r="160"/> <circle r="150" fill="#fff"/> <text font-size="28" font-stretch="condensed" font-family="Impact"> <animateTransform type="rotate" from="360 0 0" to="0 0 0" dur="10s" attributeName="transform" repeatCount="indefinite"/> <textPath xlink:href="#f">I thought what I'd do was, I'd pretend I was one of those deaf-mutes</textPath> </text> <circle r="115"/> <circle r="95" fill="#fff"/> <path d="m-8-119h16 l2,5h-20z"/> <circle cx="160" cy="0" r="40"/> <path d="m-95-20v-20h255a40,40 0,0 1 0,80h-55v-20z"/> <path d="m-85 0a85,85 0,0 0 170,0h-20a65,65 0,0 1-130,0z"/> <path d="m-65 20v20h140v-20z"/> <path d="m-115-20v10h25v30h250a20,20 0,0 0 0,-40z" fill="#fff"/> <path d="m-20 10c-17-14-27-14-44 0 6-25 37-25 44 0z"/> <path d="m60 10c-17-14-27-14-44 0 6-25 37-25 44 0z"/> </g> </svg>
现在根据这个代码,可以大概写出下面的代码,部分坐标经过了本人的微调。
// 创建笑面男相关HRESULT ImageRenderer::CreateLaughingMan(){ // 基本半径 const FLOAT BASE_RADIUS = 135.f; HRESULT hr = S_OK; IDWriteTextFormat* pImpactFormat = nullptr; // 创建Impact文本格式 hr = m_pDWriteFactory->CreateTextFormat( L"Impact", nullptr, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_CONDENSED, 41.f/96.f*72.f, L"", &pImpactFormat ); // 创建文本布局 if (SUCCEEDED(hr)){ pImpactFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); WCHAR* text = L"I thought what I'd do was, I'd pretend I was one of those deaf-mutes"; auto length = wcslen(text); hr = m_pDWriteFactory->CreateTextLayout(text, length, pImpactFormat, BASE_RADIUS, BASE_RADIUS, &m_pTextLayoutLaughingMan); } // 创建文本几何路径: 一个圆 if (SUCCEEDED(hr)){ D2D1_ELLIPSE ellipse; ellipse.point.x = 0.f; ellipse.point.y = 0.f; ellipse.radiusX = BASE_RADIUS; ellipse.radiusY = BASE_RADIUS; hr = m_pD2DFactory->CreateEllipseGeometry(&ellipse, &m_pTextAnimationPath); } // 笑面男路径 if (SUCCEEDED(hr)){ hr = m_pD2DFactory->CreatePathGeometry(&m_pLaughingManGeometryBlue); // 画线 ID2D1GeometrySink* pSink = nullptr; if (SUCCEEDED(hr)){ hr = m_pLaughingManGeometryBlue->Open(&pSink); } if (SUCCEEDED(hr)){ auto nowPoint = D2D1::Point2F(); pSink->SetFillMode(D2D1_FILL_MODE_WINDING); D2D1_ARC_SEGMENT arc; D2D1_BEZIER_SEGMENT bezier; arc.rotationAngle = 0.f; // <path d="m-8-119h16 l2,5h-20z"/> nowPoint.x = -8.f; nowPoint.y = -124.f; pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED); nowPoint.x += 16.f; pSink->AddLine(nowPoint); nowPoint.x += 2.f; nowPoint.y += 5.f; pSink->AddLine(nowPoint); nowPoint.x -= 20.f; pSink->AddLine(nowPoint); pSink->EndFigure(D2D1_FIGURE_END_CLOSED); // <path d = "m-95-20v-20h255a40,40 0,0 1 0,80h-55v-20z" / > nowPoint.x = -105.f; nowPoint.y = -20.f; pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED); nowPoint.y -= 20.f; pSink->AddLine(nowPoint); nowPoint.x += 270.f; pSink->AddLine(nowPoint); nowPoint.y += 80.f; arc.size.height = 40.f; arc.size.width = 40.f; arc.sweepDirection = D2D1_SWEEP_DIRECTION_CLOCKWISE; arc.point = nowPoint; arc.arcSize = D2D1_ARC_SIZE_SMALL; pSink->AddArc(&arc); nowPoint.x -= 55.f; pSink->AddLine(nowPoint); nowPoint.y -= 20.f; pSink->AddLine(nowPoint); nowPoint.x += 55.f; pSink->AddLine(nowPoint); nowPoint.y -= 40.f; arc.size.height = 20.f; arc.size.width = 20.f; arc.sweepDirection = D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE; arc.point = nowPoint; pSink->AddArc(&arc); pSink->EndFigure(D2D1_FIGURE_END_CLOSED); // <path d="m-85 0a85,85 0,0 0 170,0h-20a65,65 0,0 1-130,0z"/> nowPoint.x = -85.f; nowPoint.y= 20.f; pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED); nowPoint.x += 170.f; arc.size.height = 90.f; arc.size.width = 90.f; arc.sweepDirection = D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE; arc.arcSize = D2D1_ARC_SIZE_SMALL; arc.point = nowPoint; pSink->AddArc(&arc); nowPoint.x -= 20.f; pSink->AddLine(nowPoint); nowPoint.x -= 130.f; arc.size.height = 70.f; arc.size.width = 70.f; arc.sweepDirection = D2D1_SWEEP_DIRECTION_CLOCKWISE; arc.point = nowPoint; pSink->AddArc(&arc); pSink->EndFigure(D2D1_FIGURE_END_CLOSED); // <path d="m-65 20v20h130v-20z"/> nowPoint.x = -65.f; nowPoint.y = 20.f; pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED); nowPoint.y += 20.f; pSink->AddLine(nowPoint); nowPoint.x += 130.f; pSink->AddLine(nowPoint); nowPoint.y -= 20.f; pSink->AddLine(nowPoint); pSink->EndFigure(D2D1_FIGURE_END_CLOSED); //pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED); // <path d = "m-20 10c-17-14-27-14-44 0 6-25 37-25 44 0z" / > nowPoint.x = -20.f; nowPoint.y = 10.f; pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED); bezier.point1.x = nowPoint.x - 17.f; bezier.point1.y = nowPoint.y - 14.f; bezier.point2.x = nowPoint.x - 27.f; bezier.point2.y = nowPoint.y - 14.f; nowPoint.x -= 44.f; bezier.point3 = nowPoint; pSink->AddBezier(&bezier); bezier.point1.x = nowPoint.x + 6.f; bezier.point1.y = nowPoint.y - 25.f; bezier.point2.x = nowPoint.x + 37.f; bezier.point2.y = nowPoint.y - 25.f; nowPoint.x += 44.f; bezier.point3 = nowPoint; pSink->AddBezier(&bezier); pSink->EndFigure(D2D1_FIGURE_END_CLOSED); // <path d = "m60 10c-17-14-27-14-44 0 6-25 37-25 44 0z" / > nowPoint.x = 60.f; nowPoint.y = 10.f; pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED); bezier.point1.x = nowPoint.x - 17.f; bezier.point1.y = nowPoint.y - 14.f; bezier.point2.x = nowPoint.x - 27.f; bezier.point2.y = nowPoint.y - 14.f; nowPoint.x -= 44.f; bezier.point3 = nowPoint; pSink->AddBezier(&bezier); bezier.point1.x = nowPoint.x + 6.f; bezier.point1.y = nowPoint.y - 25.f; bezier.point2.x = nowPoint.x + 37.f; bezier.point2.y = nowPoint.y - 25.f; nowPoint.x += 44.f; bezier.point3 = nowPoint; pSink->AddBezier(&bezier); pSink->EndFigure(D2D1_FIGURE_END_CLOSED); hr = pSink->Close(); } SafeRelease(pSink); } // 笑面男白色部分 if (SUCCEEDED(hr)){ hr = m_pD2DFactory->CreatePathGeometry(&m_pLaughingManGeometryWhite); // 画线 ID2D1GeometrySink* pSink = nullptr; if (SUCCEEDED(hr)){ hr = m_pLaughingManGeometryWhite->Open(&pSink); } if (SUCCEEDED(hr)){ auto nowPoint = D2D1::Point2F(); // <path d = "m-115-20v10h25v30h250a20,20 0,0 0 0,-40z" fill = "#fff" / > nowPoint.x = -125.f; nowPoint.y = -20.f; pSink->BeginFigure(nowPoint, D2D1_FIGURE_BEGIN_FILLED); nowPoint.y += 10.f; pSink->AddLine(nowPoint); nowPoint.x += 35.f; pSink->AddLine(nowPoint); nowPoint.y += 30.f; pSink->AddLine(nowPoint); nowPoint.x += 260.f; pSink->AddLine(nowPoint); nowPoint.y -= 40.f; D2D1_ARC_SEGMENT arc = { nowPoint, D2D1::SizeF(20.f, 20.f), 0.f, D2D1_SWEEP_DIRECTION_COUNTER_CLOCKWISE, D2D1_ARC_SIZE_SMALL }; pSink->AddArc(&arc); pSink->EndFigure(D2D1_FIGURE_END_CLOSED); hr = pSink->Close(); } } // 笑面蓝 if (SUCCEEDED(hr)){ hr = m_pD2DDeviceContext->CreateSolidColorBrush(D2D1::ColorF(0x005577), &m_pLaughingBlueBrush); } // 笑面白 if (SUCCEEDED(hr)){ hr = m_pD2DDeviceContext->CreateSolidColorBrush(D2D1::ColorF(0xFFFFFF), &m_pLaughingWhiteBrush); } SafeRelease(pImpactFormat); return hr;}
反正很蛋疼
是的,我们这次使用的是D2D 1.1。之前说过,所以这里就使用轮询模式。
这里要使用彩色数据流 + 骨骼数据流,那么是否需要使用复源帧?
使用复源帧是为了保证数据同步,轮询模式下异步的效果很及时,所以这里不使用复源帧。
大致过程如下:
刷新:
获得彩色数据 -> 复制到位图
获得骨骼数据 -> 检查并更新头部位置,根据远近设置相对缩放率(实现近大远小,大致即可,不用精确)
渲染:
渲染彩色帧
渲染笑面男
Kinect2支持6人骨骼追踪,所以数据要准备6份。这样6人同时出现也能一起打码
效果如下:Kinect2获得的骨骼数据默认就已经平滑过了,不需要像1代那样设置平滑参数,也说明精度的提高。
为了模拟原作的抖动,只能自己手动模拟了。
范例下载地址:点击这里
- Kinect for Windows SDK v2.0 开发笔记 (五)骨骼帧与笑面男
- Kinect for Windows SDK v2.0 开发笔记 (三)深度帧与红外帧获取
- Kinect for Windows SDK v2.0 开发笔记 (一)环境
- Kinect for Windows SDK v2.0 开发笔记 (四)玩家索引与绿屏技术
- Kinect for Windows SDK v2.0 开发笔记 (二)彩色帧获取
- Kinect for Windows SDK v2.0 开发笔记 (九)面部帧获取
- Kinect for Windows SDK v2.0 开发笔记 (十) 高清面部帧(1) FACS 介绍
- Kinect for Windows SDK v2.0 开发笔记 (十五) 手势帧
- Kinect for Windows SDK v2.0 开发笔记 (十七) 深度帧3D
- Kinect for Windows SDK v2.0 开发笔记 (十六) SDK2.0正式发布 与 自带工具
- Kinect for Windows SDK v2.0 开发笔记 (六)音频获取
- Kinect for Windows SDK v2.0 开发笔记 (七)语音识别(上)
- Kinect for Windows SDK v2.0 开发笔记 (八)语音识别(下)
- Kinect for Windows SDK v2.0 开发笔记 (十八) Fusion 基本2D显示
- Kinect for Windows SDK v2.0 开发笔记 (十一) 高清面部帧(2) 面部特征对齐
- Kinect for Windows SDK v2.0 开发笔记 (十二) 高清面部帧(3) 面部模型(2D)
- Kinect for Windows SDK v2.0 开发笔记 (十三) 高清面部帧(4) 面部模型构建器
- Kinect for Windows SDK v2.0 开发笔记 (十四) 高清面部帧(5) 面部模型 3D
- 不少程序员都会碰到的三个面试题
- 嵌入式Linux移植USB网卡驱动
- hdu 2044
- HDU 2036 改革春风吹满地
- 【Visual C++】游戏开发笔记二十八 最精简的Direct3D11 Demo筋骨脉络全攻略
- Kinect for Windows SDK v2.0 开发笔记 (五)骨骼帧与笑面男
- 发现的秘密华硕背后的SonicMaster
- Linux下chkconfig命令详解
- HDU 2037 今年暑假不AC
- ListView有背景图片或背景颜色,那么在滑动ListView的时候,ListView的背景会变黑
- BOJ 433. Fibnacci
- 双耳节拍送餐服务到你的大脑
- 黑马程序员_JavaBean内省
- Painting Storages