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


Получение скелета с помощью Kinect SDK

С помощью Kinect SDK получение скелета человека является простой задачей. Ниже на рисунке то, что планируется получить. Для простейшей демонстрации помимо скелета будет определяться подняты ли вверх руки. В Kinect SDK уже есть пример получения скелета, который вы можете посмотреть.

Здесь будет реализовано консольное приложение, как и в двух предыдущих примерах:
http://recog.ru/blog/kinect/93.html
http://recog.ru/blog/kinect/94.html
Поскольку в этих примерах часть информации есть, то эта статья ограничится изменениями без приведения полного листинга.
Начальный момент – это инициализация. Для скелета при инициализации добавляется флаг NUI_INITIALIZE_FLAG_USES_SKELETON:
hr = m_pNuiSensor->NuiInitialize(NUI_INITIALIZE_FLAG_USES_DEPTH_AND_PLAYER_INDEX | NUI_INITIALIZE_FLAG_USES_SKELETON);  

Дальше как обычно создается событие
m_hNextSkeletonEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
if ( HasSkeletalEngine( m_pNuiSensor ) ) // Поддерживает ли сенсор скелет
{
	hr = m_pNuiSensor->NuiSkeletonTrackingEnable( m_hNextSkeletonEvent, m_SkeletonTrackingFlags );
	if( FAILED( hr ) )
	{					
		return hr;
	}
}

Получение глубины остается тем же (ее с меньшим разрешением выводим на экран справа сверху). Для получения скелета при событии if ( WAIT_OBJECT_0 == WaitForSingleObject(m_hNextSkeletonEvent, 0) ) вызывается следующая функция.
bool GetSkeleton()
{
	NUI_SKELETON_FRAME SkeletonFrame = {0};

    bool foundSkeleton = false;

    if ( SUCCEEDED(m_pNuiSensor->NuiSkeletonGetNextFrame( 0, &SkeletonFrame )) )
    {
        for ( int i = 0 ; i < NUI_SKELETON_COUNT ; i++ )
        {
            NUI_SKELETON_TRACKING_STATE trackingState = SkeletonFrame.SkeletonData[i].eTrackingState;

            if ( trackingState == NUI_SKELETON_TRACKED || trackingState == NUI_SKELETON_POSITION_ONLY )
            {
                foundSkeleton = true;
            }
        }
    }

    // no skeletons!
    if( !foundSkeleton )
    {
        return true;
    }

    // smooth out the skeleton data
    HRESULT hr = m_pNuiSensor->NuiTransformSmooth(&SkeletonFrame,NULL);
    if ( FAILED(hr) )
    {
        return false;
    }

    // we found a skeleton, re-start the skeletal timer
    m_bScreenBlanked = false;    
   

    for ( int i = 0 ; i < NUI_SKELETON_COUNT; i++ )
    {
        NUI_SKELETON_TRACKING_STATE trackingState = SkeletonFrame.SkeletonData[i].eTrackingState;

        if ( trackingState == NUI_SKELETON_TRACKED )
        {
            // We're tracking the skeleton, draw it
            Nui_DrawSkeleton( SkeletonFrame.SkeletonData[i], ImageOut->width, ImageOut->height );
        }        
    }  

    UpdateTrackedSkeletons( SkeletonFrame );
    

	return 1;
}

Эта функция пытается получить скелет с сенсора, если удачно, то вызывается функция NuiTransformSmooth для уменьшения дрожания скелета между кадрами. После этого для каждого из найденных скелетов вызывается функция отображения на экран Nui_DrawSkeleton, а в конце UpdateTrackedSkeletons.
Nui_DrawSkeleton как видно из названия предназначена для рисования скелета, я привожу код вывода на изображение OpenCV без изменения:
void Nui_DrawBone( const NUI_SKELETON_DATA & skel, NUI_SKELETON_POSITION_INDEX bone0, NUI_SKELETON_POSITION_INDEX bone1 )
{
    NUI_SKELETON_POSITION_TRACKING_STATE bone0State = skel.eSkeletonPositionTrackingState[bone0];
    NUI_SKELETON_POSITION_TRACKING_STATE bone1State = skel.eSkeletonPositionTrackingState[bone1];

    // If we can't find either of these joints, exit
    if ( bone0State == NUI_SKELETON_POSITION_NOT_TRACKED || bone1State == NUI_SKELETON_POSITION_NOT_TRACKED )
    {
        return;
    }
    
    // Don't draw if both points are inferred
    if ( bone0State == NUI_SKELETON_POSITION_INFERRED && bone1State == NUI_SKELETON_POSITION_INFERRED )
    {
        return;
    }

    // We assume all drawn bones are inferred unless BOTH joints are tracked
    if ( bone0State == NUI_SKELETON_POSITION_TRACKED && bone1State == NUI_SKELETON_POSITION_TRACKED )
    {
		cvLine( ImageOut, cvPointFrom32f( m_Points[bone0]), cvPointFrom32f( m_Points[bone1]), CV_RGB(0,255,0), 2 );        
    }
    else
    {
        cvLine( ImageOut, cvPointFrom32f( m_Points[bone0]), cvPointFrom32f( m_Points[bone1]), CV_RGB(0,128,0), 2 );
    }
}

