在继《基于qualcomm平台的kinect教程三之获取深度图》之后,今天我们来获取下人体的骨骼图。
一.核心代码:
//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corpora
tion. All rights reserved.
//
//------------------------------------------------------------------------------
#include "stdafx.h"
#include
#include "SkeletonBasics.h"
#include "resource.h"
static const float g_JointThickness = 3.0f;
static const float g_TrackedBoneThickness = 6.0f;
static const float g_InferredBoneThickness = 1.0f;
///
/// Entry point for the application
///
/// handle to the application instance
/// always 0
/// command line arguments
/// whether to display minimized, maximized, or normally
/// status
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
CSkeletonBasics application;
application.Run(hInstance, nCmdShow);
}
///
/// Constructor
///
CSkeletonBasics::CSkeletonBasics() :
m_pD2DFactory(NULL),
m_hNextSkeletonEvent(INVALID_HANDLE_VALUE),
m_pSkeletonStreamHandle(INVALID_HANDLE_VALUE),
m_bSeatedMode(false),
m_pRenderTarget(NULL),
m_pBrushJointTracked(NULL),
m_pBrushJointInferred(NULL),
m_pBrushBoneTracked(NULL),
m_pBrushBoneInferred(NULL),
m_pNuiSensor(NULL)
{
ZeroMemory(m_Points,sizeof(m_Points));
}
///
/// Destructor
///
CSkeletonBasics::~CSkeletonBasics()
{
if (m_pNuiSensor)
{
m_pNuiSensor->NuiShutdown();
}
if (m_hNextSkeletonEvent && (m_hNextSkeletonEvent != INVALID_HANDLE_VALUE))
{
CloseHandle(m_hNextSkeletonEvent);
}
// clean up Direct2D objects
DiscardDirect2DResources();
// clean up Direct2D
SafeRelease(m_pD2DFactory);
SafeRelease(m_pNuiSensor);
}
///
/// Creates the main window and begins processing
///
/// handle to the application instance
/// whether to display minimized, maximized, or normally
int CSkeletonBasics::Run(HINSTANCE hInstance, int nCmdShow)
{
MSG msg = {0};
WNDCLASS wc = {0};
// Dialog custom window class
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.cbWndExtra = DLGWINDOWEXTRA;
wc.hInstance = hInstance;
wc.hCursor = LoadCursorW(NULL, IDC_ARROW);
wc.hIcon = LoadIconW(hInstance, MAKEINTRESOURCE(IDI_APP));
wc.lpfnWndProc = DefDlgProcW;
wc.lpszClassName = L"SkeletonBasicsAppDlgWndClass";
if (!RegisterClassW(&wc))
{
return 0;
}
// Create main application window
HWND hWndApp = CreateDialogParamW(
hInstance,
MAKEINTRESOURCE(IDD_APP),
NULL,
(DLGPROC)CSkeletonBasics::MessageRouter,
reinterpret_cast(this));
// Show window
ShowWindow(hWndApp, nCmdShow);
const int eventCount = 1;
HANDLE hEvents[eventCount];
// Main message loop
while (WM_QUIT != msg.message)
{
hEvents[0] = m_hNextSkeletonEvent;
// Check to see if we have either a message (by passing in QS_ALLEVENTS)
// Or a Kinect event (hEvents)
// Update() will check for Kinect events individually, in case more than one are signalled
MsgWaitForMultipleObjects(eventCount, hEvents, FALSE, INFINITE, QS_ALLINPUT);
// Explicitly check the Kinect frame event since MsgWaitForMultipleObjects
// can return for other reasons even though it is signaled.
Update();
while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
{
// If a dialog message will be taken care of by the dialog proc
if ((hWndApp != NULL) && IsDialogMessageW(hWndApp, &msg))
{
continue;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
return static_cast(msg.wParam);
}
///
/// Main processing function
///
void CSkeletonBasics::Update()
{
if (NULL == m_pNuiSensor)
{
return;
}
// Wait for 0ms, just quickly test if it is time to process a skeleton
if ( WAIT_OBJECT_0 == WaitForSingleObject(m_hNextSkeletonEvent, 0) )
{
ProcessSkeleton();
}
}
///
/// Handles window messages, passes most to the class instance to handle
///
/// window message is for
/// message
/// message data
/// additional message data
/// result of message processing
LRESULT CALLBACK CSkeletonBasics::MessageRouter(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CSkeletonBasics* pThis = NULL;
if (WM_INITDIALOG == uMsg)
{
pThis = reinterpret_cast(lParam);
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast(pThis));
}
else
{
pThis = reinterpret_cast(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
if (pThis)
{
return pThis->DlgProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
///
/// Handle windows messages for the class instance
///
/// window message is for
/// message
/// message data
/// additional message data
/// result of message processing
LRESULT CALLBACK CSkeletonBasics::DlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
{
// Bind application window handle
m_hWnd = hWnd;
// Init Direct2D
D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);
// Look for a connected Kinect, and create it if found
CreateFirstConnected();
}
break;
// If the titlebar X is clicked, destroy app
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
// Quit the main message pump
PostQuitMessage(0);
break;
// Handle button press
case WM_COMMAND:
// If it was for the near mode control and a clicked event, change near mode
if (IDC_CHECK_SEATED == LOWORD(wParam) && BN_CLICKED == HIWORD(wParam))
{
// Toggle out internal state for near mode
m_bSeatedMode = !m_bSeatedMode;
if (NULL != m_pNuiSensor)
{
// Set near mode for sensor based on our internal state
m_pNuiSensor->NuiSkeletonTrackingEnable(m_hNextSkeletonEvent, m_bSeatedMode ? NUI_SKELETON_TRACKING_FLAG_ENABLE_SEATED_SUPPORT : 0);
}
}
break;
}
return FALSE;
}
///
/// Create the first connected Kinect found
///
/// indicates success or failure
HRESULT CSkeletonBasics::CreateFirstConnected()
{
INuiSensor * pNuiSensor;
int iSensorCount = 0;
HRESULT hr = NuiGetSensorCount(&iSensorCount);
if (FAILED(hr))
{
return hr;
}
// Look at each Kinect sensor
for (int i = 0; i < iSensorCount; ++i)
{
// Create the sensor so we can check status, if we can't create it, move on to the next
hr = NuiCreateSensorByIndex(i, &pNuiSensor);
if (FAILED(hr))
{
continue;
}
// Get the status of the sensor, and if connected, then we can initialize it
hr = pNuiSensor->NuiStatus();
if (S_OK == hr)
{
m_pNuiSensor = pNuiSensor;
break;
}
// This sensor wasn't OK, so release it since we're not using it
pNuiSensor->Release();
}
if (NULL != m_pNuiSensor)
{
// Initialize the Kinect and specify that we'll be using skeleton
hr = m_pNuiSensor->NuiInitialize(NUI_INITIALIZE_FLAG_USES_SKELETON);
if (SUCCEEDED(hr))
{
// Create an event that will be signaled when skeleton data is available
m_hNextSkeletonEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
// Open a skeleton stream to receive skeleton data
hr = m_pNuiSensor->NuiSkeletonTrackingEnable(m_hNextSkeletonEvent, 0);
}
}
if (NULL == m_pNuiSensor || FAILED(hr))
{
SetStatusMessage(L"No ready Kinect found!");
return E_FAIL;
}
return hr;
}
///
/// Handle new skeleton data
///
void CSkeletonBasics::ProcessSkeleton()
{
NUI_SKELETON_FRAME skeletonFrame = {0};
HRESULT hr = m_pNuiSensor->NuiSkeletonGetNextFrame(0, &skeletonFrame);
if ( FAILED(hr) )
{
return;
}
// smooth out the skeleton data
m_pNuiSensor->NuiTransformSmooth(&skeletonFrame, NULL);
// Endure Direct2D is ready to draw
hr = EnsureDirect2DResources( );
if ( FAILED(hr) )
{
return;
}
m_pRenderTarget->BeginDraw();
m_pRenderTarget->Clear( );
RECT rct;
GetClientRect( GetDlgItem( m_hWnd, IDC_VIDEOVIEW ), &rct);
int width = rct.right;
int height = rct.bottom;
for (int i = 0 ; i < NUI_SKELETON_COUNT; ++i)
{
NUI_SKELETON_TRACKING_STATE trackingState = skeletonFrame.SkeletonData
.eTrackingState;
if (NUI_SKELETON_TRACKED == trackingState)
{
// We're tracking the skeleton, draw it
DrawSkeleton(skeletonFrame.SkeletonData, width, height);
}
else if (NUI_SKELETON_POSITION_ONLY == trackingState)
{
// we've only received the center point of the skeleton, draw that
D2D1_ELLIPSE ellipse = D2D1::Ellipse(
SkeletonToScreen(skeletonFrame.SkeletonData.Position, width, height),
g_JointThickness,
g_JointThickness
);
m_pRenderTarget->DrawEllipse(ellipse, m_pBrushJointTracked);
}
}
hr = m_pRenderTarget->EndDraw();
// Device lost, need to recreate the render target
// We'll dispose it now and retry drawing
if (D2DERR_RECREATE_TARGET == hr)
{
hr = S_OK;
DiscardDirect2DResources();
}
}
///
/// Draws a skeleton
///
/// skeleton to draw
/// width (in pixels) of output buffer
/// height (in pixels) of output buffer
void CSkeletonBasics::DrawSkeleton(const NUI_SKELETON_DATA & skel, int windowWidth, int windowHeight)
{
int i;
for (i = 0; i < NUI_SKELETON_POSITION_COUNT; ++i)
{
m_Points = SkeletonToScreen(skel.SkeletonPositions, windowWidth, windowHeight);
}
// Render Torso
DrawBone(skel, NUI_SKELETON_POSITION_HEAD, NUI_SKELETON_POSITION_SHOULDER_CENTER);
DrawBone(skel, NUI_SKELETON_POSITION_SHOULDER_CENTER, NUI_SKELETON_POSITION_SHOULDER_LEFT);
DrawBone(skel, NUI_SKELETON_POSITION_SHOULDER_CENTER, NUI_SKELETON_POSITION_SHOULDER_RIGHT);
DrawBone(skel, NUI_SKELETON_POSITION_SHOULDER_CENTER, NUI_SKELETON_POSITION_SPINE);
DrawBone(skel, NUI_SKELETON_POSITION_SPINE, NUI_SKELETON_POSITION_HIP_CENTER);
DrawBone(skel, NUI_SKELETON_POSITION_HIP_CENTER, NUI_SKELETON_POSITION_HIP_LEFT);
DrawBone(skel, NUI_SKELETON_POSITION_HIP_CENTER, NUI_SKELETON_POSITION_HIP_RIGHT);
// Left ARM
DrawBone(skel, NUI_SKELETON_POSITION_SHOULDER_LEFT, NUI_SKELETON_POSITION_ELBOW_LEFT);
DrawBone(skel, NUI_SKELETON_POSITION_ELBOW_LEFT, NUI_SKELETON_POSITION_WRIST_LEFT);
DrawBone(skel, NUI_SKELETON_POSITION_WRIST_LEFT, NUI_SKELETON_POSITION_HAND_LEFT);
// Right Arm
DrawBone(skel, NUI_SKELETON_POSITION_SHOULDER_RIGHT, NUI_SKELETON_POSITION_ELBOW_RIGHT);
DrawBone(skel, NUI_SKELETON_POSITION_ELBOW_RIGHT, NUI_SKELETON_POSITION_WRIST_RIGHT);
DrawBone(skel, NUI_SKELETON_POSITION_WRIST_RIGHT, NUI_SKELETON_POSITION_HAND_RIGHT);
// Left Leg
DrawBone(skel, NUI_SKELETON_POSITION_HIP_LEFT, NUI_SKELETON_POSITION_KNEE_LEFT);
DrawBone(skel, NUI_SKELETON_POSITION_KNEE_LEFT, NUI_SKELETON_POSITION_ANKLE_LEFT);
DrawBone(skel, NUI_SKELETON_POSITION_ANKLE_LEFT, NUI_SKELETON_POSITION_FOOT_LEFT);
// Right Leg
DrawBone(skel, NUI_SKELETON_POSITION_HIP_RIGHT, NUI_SKELETON_POSITION_KNEE_RIGHT);
DrawBone(skel, NUI_SKELETON_POSITION_KNEE_RIGHT, NUI_SKELETON_POSITION_ANKLE_RIGHT);
DrawBone(skel, NUI_SKELETON_POSITION_ANKLE_RIGHT, NUI_SKELETON_POSITION_FOOT_RIGHT);
// Draw the joints in a different color
for (i = 0; i < NUI_SKELETON_POSITION_COUNT; ++i)
{
D2D1_ELLIPSE ellipse = D2D1::Ellipse( m_Points, g_JointThickness, g_JointThickness );
if ( skel.eSkeletonPositionTrackingState == NUI_SKELETON_POSITION_INFERRED )
{
m_pRenderTarget->DrawEllipse(ellipse, m_pBrushJointInferred);
}
else if ( skel.eSkeletonPositionTrackingState == NUI_SKELETON_POSITION_TRACKED )
{
m_pRenderTarget->DrawEllipse(ellipse, m_pBrushJointTracked);
}
}
}
///
/// Draws a bone line between two joints
///
/// skeleton to draw bones from
/// joint to start drawing from
/// joint to end drawing at
void CSkeletonBasics::DrawBone(const NUI_SKELETON_DATA & skel, NUI_SKELETON_POSITION_INDEX joint0, NUI_SKELETON_POSITION_INDEX joint1)
{
NUI_SKELETON_POSITION_TRACKING_STATE joint0State = skel.eSkeletonPositionTrackingState[joint0];
NUI_SKELETON_POSITION_TRACKING_STATE joint1State = skel.eSkeletonPositionTrackingState[joint1];
// If we can't find either of these joints, exit
if (joint0State == NUI_SKELETON_POSITION_NOT_TRACKED || joint1State == NUI_SKELETON_POSITION_NOT_TRACKED)
{
return;
}
// Don't draw if both points are inferred
if (joint0State == NUI_SKELETON_POSITION_INFERRED && joint1State == NUI_SKELETON_POSITION_INFERRED)
{
return;
}
// We assume all drawn bones are inferred unless BOTH joints are tracked
if (joint0State == NUI_SKELETON_POSITION_TRACKED && joint1State == NUI_SKELETON_POSITION_TRACKED)
{
m_pRenderTarget->DrawLine(m_Points[joint0], m_Points[joint1], m_pBrushBoneTracked, g_TrackedBoneThickness);
}
else
{
m_pRenderTarget->DrawLine(m_Points[joint0], m_Points[joint1], m_pBrushBoneInferred, g_InferredBoneThickness);
}
}
///
/// Converts a skeleton point to screen space
///
/// skeleton point to tranform
/// width (in pixels) of output buffer
/// height (in pixels) of output buffer
/// point in screen-space
D2D1_POINT_2F CSkeletonBasics::SkeletonToScreen(Vector4 skeletonPoint, int width, int height)
{
LONG x, y;
USHORT depth;
// Calculate the skeleton's position on the screen
// NuiTransformSkeletonToDepthImage returns coordinates in NUI_IMAGE_RESOLUTION_320x240 space
NuiTransformSkeletonToDepthImage(skeletonPoint, &x, &y, &depth);
float screenPointX = static_cast(x * width) / cScreenWidth;
float screenPointY = static_cast(y * height) / cScreenHeight;
return D2D1::Point2F(screenPointX, screenPointY);
}
///
/// Ensure necessary Direct2d resources are created
///
/// S_OK if successful, otherwise an error code
HRESULT CSkeletonBasics::EnsureDirect2DResources()
{
HRESULT hr = S_OK;
// If there isn't currently a render target, we need to create one
if (NULL == m_pRenderTarget)
{
RECT rc;
GetWindowRect( GetDlgItem( m_hWnd, IDC_VIDEOVIEW ), &rc );
int width = rc.right - rc.left;
int height = rc.bottom - rc.top;
D2D1_SIZE_U size = D2D1::SizeU( width, height );
D2D1_RENDER_TARGET_PROPERTIES rtProps = D2D1::RenderTargetProperties();
rtProps.pixelFormat = D2D1::PixelFormat( DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE);
rtProps.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
// Create a Hwnd render target, in order to render to the window set in initialize
hr = m_pD2DFactory->CreateHwndRenderTarget(
rtProps,
D2D1::HwndRenderTargetProperties(GetDlgItem( m_hWnd, IDC_VIDEOVIEW), size),
&m_pRenderTarget
);
if ( FAILED(hr) )
{
SetStatusMessage(L"Couldn't create Direct2D render target!");
return hr;
}
//light green
m_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(0.27f, 0.75f, 0.27f), &m_pBrushJointTracked);
m_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Yellow, 1.0f), &m_pBrushJointInferred);
m_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Green, 1.0f), &m_pBrushBoneTracked);
m_pRenderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Gray, 1.0f), &m_pBrushBoneInferred);
}
return hr;
}
///
/// Dispose Direct2d resources
///
void CSkeletonBasics::DiscardDirect2DResources( )
{
SafeRelease(m_pRenderTarget);
SafeRelease(m_pBrushJointTracked);
SafeRelease(m_pBrushJointInferred);
SafeRelease(m_pBrushBoneTracked);
SafeRelease(m_pBrushBoneInferred);
}
///
/// Set the status bar message
///
/// message to display
void CSkeletonBasics::SetStatusMessage(WCHAR * szMessage)
{
SendDlgItemMessageW(m_hWnd, IDC_STATUS, WM_SETTEXT, 0, (LPARAM)szMessage);
}
二.实测效果图:(这是有名的投降姿势矫正,哈哈~!)
三.例程工程分享:
http://pan.baidu.com/s/1c1S4Pkk