參考: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ì)研究。:)