CvPoint2D32f SkeletonToScreen( Vector4 skeletonPoint, int width, int height )
{
    LONG x, y;
    USHORT depth;

    // calculate the skeleton's position on the screen
    // NuiTransformSkeletonToDepthImage returns coordinates in NUI_IMAGE_RESOLUTION_320x240 space
    NuiTransformSkeletonToDepthImage( skeletonPoint, &x, &y, &depth );

    float screenPointX = static_cast<float>(x * width) / g_ScreenWidth;
    float screenPointY = static_cast<float>(y * height) / g_ScreenHeight;

    return cvPoint2D32f(screenPointX, screenPointY);
}

/// <summary>
/// Draws a skeleton
/// </summary>
/// <param name="skel">skeleton to draw</param>
/// <param name="windowWidth">width (in pixels) of output buffer</param>
/// <param name="windowHeight">height (in pixels) of output buffer</param>
void Nui_DrawSkeleton( const NUI_SKELETON_DATA & skel, int windowWidth, int windowHeight )
{      
    int i;

	cvSet( ImageOut, cvScalar(0));
	cvSetImageROI( ImageOut, cvRect( 320, 0, 320, 240 ));
	cvCvtColor( Image, ImageOut, CV_GRAY2BGR );
	cvResetImageROI( ImageOut );
    for (i = 0; i < NUI_SKELETON_POSITION_COUNT; i++)
    {
        m_Points[i] = SkeletonToScreen( skel.SkeletonPositions[i], windowWidth, windowHeight );
    }

    // Render Torso
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_HEAD, NUI_SKELETON_POSITION_SHOULDER_CENTER );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_SHOULDER_CENTER, NUI_SKELETON_POSITION_SHOULDER_LEFT );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_SHOULDER_CENTER, NUI_SKELETON_POSITION_SHOULDER_RIGHT );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_SHOULDER_CENTER, NUI_SKELETON_POSITION_SPINE );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_SPINE, NUI_SKELETON_POSITION_HIP_CENTER );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_HIP_CENTER, NUI_SKELETON_POSITION_HIP_LEFT );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_HIP_CENTER, NUI_SKELETON_POSITION_HIP_RIGHT );

    // Left Arm
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_SHOULDER_LEFT, NUI_SKELETON_POSITION_ELBOW_LEFT );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_ELBOW_LEFT, NUI_SKELETON_POSITION_WRIST_LEFT );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_WRIST_LEFT, NUI_SKELETON_POSITION_HAND_LEFT );

    // Right Arm
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_SHOULDER_RIGHT, NUI_SKELETON_POSITION_ELBOW_RIGHT );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_ELBOW_RIGHT, NUI_SKELETON_POSITION_WRIST_RIGHT );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_WRIST_RIGHT, NUI_SKELETON_POSITION_HAND_RIGHT );

    // Left Leg
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_HIP_LEFT, NUI_SKELETON_POSITION_KNEE_LEFT );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_KNEE_LEFT, NUI_SKELETON_POSITION_ANKLE_LEFT );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_ANKLE_LEFT, NUI_SKELETON_POSITION_FOOT_LEFT );

    // Right Leg
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_HIP_RIGHT, NUI_SKELETON_POSITION_KNEE_RIGHT );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_KNEE_RIGHT, NUI_SKELETON_POSITION_ANKLE_RIGHT );
    Nui_DrawBone( skel, NUI_SKELETON_POSITION_ANKLE_RIGHT, NUI_SKELETON_POSITION_FOOT_RIGHT );
    
    // Draw the joints in a different color
    for ( i = 0; i < NUI_SKELETON_POSITION_COUNT; i++ )
    {        

        if ( skel.eSkeletonPositionTrackingState[i] == NUI_SKELETON_POSITION_INFERRED )
        {            
			cvCircle( ImageOut,  cvPointFrom32f( m_Points[i]), 5, CV_RGB(255,0,0), 3 );
        }
        else if ( skel.eSkeletonPositionTrackingState[i] == NUI_SKELETON_POSITION_TRACKED )
        {
            cvCircle( ImageOut,  cvPointFrom32f( m_Points[i]), 5, CV_RGB(128,0,0), 3 );
        }
    }
	CvFont font;
	float aa=1;
	cvInitFont( &font, CV_FONT_HERSHEY_SIMPLEX, aa,aa,0,2, 8 ); 
	i = 0;
	if ( m_Points[NUI_SKELETON_POSITION_HAND_RIGHT].y < m_Points[NUI_SKELETON_POSITION_HEAD].y )
	{
		cvPutText( ImageOut, "Right Hand Up", cvPoint(0,30), &font, CV_RGB(255,255,255) );
		i++;
	}
	if ( m_Points[NUI_SKELETON_POSITION_HAND_LEFT].y < m_Points[NUI_SKELETON_POSITION_HEAD].y )
	{
		cvPutText( ImageOut, "Left Hand Up", cvPoint(0,30+i*30), &font, CV_RGB(255,255,255) );
		i++;
	}
}

