Kinect for Windows SDK v2.0 開發筆記 (五)骨骼幀與笑面男

來源:互聯網
上載者:User

(轉載請註明出處)

使用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代那樣設定平滑參數,也說明精度的提高。

為了類比原作的抖動,只能自己手動類比了。



範例下載地址:點擊這裡

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.