Kinect學(xué)習(xí)(1)——DepthBasics-D2D

參考:DepthBasics-D2D詳解之一DepthBasics-D2D詳解之二DepthBasics-D2D詳解之三
這是一個(gè)對(duì)Kinect深度獲取程序的解析,Kinect 2.0的程序和它差不多。

DepthBasics-D2D工程就兩個(gè)源代碼:DepthBasics.cpp和ImageRenderer.cpp。其中,DepthBasics.cpp里的第一個(gè)wWinMain函數(shù)就是主函數(shù)。wWinMain函數(shù)主體是一個(gè)CDepthBasics類(lèi)對(duì)象的創(chuàng)建和它的Run函數(shù):

    CDepthBasics application;
    application.Run(hInstance, nShowCmd);

CDepthBasics類(lèi)被創(chuàng)建時(shí)執(zhí)行它的同名構(gòu)造函數(shù)進(jìn)行初始化,略。

轉(zhuǎn)到Run函數(shù)實(shí)體,前半部分在創(chuàng)建和顯示窗口,后半部分是一個(gè)對(duì)Update函數(shù)的while循環(huán)。注意,創(chuàng)建窗口的CreateDialogParamW承擔(dān)了初始化參數(shù)的作用。所以,Run函數(shù)就是一個(gè)不停更新(獲取和顯示)深度數(shù)據(jù)的過(guò)程。

    // Main message loop
    while (WM_QUIT != msg.message)
    {
        Update();

        while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE))
        {
            // If a dialog message will be taken care of by the dialog proc
            if (hWndApp && IsDialogMessageW(hWndApp, &msg))
            {
                continue;
            }

            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }

下面看Update函數(shù),已經(jīng)寫(xiě)了部分注釋?zhuān)容^好理解:

void CDepthBasics::Update()
{
    // 如果m_pDepthFrameReader==NULL,說(shuō)明沒(méi)初始化好sensor。
    // CDepthBasics初始化m_pDepthFrameReader為NULL,CreateDialogParamW的初始化包括了對(duì)sensor的初始化,具體來(lái)說(shuō)是在InitializeDefaultSensor內(nèi)。
    if (!m_pDepthFrameReader) 
    {
        return;
    }

    IDepthFrame* pDepthFrame = NULL;

    // pDepthFrame獲取當(dāng)前幀
    HRESULT hr = m_pDepthFrameReader->AcquireLatestFrame(&pDepthFrame);

    if (SUCCEEDED(hr))
    {
        INT64 nTime = 0;
        IFrameDescription* pFrameDescription = NULL;
        int nWidth = 0;
        int nHeight = 0;
        USHORT nDepthMinReliableDistance = 0;
        USHORT nDepthMaxDistance = 0;
        UINT nBufferSize = 0;
        UINT16 *pBuffer = NULL;

        hr = pDepthFrame->get_RelativeTime(&nTime); // 當(dāng)前時(shí)間

        // 獲取幀的寬和高 nWidth=512,nHeight=424
        if (SUCCEEDED(hr))
        {
            hr = pDepthFrame->get_FrameDescription(&pFrameDescription);
        }

        if (SUCCEEDED(hr))
        {
            hr = pFrameDescription->get_Width(&nWidth);
        }

        if (SUCCEEDED(hr))
        {
            hr = pFrameDescription->get_Height(&nHeight);
        }

        // 獲取深度的最小可靠距離,結(jié)果nDepthMinReliableDistance=500,應(yīng)該就是0.5m
        if (SUCCEEDED(hr))
        {
            hr = pDepthFrame->get_DepthMinReliableDistance(&nDepthMinReliableDistance);
        }

        // 獲取深度的最大距離
        if (SUCCEEDED(hr))
        {
            // In order to see the full range of depth (including the less reliable far field depth)
            // we are setting nDepthMaxDistance to the extreme potential depth threshold
            // 翻譯:為了看到全范圍的深度(包括較不可靠的遠(yuǎn)場(chǎng)深度),我們將nDepthMaxDistance設(shè)置為極端潛在深度閾值
            // USHRT_MAX=0xffff
            nDepthMaxDistance = USHRT_MAX;

            // Note:  If you wish to filter by reliable depth distance, uncomment the following line.
            // 翻譯:如果您想進(jìn)行可靠的深度過(guò)濾,取消下行的注釋。
            // 如果用下行函數(shù)獲取最大可靠深度的話(huà),結(jié)果nDepthMaxDistance=4500,也就是4.5m
            //// hr = pDepthFrame->get_DepthMaxReliableDistance(&nDepthMaxDistance);
        }

        // 獲取深度幀數(shù)據(jù)的指針。
        if (SUCCEEDED(hr))
        {
            hr = pDepthFrame->AccessUnderlyingBuffer(&nBufferSize, &pBuffer);            
        }

        // 處理深度數(shù)據(jù)
        if (SUCCEEDED(hr))
        {
            ProcessDepth(nTime, pBuffer, nWidth, nHeight, nDepthMinReliableDistance, nDepthMaxDistance);
        }

        SafeRelease(pFrameDescription);
    }

    SafeRelease(pDepthFrame);
}

顯然最重要的部分在ProcessDepth內(nèi),下面是ProcessDepth的代碼:

