zoukankan      html  css  js  c++  java
  • kinect for windows

    首先看骨骼追踪例子代码的结构:


    例子代码不是很多,在SkeletonBasics.cpp中,大部分的功能都在kinect SDK中实现,应用开发只需要了解接口,并调用它的接口即可。首先我们找到main函数:

    int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
    {
        CSkeletonBasics application;
        application.Run(hInstance, nCmdShow);
    }

    这个main函数是不是似曾相识,跟深度图的main函数差不多的,只是构造一个对象,然后调用这个对象的Run接口,构造函数只是初始化成员,所以我们直接看Run的实现:

    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<LPARAM>(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
            DWORD dwEvent = MsgWaitForMultipleObjects(eventCount, hEvents, FALSE, INFINITE, QS_ALLINPUT);
    
            // Check if this is an event we're waiting on and not a timeout or message
            if (WAIT_OBJECT_0 == dwEvent)
            {
                Update();
            }
    
            if (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<int>(msg.wParam);
    }

    这个Run函数是不是也是很熟悉,跟深度图的Run函数也是差不多,创建对话框,然后进入消息处理死循环,在死循环里处理窗口消息和kinect消息。

    接下来和深度图一样,我们也进入到窗口初始化消息里,看整个程序的初始化是怎么样的:

    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;

    初始化处理和深度图也差不多,D2D1CreateFactory函数创建DirectX对象,然后调用CreateFirstConnected去创建kinect对象,在CreateFirstConnected函数里的处理又有何不同呢?我们一起来看代码:

    HRESULT CSkeletonBasics::CreateFirstConnected()
    {
        INuiSensor * pNuiSensor;
    
    	// 获取kinect数量
        int iSensorCount = 0;
        HRESULT hr = NuiGetSensorCount(&iSensorCount);
        if (FAILED(hr))
        {
            return hr;
        }
    
        // Look at each Kinect sensor  查找一个有效的kinect设备
        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)
        {
            // 这里的参数和深度图不一样,深度图用的参数是NUI_INITIALIZE_FLAG_USES_DEPTH
    		// 这个意思就是将这个kinect初始化成骨骼跟踪
            hr = m_pNuiSensor->NuiInitialize(NUI_INITIALIZE_FLAG_USES_SKELETON); 
            if (SUCCEEDED(hr))
            {
                // 这个Event和深度图一样,是kinectSDK和上层应用程序通信的事件
                m_hNextSkeletonEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
    
                // 打开一个骨骼跟踪数据,深度图需要的参数比较复杂,这个比较简答
                hr = m_pNuiSensor->NuiSkeletonTrackingEnable(m_hNextSkeletonEvent, 0); 
            }
        }
    
        if (NULL == m_pNuiSensor || FAILED(hr))
        {
            SetStatusMessage(L"No ready Kinect found!");
            return E_FAIL;
        }
    
        return hr;
    }

    看初始化kinect设备的代码,貌似区别也不是很大,只是初始化参数不同,打开数据流的函数不同,框架性的代码还是差不多的。

    当这个m_hNextSkeletonEvent事件被注册到kinect SDK库之后,当有数据来时,Update函数会被调用,Update函数最后调用了ProcessSkeleton函数来处理骨骼数据:

    void CSkeletonBasics::ProcessSkeleton()
    {
        NUI_SKELETON_FRAME skeletonFrame = {0};
    
    	// 获取一帧骨骼数据
        HRESULT hr = m_pNuiSensor->NuiSkeletonGetNextFrame(0, &skeletonFrame);
        if ( FAILED(hr) )
        {
            return;
        }
    
        // 让骨骼数据变得平滑,消除抖动现象
        m_pNuiSensor->NuiTransformSmooth(&skeletonFrame, NULL);
    
        // 创建m_pRenderTarget对象
        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;
    
    	// 最多支持6个人的骨骼跟踪数据
        for (int i = 0 ; i < NUI_SKELETON_COUNT; ++i)
        {
            NUI_SKELETON_TRACKING_STATE trackingState = skeletonFrame.SkeletonData[i].eTrackingState;
    
            if (NUI_SKELETON_TRACKED == trackingState)
            {
                // 画骨骼
                DrawSkeleton(skeletonFrame.SkeletonData[i], width, height);
            }
            else if (NUI_SKELETON_POSITION_ONLY == trackingState)
            {
                // 只是跟踪位置的话,那么就画个位置,在人很多时,kinect只跟踪两个人的骨骼,其他人之显示位置
                D2D1_ELLIPSE ellipse = D2D1::Ellipse(
                    SkeletonToScreen(skeletonFrame.SkeletonData[i].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();
        }
    }

    在处理骨骼数据函数中,对数据skeletonFrame中保存的六个骨骼跟踪数据循环处理,如果状态是NUI_SKELETON_TRACKED则通过DrawSkeleton画详细骨骼信息,如果是NUI_SKELETON_POSITION_ONLY则只画位置信息,如果不是这两个状态,则啥也不做。接下来我们来看看DrawSkeleton看看骨骼信息怎么画的:

    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[i] = SkeletonToScreen(skel.SkeletonPositions[i], windowWidth, windowHeight);
        }
    
        // 画骨骼,参数1是骨骼数据,参数2和参数3是关节
        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);
    
        // 画关节
        for (i = 0; i < NUI_SKELETON_POSITION_COUNT; ++i)
        {
            D2D1_ELLIPSE ellipse = D2D1::Ellipse( m_Points[i], g_JointThickness, g_JointThickness );
    
            if ( skel.eSkeletonPositionTrackingState[i] == NUI_SKELETON_POSITION_INFERRED )
            {
                m_pRenderTarget->DrawEllipse(ellipse, m_pBrushJointInferred);
            }
            else if ( skel.eSkeletonPositionTrackingState[i] == NUI_SKELETON_POSITION_TRACKED )
            {
                m_pRenderTarget->DrawEllipse(ellipse, m_pBrushJointTracked);
            }
        }
    }

    在这里绘制比较简单,所以这里绘制并没有像深度图一样使用一个类来做绘制,直接写到函数里了。另外一个这里DirectX的绘制也就只是画画线,如果你熟悉GDI,完全可以通过GDI来绘图,而不用DirectX。

  • 相关阅读:
    记第一场省选
    POJ 2083 Fractal 分形
    CodeForces 605A Sorting Railway Cars 思维
    FZU 1896 神奇的魔法数 dp
    FZU 1893 内存管理 模拟
    FZU 1894 志愿者选拔 单调队列
    FZU 1920 Left Mouse Button 简单搜索
    FZU 2086 餐厅点餐
    poj 2299 Ultra-QuickSort 逆序对模版题
    COMP9313 week4a MapReduce
  • 原文地址:https://www.cnblogs.com/new0801/p/6177196.html
Copyright © 2011-2022 走看看