Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Dans mon précédant billet, nous avons vu comment capturer des images en 640*480 provenant du flux images de la kinect, dans ce billet, nous allons voir comment capturer, et traiter le flux de profondeur et identifier le « player » qui est reconnu par la Kinect.
Pour capturer le flux profondeur, nous allons procéder de la même manière que le flux vidéo.
On déclare deux HANDLE l’un _depthStreamHandle, qui servira de pointeur afin de recevoir le flux de données, l’autre _depthNextFrame qui servira pour signaler la disponibilité de la trame suivante que nous utiliserons en conjonction avec l’API WaitForSingleObject
HANDLE _depthStreamHandle;
HANDLE _depthNextFrame;
Le flux de profondeur s’initialise en ouvrant un flux à l’aide l’API NuiImageStreamOpen.
· Tout d’abord, on crée un manual reset event que nous passerons à la méthode NuiImageStreamOpen, afin que le runtime NUI signale la disponibilité d’une nouvelle trame.
_depthNextFrame=CreateEvent( NULL, TRUE, FALSE, NULL );
- Nous décidons de capturer à la fois les informations de profondeurs, mais également l’index du ‘player’ reconnu par la Kinect (NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX), dans une résolution de 320*240 (NUI_IMAGE_RESOLUTION_320x240)
- Le 3ième paramètre contrôle le pré-processing de l’image. Nous le mettons ici est à 0, car ce drapeau n’est pas utilisé dans la version actuelle du SDK Kinect.
· Le 4ième paramètre correspond au nombre de frame que le runtime NUI doit buffériser. Dans la documentation il est noté que le maximum est 4 (NUI_IMAGE_STREAM_FRAME_LIMIT_MAXIMUM), mais 2 suffit pour la plupart des applications.
· Enfin les 5ièmes et 6ièmes paramètres correspondent à nos deux HANDLE définis plus haut.
HRESULT hr = NuiImageStreamOpen(
NUI_IMAGE_TYPE_DEPTH_AND_PLAYER_INDEX,
NUI_IMAGE_RESOLUTION_320x240,
0,
2,
_depthNextFrame,
&_depthStreamHandle );
Pour récupérer la trame suivante, nous utilisons dans une boucle l’API NuiImageStreamGetNextFrame de la manière suivante :
o Nous allouons un tableau de structure RGBQUAD
RGBQUAD *rgbWk=(RGBQUAD*)malloc(640*480)
o Ensuite on appelle la méthode NuiImageStreamGetNextFrame en lui passant les paramètres suivants :
hr = NuiImageStreamGetNextFrame(
_depthStreamHandle,
0,
&pImageFrame );
o Le 1er paramètre est _ depthStreamHandle crée par l’API NuiImageStreamOpen
o Le second paramètre est un délai d’attente exprimé en millisecondes avant que la méthode ne retourne sans nouvelle trame.
o Le 3ième est un pointeur sur la structure NUI_IMAGE_FRAME qui recevra les données. C’est cette structure que nous manipulerons pour afficher la trame à l’écran.
const NUI_IMAGE_FRAME * pImageFrame = NULL;
o A partir de la trame, nous allons récupérer les informations de profondeurs à l’aide de l’objet NuiImageBuffer (Cet objet est similaire à la texture Direct3D)
NuiImageBuffer * pTexture = pImageFrame->pFrameTexture;
o A partir de cette texture, nous allons récupérer un pointeur sur la structure KINECT_LOCKED_RECT
pTexture->LockRect( 0, &LockedRect, NULL, 0 ) ;
Structure qui contiendra un pointeur sur les données de la trame
BYTE * pBuffer = (BYTE*) LockedRect.pBits;
Ainsi que la longueur de chaque ligne
LockedRect.Pitch
o Puis on récupère l’adresse du 1er élément de notre tableau de structure RGBQUAD
RGBQUAD * rgbrun=rgbWk;
o On récupère les 16 premiers bits du buffer de données.
USHORT * pBufferRun = (USHORT*)pBuffer;
Les 13 1er bits correspondant à l’information de profondeur, les 3 bits de poids faible, correspondant aux index des « players » que Kinect a détectée.
o On fournit ces informations, à la méthode KinNuiShortToQuadDepth() (Listing à la fin de ce billet) , qui est en charge de transformer les informations de profondeur en couleur.
RGBQUAD quad = KinNuiShortToQuadDepth( *pBufferRun );
o Enfin on dessine notre trame.
_pD2D1Helper->D2D1DrawFrame ((BYTE*)rgbWk,LockedRect.Pitch*2,320,240);
Dans la vidéo, l’intensité du player change en fonction de la distance par rapport à la kinect.
Listing complet
HRESULT DPEKinectHelper::KinDemarrerDepth ()
{
HRESULT hr=S_OK;
//Allocation d'un tableau de structure RGQQUAD
RGBQUAD *rgbWk=(RGBQUAD*)malloc(640*480);
while(true)
{
//Si un arrêt est demandé sur la tâche
if (Context::IsCurrentTaskCollectionCanceling ())
{
break;
}
const NUI_IMAGE_FRAME * pImageFrame = NULL;
WaitForSingleObject (_depthNextFrame,INFINITE);
hr = NuiImageStreamGetNextFrame(
_depthStreamHandle,
0,
&pImageFrame );
if (SUCCEEDED(hr))
{
NuiImageBuffer * pTexture = pImageFrame->pFrameTexture;
KINECT_LOCKED_RECT LockedRect;
pTexture->LockRect( 0, &LockedRect, NULL, 0 );
if( LockedRect.Pitch != 0 )
{
BYTE * pBuffer = (BYTE*) LockedRect.pBits;
//Récupère le pointeur sur le 1er élèment de notre tableau de RGBQUAD
RGBQUAD * rgbrun=nullptr;
//si l'allocation n'a pas echouée
if (rgbWk)
{
rgbrun = rgbWk;
}
else
return E_OUTOFMEMORY;
//Récupère les 16 premiers bits du buffer de données
//13 Bits pour les informations de profondeurs
//3 Bits correspondant aux index des "players" que Kinect a detecté
USHORT * pBufferRun = (USHORT*)pBuffer;
//Parcours de l'integralité de la trame
for( int y = 0 ; y < 240 ; y++ )
{
for( int x = 0 ; x < 320 ; x++ )
{
//Passe les 16 1er bits à la méthode KinNuiShortToQuadDepth
//afin qu’elle calcul un code de couleur a afficher
RGBQUAD quad = KinNuiShortToQuadDepth( *pBufferRun );
//Avance le pointeur USHORT pour récupérer les prochaines
informations de profondeurs et de player
pBufferRun++;
*rgbrun = quad;
rgbrun++;
}
}
//Dessiner la trame
_pD2D1Helper->D2D1GetContexteRendu ()->BeginDraw ();
_pD2D1Helper->D2D1PeindreArrierePlan ();
_pD2D1Helper->D2D1DrawFrame ((BYTE*)rgbWk,LockedRect.Pitch*2,320,240);
_pD2D1Helper->D2D1GetContexteRendu ()->EndDraw ();
hr=pTexture->UnlockRect (0);
}
else
{
OutputDebugString( L"Longueur du buffer erronée\r\n" );
}
NuiImageStreamReleaseFrame( _depthStreamHandle, pImageFrame );
}
}
free(rgbWk);
return hr;
}
Listing de la méthode KinNuiShortToQuadDepth()
RGBQUAD DPEKinectHelper::KinNuiShortToQuadDepth( USHORT s )
{
//Décale de 3 Bits vers la droite afin de ne récupère que les informations
//concernant la profondeur
USHORT RealDepth = (s & 0xfff8) >> 3;
//Masque pour retrouver si un player a été detecté
USHORT Player = s & 7;
// Transforme les informations de profondeur sur 13-Bit en une intensité codé sur 8 Bits
// afin d'afficher une couleur en fonction de la profondeur
BYTE l = 255 - (BYTE)(256*RealDepth/0x0fff);
RGBQUAD q;
q.rgbRed = q.rgbBlue = q.rgbGreen = 0;
switch( Player )
{
case 0:
//Affiche les informations (non player) en niveau de gris
q.rgbRed = l / 2;
q.rgbBlue = l / 2;
q.rgbGreen = l / 2;
break;
//Si un player est detecté affiche le en couleur
//avec une intensité relatif à la profondeur
case 1:
q.rgbRed = l;
break;
case 2:
q.rgbGreen = l;
break;
case 3:
q.rgbRed = l / 4;
q.rgbGreen = l;
q.rgbBlue = l;
break;
case 4:
q.rgbRed = l;
q.rgbGreen = l;
q.rgbBlue = l / 4;
break;
case 5:
q.rgbRed = l;
q.rgbGreen = l / 4;
q.rgbBlue = l;
break;
case 6:
q.rgbRed = l / 2;
q.rgbGreen = l / 2;
q.rgbBlue = l;
break;
case 7:
q.rgbRed = 255 - ( l / 2 );
q.rgbGreen = 255 - ( l / 2 );
q.rgbBlue = 255 - ( l / 2 );
}
return q;
}
Dans mon 3ième billet, nous abordons la manière de traquer et dessiner le squelette détecté par Kinect.
Comment développer avec le SDK Kinect en C++ Part III
Eric Vernié
Comments
- Anonymous
April 04, 2013
Bonjour, je suis en projet de bac Avec ma kinect et je voulais juste savoir a quoi sert le "code source" ?? merci :)