ImageOut – это изображение 640 на 480 24 битное, которое выводится в видео-файл.
Внизу функции Nui_DrawSkeleton простая обработка на поднятие левой или правой руки. Ну и последняя функция, также скопированная из примера, определяет какие скелеты отслеживать и отслеживает их.
void UpdateTrackedSkeletons( const NUI_SKELETON_FRAME & skel )
{
    DWORD nearestIDs[2] = { 0, 0 };
    USHORT nearestDepths[2] = { NUI_IMAGE_DEPTH_MAXIMUM, NUI_IMAGE_DEPTH_MAXIMUM };

    // Purge old sticky skeleton IDs, if the user has left the frame, etc
    bool stickyID0Found = false;
    bool stickyID1Found = false;
    for ( int i = 0 ; i < NUI_SKELETON_COUNT; i++ )
    {
        NUI_SKELETON_TRACKING_STATE trackingState = skel.SkeletonData[i].eTrackingState;

        if ( trackingState == NUI_SKELETON_TRACKED || trackingState == NUI_SKELETON_POSITION_ONLY )
        {
            if ( skel.SkeletonData[i].dwTrackingID == m_StickySkeletonIds[0] )
            {
                stickyID0Found = true;
            }
            else if ( skel.SkeletonData[i].dwTrackingID == m_StickySkeletonIds[1] )
            {
                stickyID1Found = true;
            }
        }
    }

    if ( !stickyID0Found && stickyID1Found )
    {
        m_StickySkeletonIds[0] = m_StickySkeletonIds[1];
        m_StickySkeletonIds[1] = 0;
    }
    else if ( !stickyID0Found )
    {
        m_StickySkeletonIds[0] = 0;
    }
    else if ( !stickyID1Found )
    {
        m_StickySkeletonIds[1] = 0;
    }

    // Calculate nearest and sticky skeletons
    for ( int i = 0 ; i < NUI_SKELETON_COUNT; i++ )
    {
        NUI_SKELETON_TRACKING_STATE trackingState = skel.SkeletonData[i].eTrackingState;

        if ( trackingState == NUI_SKELETON_TRACKED || trackingState == NUI_SKELETON_POSITION_ONLY )
        {
            // Save SkeletonIds for sticky mode if there's none already saved
            if ( 0 == m_StickySkeletonIds[0] && m_StickySkeletonIds[1] != skel.SkeletonData[i].dwTrackingID )
            {
                m_StickySkeletonIds[0] = skel.SkeletonData[i].dwTrackingID;
            }
            else if ( 0 == m_StickySkeletonIds[1] && m_StickySkeletonIds[0] != skel.SkeletonData[i].dwTrackingID )
            {
                m_StickySkeletonIds[1] = skel.SkeletonData[i].dwTrackingID;
            }

            LONG x, y;
            USHORT depth;

            // calculate the skeleton's position on the screen
            NuiTransformSkeletonToDepthImage( skel.SkeletonData[i].Position, &x, &y, &depth );

            if ( depth < nearestDepths[0] )
            {
                nearestDepths[1] = nearestDepths[0];
                nearestIDs[1] = nearestIDs[0];

                nearestDepths[0] = depth;
                nearestIDs[0] = skel.SkeletonData[i].dwTrackingID;
            }
            else if ( depth < nearestDepths[1] )
            {
                nearestDepths[1] = depth;
                nearestIDs[1] = skel.SkeletonData[i].dwTrackingID;
            }
        }
    }    
}

Ниже представлен результат работы программы.
  • 0
  • 21 января 2013, 17:21
  • vidikon

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

RSS свернуть / развернуть

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