Kinect SDK 1.0 – 4 – Kinect en profondeur avec le DepthStream
1. Introduction à l’API |
2. Utilisation du ColorImageStream |
3. Tracker le squelette avec le SkeletonStream |
4. Kinect en profondeur avec le DepthStream |
5. Reconnaissance vocale |
Ce nouvel article va encore une fois mettre en avant une particularité de la Kinect, qui est sa capacité à avoir une vue en 3 dimensions de l’espace.
Cela peut sembler anodin à l’heure où on est inondés de films 3D, mais la technologie utilisée par la Kinect n’est pas du tout la même que celle utilisée dans le cinéma: pour tourner un film en 3D, on utilise de la 3D stéréoscopique. Dans ce cas, ils n’utilisent non pas une mais deux caméras espacées pour reproduire la vision des yeux et obtenir simplement une image pour l’oeil droit et une image pour l’oeil gauche. C’est ensuite votre cerveau qui traite les informations des deux images et apporte une notion de distance.
La Kinect par contre va utiliser un émetteur et un récepteur d’infrarouges qui vont permettre de calculer la distance des points de l’environnement. La Kinect se suffit donc à elle même!
Dans cet article on va voir comment obtenir un DepthImageFrame de la Kinect, et comment l’utiliser pour créer une vidéo en 3 dimension! Au passage on va également avoir un exemple de polling (je vous en avais parlé dans le premier article).
Vous pouvez immédiatement télécharger les sources du projet ici:
Pour les exemples de code qui vont suivre, on va créer un projet XNA! Alors si vous n’êtes pas très familier avec ce framework, ce n’est pas bien grave: moi non plus!
Ce qu’il faut juste savoir ici, c’est que notre application n’est pas event-driven mais est exécutée dans une boucle. Cela veut dire qu’on ne va pas attendre qu’un évènement soit déclenché pour faire une action. Au lieu de ça, à chaque passage dans la boucle on va aller demander de nouvelles informations à la Kinect!
Scène 3D: principe du projet
Comme expliqué plus haut, la Kinect peut donner la profondeur de chaque pixel. Dès lors, il est tout à fait imaginable de représenter l’image vue par la Kinect dans un espace en 3 dimensions, et de changer virtuellement la position de la caméra! On pourrait alors en quelque sorte se déplacer au milieu d’une scène et la voir sous différents angles alors qu’elle n’est pourtant filmée que d’un seul endroit!
Initialisation de la Kinect
On va donc simplement récupérer la Kinect connectée, et activer les différents flux dans le constructeur de Game1 :
public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; // Use the first Connected Kinect Kinect = KinectSensor.KinectSensors.FirstOrDefault(k => k.Status == KinectStatus.Connected); if (Kinect != null) { // Activate the Near mode Kinect.DepthStream.Range = DepthRange.Near; // Enable the DepthStream Kinect.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30); // ... and instantiate the needed arrays to store some data depthPixelData = new short[Kinect.DepthStream.FramePixelDataLength]; colorCoordinates = new ColorImagePoint[depthPixelData.Length]; vertexPositionColors = new VertexPositionColor[depthPixelData.Length * 2]; // Enable the Color Stream Kinect.ColorStream.Enable(ColorImageFormat.RgbResolution1280x960Fps12); colorFramePixelData = new byte[Kinect.ColorStream.FramePixelDataLength]; Kinect.Start(); } }
Dans la méthode Update, on va demander explicitement des infos à la Kinect:
if (Kinect != null) { // Ask for a DepthFrame using (DepthImageFrame depthFrame = Kinect.DepthStream.OpenNextFrame(0)) { if (depthFrame != null) { // Copy the data depthFrame.CopyPixelDataTo(depthPixelData); // And match each point of the depthframe to a position // in the colorframe that we are about to receive Kinect.MapDepthFrameToColorFrame(depthFrame.Format, depthPixelData, Kinect.ColorStream.Format, colorCoordinates); using (ColorImageFrame colorFrame = Kinect.ColorStream.OpenNextFrame(0)) { if (colorFrame != null) { // Copy the data colorFrame.CopyPixelDataTo(colorFramePixelData); } } } } }
Ici on va utiliser les méthodes OpenNextFrame directement sur les flux qui nous intéressent. La valeur passée en paramètre indique le temps à attendre avant un timeout. Dans ce cas-ci, si une frame n’est pas disponible immédiatement, on ne veut pas attendre.
Ensuite, dans la méthode Draw, on va mettre à jour les points à dessiner.
// For each pixel index in the colorCoordinates array for (int i = 0; i < colorCoordinates.Length; i++) { // If the coordinates are in the range of the colorstream frame size if (colorCoordinates[i].X < Kinect.ColorStream.FrameWidth && colorCoordinates[i].Y < Kinect.ColorStream.FrameHeight) { // calculate the X,Y coordinates of the pixel in the depthstream frame var pixelY = (i) / Kinect.DepthStream.FrameHeight; var pixelX = (i) % Kinect.DepthStream.FrameWidth; // Find the corresponding value in the Skeleton referential var skeletonPoint = Kinect.MapDepthToSkeletonPoint(Kinect.DepthStream.Format, pixelX, pixelY, depthPixelData[i]); // Retrieve the first index of the fourth-bytes pixel in the ColorFrame. var baseIndex = (colorCoordinates[i].Y * Kinect.ColorStream.FrameWidth + colorCoordinates[i].X) * 4; // And finally we update the corresponding vertexPositionColors[i * 2].Color.R = colorFramePixelData[baseIndex + 2]; vertexPositionColors[i * 2].Color.G = colorFramePixelData[baseIndex + 1]; vertexPositionColors[i * 2].Color.B = colorFramePixelData[baseIndex + 0]; vertexPositionColors[i * 2].Position.X = skeletonPoint.X; vertexPositionColors[i * 2].Position.Y = skeletonPoint.Y; vertexPositionColors[i * 2].Position.Z = skeletonPoint.Z; // Use another point right behind the first one vertexPositionColors[i * 2 + 1] = vertexPositionColors[i * 2]; vertexPositionColors[i * 2 + 1].Position.Z = skeletonPoint.Z + 0.05f; } }
Dans le code ci-dessus, pour chaque pixel que l’on veut imprimer, on récupère sa position réelle dans un référentiel en 3 dimensions grâce à la méthode MapDepthToSkeletonPoint.
Il faut savoir qu’avec XNA 4.0, il n’est pas possible de dessiner un point. Du coup on va dessiner une droite relativement petite à partir de deux points: un premier à la position exacte à laquelle il devrait être selon la Kinect, et un second 5 centimètres derrière.
foreach (EffectPass effectPass in basicEffect.CurrentTechnique.Passes) { effectPass.Apply(); graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>( PrimitiveType.LineList, vertexPositionColors, 0, vertexPositionColors.Length / 2 ); }
Pour terminer, on va simplement dessiner une liste de lignes en se basant sur les points que l’on vient de définir juste avant!
Bouger la caméra
On va d’abord initialiser la caméra au lancement de l’application:
/// <summary> /// LoadContent will be called once per game and is the place to load /// all of your content. /// </summary> protected override void LoadContent() { basicEffect = new BasicEffect(GraphicsDevice); // The position of the camera cameraPosition = new Vector3(0, 0, -1); // creates the view based on the camera position, the target position, and the up orientation. viewMatrix = Matrix.CreateLookAt(cameraPosition, new Vector3(0, 0, 2), Vector3.Up); basicEffect.View = viewMatrix; basicEffect.Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45f), GraphicsDevice.Viewport.AspectRatio, 1f, 1000f); basicEffect.VertexColorEnabled = true; }
Et pour faire en sorte de pouvoir bouger la position de la caméra au cours de l’application, on ajoute ceci dans la méthode Update:
foreach (Keys key in Keyboard.GetState().GetPressedKeys()) { if (key == Keys.D) { cameraPosition.X -= 0.01f; } if (key == Keys.Q) { cameraPosition.X += 0.01f; } if (key == Keys.Z) { cameraPosition.Y += 0.01f; } if (key == Keys.S) { cameraPosition.Y -= 0.01f; } if (key == Keys.E) { cameraPosition.Z += 0.01f; } if (key == Keys.X) { cameraPosition.Z -= 0.01f; } } viewMatrix = Matrix.CreateLookAt(cameraPosition, new Vector3(0, 0, 2), Vector3.Up); basicEffect.View = viewMatrix;
Ce code va modifier la position de la caméra si on appuie sur les touches indiquées. Par exemple, Z va faire monter la caméra sur l’axe Y, tandis que S va la faire descendre.
Article original: Kinect SDK 1.0 – 4 – Kinect en profondeur avec le DepthStream
Version anglaise: http://www.renauddumont.be/en/2012/kinect-sdk-1-0-4-kinect-en-profondeur-avec-le-depthstream
1 commentaire
Kinect SDK 1.0 – Hands-on! | Renaud Dumont - { .NET blog } · 01/05/2012 à 4:54
[…] […]
Les commentaires sont fermés.