• 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。

  • 相关阅读:
    【Jmeter】 Report Dashboard 生成html图形测试报告
    【Python】更优的字符串格式化方式 -- "format"替代"%s"
    【Jmeter】压测mysql数据库中间件mycat
    UTF-8文件的Unicode签名BOM(Byte Order Mark)问题记录(EF BB BF)
    【Python】常用排序算法的python实现和性能分析
    【Python】模块学习之Timer定时任务,递归定时自调获取博客浏览量
    博客园文章添加阅读目录及CSS样式的方法总结
    【Python】解决Python脚本 在cmd命令行窗口运行时,中文乱码问题
    【Python】模块学习之locust性能测试
    【Python】模块学习之使用paramiko连接Linux,远程执行命令,上传下载、文件
  • 原文地址:https://www.cnblogs.com/new0801/p/6177196.html
Copyright © 2020-2023  润新知