void CDepthBasics::ProcessDepth(INT64 nTime, const UINT16* pBuffer, int nWidth, int nHeight, USHORT nMinDepth, USHORT nMaxDepth)
{
    if (m_hWnd)
    {
        if (!m_nStartTime)
        {
            m_nStartTime = nTime; // 初次進(jìn)入的時(shí)候把剛才用get_RelativeTime獲取的nTime當(dāng)作開(kāi)始時(shí)間m_nStartTime,之后不再改變m_nStartTime
        }

        // 以下在計(jì)算和顯示幀率,時(shí)間
        double fps = 0.0;

        LARGE_INTEGER qpcNow = {0};
        if (m_fFreq)
        {
            if (QueryPerformanceCounter(&qpcNow))
            {
                if (m_nLastCounter)
                {
                    m_nFramesSinceUpdate++;
                    fps = m_fFreq * m_nFramesSinceUpdate / double(qpcNow.QuadPart - m_nLastCounter);
                }
            }
        }

        WCHAR szStatusMessage[64];
        StringCchPrintf(szStatusMessage, _countof(szStatusMessage), L" FPS = %0.2f    Time = %I64d", fps, (nTime - m_nStartTime));

        if (SetStatusMessage(szStatusMessage, 1000, false))
        {
            m_nLastCounter = qpcNow.QuadPart;
            m_nFramesSinceUpdate = 0;
        }
    }

    // Make sure we've received valid data 確定接收到了有效數(shù)據(jù)
    // m_pDepthRGBX是在最開(kāi)始的構(gòu)造函數(shù)CDepthBasics中初始化的RGBX(RGBQUAD)存儲(chǔ)空間,大小為512x424,每個(gè)像素內(nèi)按B、G、R、reserved排列,都是8位Byte型,因此一個(gè)像素占32位(4 Byte)
    // pBuffer是存儲(chǔ)深度數(shù)據(jù)的指針,指向第一個(gè)深度數(shù)據(jù)
    // (nWidth == cDepthWidth) && (nHeight == cDepthHeight) 是檢測(cè)讀取的數(shù)據(jù)尺寸對(duì)不對(duì)
    if (m_pDepthRGBX && pBuffer && (nWidth == cDepthWidth) && (nHeight == cDepthHeight))
    {
        RGBQUAD* pRGBX = m_pDepthRGBX;

        // end pixel is start + width*height - 1 
        // pBufferEnd是末深度數(shù)據(jù)地址+1
        const UINT16* pBufferEnd = pBuffer + (nWidth * nHeight);

        while (pBuffer < pBufferEnd) //從第一個(gè)深度數(shù)據(jù)到最后一個(gè)深度數(shù)據(jù)開(kāi)始遍歷
        {
            USHORT depth = *pBuffer; // depth指向第一個(gè)深度數(shù)據(jù)

            // To convert to a byte, we're discarding the most-significant
            // rather than least-significant bits.
            // We're preserving detail, although the intensity will "wrap."
            // Values outside the reliable depth range are mapped to 0 (black).

            // Note: Using conditionals in this loop could degrade performance.
            // Consider using a lookup table instead when writing production code.

            // 由于深度數(shù)據(jù)是UINT16型,即16位無(wú)符號(hào)整型,但像素每通道只有1 Byte(8位),因此要舍去8位。
            // 本程序舍去高8位,也就是保留深度細(xì)節(jié),最大顯示255深度,重復(fù)更大的距離。depth % 256等價(jià)于depth>>8,即右移8位,可以通過(guò)改變移動(dòng)的位數(shù)調(diào)整精度和表示的深度范圍。
            // 可靠深度范圍之外的深度會(huì)映射成0(黑色)。
            // 條件判斷語(yǔ)句會(huì)影響性能,建議工業(yè)級(jí)代碼使用查表法。
            BYTE intensity = static_cast<BYTE>((depth >= nMinDepth) && (depth <= nMaxDepth) ? (depth % 256) : 0);

            // R、G、B通道都賦給處理后的深度值,R、G、B像素值相同時(shí)顯示的就是灰度圖
            pRGBX->rgbRed   = intensity;
            pRGBX->rgbGreen = intensity;
            pRGBX->rgbBlue  = intensity;

            ++pRGBX;
            ++pBuffer;
        }

        // Draw the data with Direct2D 繪制、顯示深度圖
        m_pDrawDepth->Draw(reinterpret_cast<BYTE*>(m_pDepthRGBX), cDepthWidth * cDepthHeight * sizeof(RGBQUAD));

        // 存儲(chǔ)深度圖,保存為bmp文件
        if (m_bSaveScreenshot)
        {
            WCHAR szScreenshotPath[MAX_PATH];

            // Retrieve the path to My Photos
            GetScreenshotFileName(szScreenshotPath, _countof(szScreenshotPath));

            // Write out the bitmap to disk
            HRESULT hr = SaveBitmapToFile(reinterpret_cast<BYTE*>(m_pDepthRGBX), nWidth, nHeight, sizeof(RGBQUAD) * 8, szScreenshotPath);

            WCHAR szStatusMessage[64 + MAX_PATH];
            if (SUCCEEDED(hr))
            {
                // Set the status bar to show where the screenshot was saved
                StringCchPrintf(szStatusMessage, _countof(szStatusMessage), L"Screenshot saved to %s", szScreenshotPath);
            }
            else
            {
                StringCchPrintf(szStatusMessage, _countof(szStatusMessage), L"Failed to write screenshot to %s", szScreenshotPath);
            }

            SetStatusMessage(szStatusMessage, 5000, true);

            // toggle off so we don't save a screenshot again next frame
            m_bSaveScreenshot = false;
        }
    }
}

暫時(shí)先看這些,大致了解過(guò)程和原理,有需要再有針對(duì)性地詳細(xì)研究。:)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容