Получение карты глубины с использованием Kinect SDK / Kinect / Recog.ru - Распознавание образов для программистов


Получение карты глубины с использованием Kinect SDK

Первым приложением, которое хочется сделать при помощи Kinect — это то, для чего в основном сенсор и предназначен. Получение карты глубины. Для этого напишем следующий пример (предполагается, что Kinect SDK установлен и естественно есть сам сенсор). Пример построен на основе DepthBasics-D2D, только выброшено все лишнее.

#include "opencv2/core/core_c.h"
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/highgui/highgui_c.h"
#include "NuiApi.h"

static const int        cDepthWidth  = 640;
static const int        cDepthHeight = 480;
static const int        cBytesPerPixel = 1;

INuiSensor*             m_pNuiSensor = NULL;
HANDLE                  m_pDepthStreamHandle = NULL;
HANDLE                  m_hNextDepthFrameEvent = INVALID_HANDLE_VALUE;
BYTE*  m_depthRGBX;

HRESULT CreateFirstConnected();
void GetDepth();

int main()
{	
	m_depthRGBX = new BYTE[cDepthWidth*cDepthHeight*cBytesPerPixel];

	// Коннектимся к кинекту
    CreateFirstConnected();

 
	// Получаем глубину
	const int eventCount = 1;
    HANDLE hEvents[eventCount];
	hEvents[0] = m_hNextDepthFrameEvent;
	DWORD dwEvent = MsgWaitForMultipleObjects(eventCount, hEvents, FALSE, INFINITE, QS_ALLINPUT);

	if ( WAIT_OBJECT_0 == WaitForSingleObject(m_hNextDepthFrameEvent, 0) )
	{
		GetDepth();
	}

	// Закрытие
	if (m_pNuiSensor)
    {
        m_pNuiSensor->NuiShutdown();
    }

    if (m_hNextDepthFrameEvent != INVALID_HANDLE_VALUE)
    {
        CloseHandle(m_hNextDepthFrameEvent);
    }    

	// Переводим в формат OpenCV
	IplImage* Image = cvCreateImage( cvSize( 640, 480 ), 8, 1 );

	memcpy( Image->imageData,  m_depthRGBX, cDepthWidth*cDepthHeight*cBytesPerPixel );

	cvSaveImage( "out.png", Image );
    // done with depth pixel data
    delete[] m_depthRGBX;
	cvReleaseImage( &Image );
    
    m_pNuiSensor->Release();


	return 0;
}


HRESULT CreateFirstConnected()
{
    INuiSensor * pNuiSensor;
    HRESULT hr;

    int iSensorCount = 0;
    hr = NuiGetSensorCount(&iSensorCount);
    if (FAILED(hr))
    {
        return hr;
    }

    // Look at each Kinect sensor
    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)
    {
        // Initialize the Kinect and specify that we'll be using depth
        hr = m_pNuiSensor->NuiInitialize(NUI_INITIALIZE_FLAG_USES_DEPTH); 
        if (SUCCEEDED(hr))
        {
            // Create an event that will be signaled when depth data is available
            m_hNextDepthFrameEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

            // Open a depth image stream to receive depth frames
            hr = m_pNuiSensor->NuiImageStreamOpen(
                NUI_IMAGE_TYPE_DEPTH,
                NUI_IMAGE_RESOLUTION_640x480,
                0,
                2,
                m_hNextDepthFrameEvent,
                &m_pDepthStreamHandle);
        }
    }

    if (NULL == m_pNuiSensor || FAILED(hr))
    {        
        return E_FAIL;
    }

    return hr;
}



void GetDepth()
{
    HRESULT hr;
    NUI_IMAGE_FRAME imageFrame;

    // Attempt to get the depth frame
    hr = m_pNuiSensor->NuiImageStreamGetNextFrame(m_pDepthStreamHandle, 0, &imageFrame);
    if (FAILED(hr))
    {
        return;
    }

    BOOL nearMode;
    INuiFrameTexture* pTexture;

    // Get the depth image pixel texture
    hr = m_pNuiSensor->NuiImageFrameGetDepthImagePixelFrameTexture(
        m_pDepthStreamHandle, &imageFrame, &nearMode, &pTexture);
    if (FAILED(hr))
    {
        goto ReleaseFrame;
    }

    NUI_LOCKED_RECT LockedRect;

    // Lock the frame data so the Kinect knows not to modify it while we're reading it
    pTexture->LockRect(0, &LockedRect, NULL, 0);

    // Make sure we've received valid data
    if (LockedRect.Pitch != 0)
    {
        // Get the min and max reliable depth for the current frame
        int minDepth = (nearMode ? NUI_IMAGE_DEPTH_MINIMUM_NEAR_MODE : NUI_IMAGE_DEPTH_MINIMUM) >> NUI_IMAGE_PLAYER_INDEX_SHIFT;
        int maxDepth = (nearMode ? NUI_IMAGE_DEPTH_MAXIMUM_NEAR_MODE : NUI_IMAGE_DEPTH_MAXIMUM) >> NUI_IMAGE_PLAYER_INDEX_SHIFT;

        BYTE * rgbrun = m_depthRGBX;
        const NUI_DEPTH_IMAGE_PIXEL * pBufferRun = reinterpret_cast<const NUI_DEPTH_IMAGE_PIXEL *>(LockedRect.pBits);

        // end pixel is start + width*height - 1
        const NUI_DEPTH_IMAGE_PIXEL * pBufferEnd = pBufferRun + (cDepthWidth * cDepthHeight);

        while ( pBufferRun < pBufferEnd )
        {
            // discard the portion of the depth that contains only the player index
            USHORT depth = pBufferRun->depth;
            
            BYTE intensity = static_cast<BYTE>(depth >= minDepth && depth <= maxDepth ? depth % 256 : 0);

            // Write out blue byte
            *(rgbrun++) = intensity;

            // Increment our index into the Kinect's depth buffer
            ++pBufferRun;
        }
        
    }

    // We're done with the texture so unlock it
    pTexture->UnlockRect(0);

    pTexture->Release();

ReleaseFrame:
    // Release the frame
    m_pNuiSensor->NuiImageStreamReleaseFrame(m_pDepthStreamHandle, &imageFrame);
}


Первоначально вызывается функция CreateFirstConnected для подключения к Кинекту и проведения инициализации. Затем ожидаем события m_hNextDepthFrameEvent, которое говорит о том, что сенсор сканировал глубину. Для того, чтобы прочитать глубину вызывается функция GetDepth(). Ну а затем создается изображение OpenCV (сделано просто для упрощения сохранения изображения в файл) и сохраняется. В конце осуществлено освобождение ресурсов.
У меня программа выдала следующий файл out.png:
  • 0
  • 11 января 2013, 18:49
  • vidikon

Комментарии (2)

RSS свернуть / развернуть
+
0
А в чем преимущество такого метода перед использованием OpenNI и стандартных средств OpenCV?
Там все короче решается через:
cv::VideoCapture cap(CV_CAP_OPENNI);
cap.grab();
cap.retrieve( depthMap, CV_16UC1 );
cap.retrieve( bgrImage, CV_32FC1 );
Или при таком подходе Depth Map чем-то отличается?
avatar

BelBES

  • 22 марта 2013, 14:52
+
0
Да в общем не думаю, чтобы в чем-то было преимущество. Но в данном случае речь идет об использовании Kinect SDK, а не OpenCV. OpenCV просто для того, чтобы картинку выводить — лень было по-другому делать.
avatar

vidikon

  • 22 марта 2013, 15:44

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.