diff -r 10d5199d9874 -r 4b78f179e7ce middleware/src/MainModule/KinectMain.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/middleware/src/MainModule/KinectMain.cs Mon Apr 02 16:30:56 2012 +0200 @@ -0,0 +1,568 @@ +/* +* This file is part of the TraKERS\Middleware package. +* +* (c) IRI +* +* For the full copyright and license information, please view the LICENSE_MIDDLEWARE +* file that was distributed with this source code. +*/ + +/* + * Projet : TraKERS + * Module : MIDDLEWARE + * Sous-Module : Tracking + * Classe : KinectMain + * + * Auteur : alexandre.bastien@iri.centrepompidou.fr + * + * Fonctionnalités : Récupère les trames de données de la Kinect, les squelettes détectés via le SDK 1.0 de Microsoft. + * Interprète ces trames de façon à afficher le flux vidéo couleurs, et récupérer la distance de l'utilisateur et les + * noeuds de son squelette. Lance des événements lorsque la main gauche/droite entre dans/quitte le champ. + * Envoie des données au sous-module de debug de manière a afficher un retour visuel sur la position de l'utilisateur, + * son squelette, la détection de ses mains. + * Découpe l'interaction avec le middleware en différents modes. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Documents; +using Microsoft.Kinect; +using Trakers.Communication; +using Trakers.MainModule.Events; +using Trakers.MainModule.Gestures; +using Trakers.MainModule.Search; +using Trakers.Debug; +using System.Windows; + +namespace Trakers.MainModule +{ + //Il s'agit des fonctions permettant d'appeler les fonctions des événements Main droite/gauche entre/quitte le champ. + public delegate void LeftHandTrackedHandler(object o, LeftHandTrackedEventArgs e); + public delegate void RightHandTrackedHandler(object o, RightHandTrackedEventArgs e); + public delegate void LeftHandQuitHandler(object o, LeftHandQuitEventArgs e); + public delegate void RightHandQuitHandler(object o, RightHandQuitEventArgs e); + //Il s'agit de la fonction permettant d'appeler les fonctions des événements Swipe left/right/up/down. + public delegate void SwipeHandler(object o, SwipeEventArgs e); + //Il s'agit de la fonction permettant d'appeler les fonctions des événements Push/Pull. + public delegate void PushHandler(object o, PushEventArgs e); + //Il s'agit de la fonction permettant d'appeler les fonctions des événements Jump. + public delegate void JumpHandler(object o, JumpEventArgs e); + //Il s'agit de la fonction permettant d'appeler les fonctions des événements de proximité. + public delegate void UserPositionHandler(object o, UserPositionEventArgs e); + + public class KinectMain + { + //Fenêtre de debug. + private DebugWindow debug; + //Squelettes (Il y en a 6 par défaut). + private Skeleton[] skeletons; + //Caméra infrarouge (sensor) de la Kinect. + private KinectSensor kinectSensor; + + //Détecteur de swipes. + private SwipeDetector swipeDetector; + //Détecteur de pushes. + private PushDetector pushDetector; + //Détecteur de jumps. + private JumpDetector jumpDetector; + //Détecteur de proximité. + private UserPositionDetector userPositionDetector; + + //Serveur TUIO pour la connexion du Middleware vers le Front Atelier. + private Server server; + + //Gestionnaire de modes. + private ModeManagement modeManagement; + + //Les événements des mains pour la recherche. + public static event LeftHandTrackedHandler LeftHandTrackedEvent; + public static event RightHandTrackedHandler RightHandTrackedEvent; + public static event LeftHandQuitHandler LeftHandQuitEvent; + public static event RightHandQuitHandler RightHandQuitEvent; + //L'événement swipe. + public static event SwipeHandler SwipeEvent; + //L'événement push. + public static event PushHandler PushEvent; + //L'événement jump. + public static event JumpHandler JumpEvent; + //L'événement l'utilisateur se déplace dans la zone de détection. + public static event UserPositionHandler UserPositionEvent; + + /* + * Initialisation de la classe principale. + * Affiche l'écran de debug dans lequel on voit la distance à la Kinect, + * les mains détectées et le squelette de l'utilisateur. + */ + public KinectMain() + { + //On crée la fenêtre de debug. + debug = new DebugWindow(); + + //On crée les détecteurs de gestes. + swipeDetector = new SwipeDetector(); + pushDetector = new PushDetector(); + jumpDetector = new JumpDetector(); + //On crée le détecteur de proximité. + userPositionDetector = new UserPositionDetector(debug.getMinDist(), debug.getMaxDist(), debug.getZeroPoint(), debug.getMinDistHands(), debug.getMaxDistHands()); + + //On écoute l'événement de clic sur le bouton on/off du debug. + //Car on lancera l'intitialisation/fermeture de la kinect. + debug.getSwitch().Click += new RoutedEventHandler(Switch_ClickInKinectMain); + debug.Loaded += new RoutedEventHandler(Window_LoadedInKinectMain); + debug.Closed += new EventHandler(Window_CloseInKinectMain); + debug.getQuitMenu().Click += new RoutedEventHandler(Quit_ClickInKinectMain); + + //On affiche la fenêtre de debug. + try + { + debug.ShowDialog(); + } + catch(Exception){ + } + } + + /* + * Initialisation du sensor de la Kinect. + */ + public void KinectInitialization() + { + try + { + //On sélectionne la première kinect détectée. + kinectSensor = KinectSensor.KinectSensors.FirstOrDefault(s => s.Status == KinectStatus.Connected); + //La caméra couleur est activée avec une résolution 640x480 et un framerate de 30 FPS. + kinectSensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30); + //La caméra de profondeur est activée. + kinectSensor.DepthStream.Enable(); + //Le squelette est activé. + kinectSensor.SkeletonStream.Enable(); + + //Quand le Middleware reçoit des trames de la Kinect, on va dans cette fonction. + kinectSensor.AllFramesReady += new EventHandler(AllFramesReady); + + //On applique des paramètres d'ajustement pour le squelette. + TransformSmoothParameters parameters = new TransformSmoothParameters(); + parameters.Smoothing = 0.2f; + parameters.Correction = 0.8f; + parameters.Prediction = 0.0f; + parameters.JitterRadius = 0.5f; + parameters.MaxDeviationRadius = 0.5f; + kinectSensor.SkeletonStream.Enable(parameters); + //On démarre la Kinect. + kinectSensor.Start(); + + } + catch (System.Exception) + { + debug.ShowException("KinectNotConnected"); + } + + //Pour les événements main gauche/droite entre dans/quitte le champ, on a 4 listeners. + //Fonction appelée lorsque la main gauche entre dans le champ de recherche. + LeftHandTrackedListener leftHandTrackedListener = new LeftHandTrackedListener(); + LeftHandTrackedEvent += new LeftHandTrackedHandler(leftHandTrackedListener.showAndSend); + + //Fonction appelée lorsque la main droite entre dans le champ de recherche. + RightHandTrackedListener rightHandTrackedListener = new RightHandTrackedListener(); + RightHandTrackedEvent += new RightHandTrackedHandler(rightHandTrackedListener.showAndSend); + + //Fonction appelée lorsque la main gauche quitte le champ de recherche. + LeftHandQuitListener leftHandQuitListener = new LeftHandQuitListener(); + LeftHandQuitEvent += new LeftHandQuitHandler(leftHandQuitListener.showAndSend); + + //Fonction appelée lorsque la main droite quitte le champ de recherche. + RightHandQuitListener rightHandQuitListener = new RightHandQuitListener(); + RightHandQuitEvent += new RightHandQuitHandler(rightHandQuitListener.showAndSend); + + //Fonction appelée lorsque l'utilisateur effectue un Swipe right/left/up/down. + SwipeListener swipeListener = new SwipeListener(); + SwipeEvent += new SwipeHandler(swipeListener.showAndSend); + + //Fonction appelée lorsque l'utilisateur effectue un Push/Pull. + PushListener pushListener = new PushListener(); + PushEvent += new PushHandler(pushListener.showAndSend); + + //Fonction appelée lorsque l'utilisateur effectue un Jump. + JumpListener jumpListener = new JumpListener(); + JumpEvent += new JumpHandler(jumpListener.showAndSend); + + //Fonction appelée lorsque l'utilisateur se déplace dans la zone de détection. + UserPositionListener userPositionListener = new UserPositionListener(); + UserPositionEvent += new UserPositionHandler(userPositionListener.showAndSend); + + //On connecte le serveur à l'adresse locale sur le port 80. + try + { + server = new Server(debug.getConnexionHost(), debug.getConnexionPort(), debug.getTimerElapsing()); + //On crée le gestionnaire de modes. + modeManagement = new ModeManagement(server, debug, this); + modeManagement.DetectProximityBasedModes(0); + } + catch (Exception) + { + debug.ShowException("serverCantStart"); + } + } + + /* + * Bouton ON/OFF du debug écouté dans KinectMain. + */ + public void Switch_ClickInKinectMain(object sender, RoutedEventArgs e) + { + Console.Out.WriteLine(debug.getOn()); + //Si la kinect est allumée. + if (debug.getOn()) + { + //On initialise la Kinect. + KinectInitialization(); + } + else + { + //On éteint la Kinect. + KinectClose(); + } + } + + /* + * Méthode associée à l'événement : Quitter via le menu écoutée dans KinectMain. + */ + private void Quit_ClickInKinectMain(object sender, RoutedEventArgs e) + { + KinectClose(); + debug.Quit_Click(sender, e); + } + + /* + * Permet d'initialiser la Kinect dès que la fenêtre est lancée écoutée dans KinectMain. + */ + private void Window_LoadedInKinectMain(object sender, RoutedEventArgs e) + { + KinectInitialization(); + } + + /* + * Fermeture du debug écouté dans KinectMain. + */ + private void Window_CloseInKinectMain(object sender, EventArgs e) + { + //On éteint la Kinect. + KinectClose(); + debug.Window_Closed(sender, e); + } + + /* + * Fermeture du sensor de la Kinect. + */ + public void KinectClose() + { + try + { + //On stoppe la Kinect. + kinectSensor.Stop(); + //On met a zero l'image d'affichage et le serveur. + debug.ShutDownInterface(); + } + catch (System.Exception) + { + debug.ShowException("KinectNotConnected"); + } + } + + /* + * Récupère le premier squelette. + */ + Skeleton GetFirstSkeleton(object sender, AllFramesReadyEventArgs e) + { + using (SkeletonFrame skeletonFrameData = e.OpenSkeletonFrame()) + { + if (skeletonFrameData == null) + return null; + if ((skeletons == null) || (skeletons.Length != skeletonFrameData.SkeletonArrayLength)) + skeletons = new Skeleton[skeletonFrameData.SkeletonArrayLength]; + skeletonFrameData.CopySkeletonDataTo(skeletons); + + //On obtient le premier skelette. + Skeleton first = (from s in skeletons where s.TrackingState == SkeletonTrackingState.Tracked select s).FirstOrDefault(); + + return first; + } + } + + /* + * Récupère le squelette le plus proche. + */ + Skeleton GetNearestSkeleton(object sender, AllFramesReadyEventArgs e) + { + using (SkeletonFrame skeletonFrameData = e.OpenSkeletonFrame()) + { + if (skeletonFrameData == null) + return null; + if ((skeletons == null) || (skeletons.Length != skeletonFrameData.SkeletonArrayLength)) + skeletons = new Skeleton[skeletonFrameData.SkeletonArrayLength]; + skeletonFrameData.CopySkeletonDataTo(skeletons); + + Skeleton s; + float minDist = (float)-1.0; + int minID = 0; + + //Pour tous les squelettes. + for(int i = 0 ; i < skeletons.Count() ; i++) + { + s = skeletons.ElementAt(i); + //S'il est tracké. + if(s.TrackingState == SkeletonTrackingState.Tracked) + { + //On récupère sa position et on obtient la distance min et l'ID du squelette qui est à la distance min. + float dist = skeletons.ElementAt(i).Position.Z; + if (minDist == -1) + { + minDist = dist; + minID = i; + } + else if(minDist > dist) + { + minDist = dist; + minID = i; + } + } + } + + //On renvoie le skelette le plus proche. + return skeletons.ElementAt(minID); + } + } + + /* + * Récupère le squelette le plus proche. + */ + private void AllFramesReady(object sender, AllFramesReadyEventArgs e) + { + //On ne calcule rien si la fenêtre de debug se ferme. + if (debug.isClosing()) + return; + + //On écoute le debug pour savoir si les paramètres ont été modifiés. + + + //On met à jour la vidéo de debug. + debug.RefreshVideo(e); + //On récupère le premier squelette tracké. + //Skeleton first = GetFirstSkeleton(e); + //On récupère le plus proche squelette tracké. + Skeleton first = GetNearestSkeleton(sender, e); + //Si celui-ci n’est pas nul + if (first == null) + return; + + //Si ce squelette est tracké (donc suivi et reconnu par la camera) + if (first.TrackingState == SkeletonTrackingState.Tracked) + { + //Ensemble des noeuds du squelette. + Joint hipCenter = getJoint(first, JointType.HipCenter), spine = getJoint(first, JointType.Spine), shoulderCenter = getJoint(first, JointType.ShoulderCenter), head = getJoint(first, JointType.Head); + Joint shoulderLeft = getJoint(first, JointType.ShoulderLeft), elbowLeft = getJoint(first, JointType.ElbowLeft), wristLeft = getJoint(first, JointType.WristLeft), handLeft = getJoint(first, JointType.HandLeft); + Joint shoulderRight = getJoint(first, JointType.ShoulderRight), elbowRight = getJoint(first, JointType.ElbowRight), wristRight = getJoint(first, JointType.WristRight), handRight = getJoint(first, JointType.HandRight); + Joint hipLeft = getJoint(first, JointType.HipLeft), kneeLeft = getJoint(first, JointType.KneeLeft), ankleLeft = getJoint(first, JointType.AnkleLeft), footLeft = getJoint(first, JointType.FootLeft); + Joint hipRight = getJoint(first, JointType.HipRight), kneeRight = getJoint(first, JointType.KneeRight), ankleRight = getJoint(first, JointType.AnkleRight), footRight = getJoint(first, JointType.FootRight); + + //On construit l'historique des postures. + List joints = new List(); + joints.Clear(); + joints.Insert((int)JointType.HipCenter, hipCenter); + joints.Insert((int)JointType.Spine, spine); + joints.Insert((int)JointType.ShoulderCenter, shoulderCenter); + joints.Insert((int)JointType.Head, head); + joints.Insert((int)JointType.ShoulderLeft, shoulderLeft); + joints.Insert((int)JointType.ElbowLeft, elbowLeft); + joints.Insert((int)JointType.WristLeft, wristLeft); + joints.Insert((int)JointType.HandLeft, handLeft); + joints.Insert((int)JointType.ShoulderRight, shoulderRight); + joints.Insert((int)JointType.ElbowRight, elbowRight); + joints.Insert((int)JointType.WristRight, wristRight); + joints.Insert((int)JointType.HandRight, handRight); + joints.Insert((int)JointType.HipLeft, hipLeft); + joints.Insert((int)JointType.KneeLeft, kneeLeft); + joints.Insert((int)JointType.AnkleLeft, ankleLeft); + joints.Insert((int)JointType.FootLeft, footLeft); + joints.Insert((int)JointType.HipRight, hipRight); + joints.Insert((int)JointType.KneeRight, kneeRight); + joints.Insert((int)JointType.AnkleRight, ankleRight); + joints.Insert((int)JointType.FootRight, footRight); + GestureDetector.UpdateSkeletonHistory(joints); + + //Si la main gauche est dans le champ, on lance l'événement approprié. + if (handLeft.Position.Z < debug.getMaxDistHands() && handLeft.Position.Z > debug.getMinDistHands()) + { + LeftHandTrackedEventArgs leftHandTrackedEvent = new LeftHandTrackedEventArgs(server, debug, handLeft, handLeft.Position.Z); + OnLeftHandTrackedEvent(leftHandTrackedEvent); + } + //Si la main gauche quitte le champ, on lance l'événement approprié. + else + { + LeftHandQuitEventArgs leftHandQuitEvent = new LeftHandQuitEventArgs(server, debug); + OnLeftHandQuitEvent(leftHandQuitEvent); + } + //Si la main droite est dans le champ, on lance l'événement approprié. + if (handRight.Position.Z < debug.getMaxDistHands() && handRight.Position.Z > debug.getMinDistHands()) + { + RightHandTrackedEventArgs rightHandTrackedEvent = new RightHandTrackedEventArgs(server, debug, handRight, handRight.Position.Z); + OnRightHandTrackedEvent(rightHandTrackedEvent); + } + //Si la main droite quitte le champ, on lance l'événement approprié. + else + { + RightHandQuitEventArgs rightHandQuitEvent = new RightHandQuitEventArgs(server, debug); + OnRightHandQuitEvent(rightHandQuitEvent); + } + + //Si l'utilisateur effectue un swipe left. + if (swipeDetector.CheckForSwipeLeft()) + { + SwipeEventArgs swipeEvent = new SwipeEventArgs(server, debug, SwipeDetector.Direction.LEFT); + OnSwipeEvent(swipeEvent); + } + + //Si l'utilisateur effectue un swipe right. + if (swipeDetector.CheckForSwipeRight()) + { + SwipeEventArgs swipeEvent = new SwipeEventArgs(server, debug, SwipeDetector.Direction.RIGHT); + OnSwipeEvent(swipeEvent); + } + + //Enum sur la main qui effectue le geste. + PushDetector.Hand handPush; + //Si l'utilisateur effectue un push. + if ((handPush = pushDetector.CheckForPush()) != PushDetector.Hand.NONE) + { + PushEventArgs pushEvent = new PushEventArgs(server, debug, PushDetector.Direction.PUSH, handPush); + OnPushEvent(pushEvent); + } + //Si l'utilisateur effectue un pull. + if ((handPush = pushDetector.CheckForPull()) != PushDetector.Hand.NONE) + { + PushEventArgs pushEvent = new PushEventArgs(server, debug, PushDetector.Direction.PULL, handPush); + OnPushEvent(pushEvent); + } + + //Si l'utilisateur se déplace dans la zone de détection. + //On traite le problème en plusieurs limites, on discrétise la zone. + if (first.TrackingState == SkeletonTrackingState.Tracked) + { + float proximity = userPositionDetector.CalcProximity(first.Position.Z); + int numberOfImages = userPositionDetector.ImagesToShow(proximity, debug.getImagesToShow()); + + modeManagement.DetectProximityBasedModes(proximity); + + if (proximity > 0f) + { + UserPositionEventArgs userPositionEvent = new UserPositionEventArgs(server, debug, proximity, numberOfImages); + OnUserPositionEvent(userPositionEvent); + } + else if(proximity < 10f) + { + debug.hideSkeleton(); + modeManagement.DetectProximityBasedModes(0); + LeftHandQuitEventArgs leftHandQuitEvent = new LeftHandQuitEventArgs(server, debug); + OnLeftHandQuitEvent(leftHandQuitEvent); + RightHandQuitEventArgs rightHandQuitEvent = new RightHandQuitEventArgs(server, debug); + OnRightHandQuitEvent(rightHandQuitEvent); + } + } + + //Dessine le squelette dans le debug. + debug.drawJoints(first.Joints, first); + debug.showSkeleton(hipCenter, spine, shoulderCenter, head, shoulderLeft, elbowLeft, wristLeft, handLeft, shoulderRight, elbowRight, wristRight, handRight, hipLeft, kneeLeft, ankleLeft, footLeft, hipRight, kneeRight, ankleRight, footRight); + } + else + { + debug.hideSkeleton(); + modeManagement.DetectProximityBasedModes(0); + LeftHandQuitEventArgs leftHandQuitEvent = new LeftHandQuitEventArgs(server, debug); + OnLeftHandQuitEvent(leftHandQuitEvent); + RightHandQuitEventArgs rightHandQuitEvent = new RightHandQuitEventArgs(server, debug); + OnRightHandQuitEvent(rightHandQuitEvent); + } + } + + /* + * Change l'échelle des coordonnées d'un noeud pour qu'en X et Y il corresponde à la résolution et en Z à la distance à la Kinect. + */ + public Joint getJoint(Skeleton ske, JointType jointID) + { + return Coding4Fun.Kinect.Wpf.SkeletalExtensions.ScaleTo(ske.Joints[jointID], 600, 400, 0.75f, 0.75f); + } + + /* + * Initialise l'événement et fait appel aux fonctions du listener quand la main gauche entre dans le champ. + */ + public static void OnLeftHandTrackedEvent(LeftHandTrackedEventArgs e) + { + if (LeftHandTrackedEvent != null) + LeftHandTrackedEvent(new object(), e); + } + + /* + * Initialise l'événement et fait appel aux fonctions du listener quand la main droite entre dans le champ. + */ + public static void OnRightHandTrackedEvent(RightHandTrackedEventArgs e) + { + if (RightHandTrackedEvent != null) + RightHandTrackedEvent(new object(), e); + } + + /* + * Initialise l'événement et fait appel aux fonctions du listener quand la main gauche quitte le champ. + */ + public static void OnLeftHandQuitEvent(LeftHandQuitEventArgs e) + { + if (LeftHandQuitEvent != null) + LeftHandQuitEvent(new object(), e); + } + + /* + * Initialise l'événement et fait appel aux fonctions du listener quand la main droite quitte le champ. + */ + public static void OnRightHandQuitEvent(RightHandQuitEventArgs e) + { + if (RightHandQuitEvent != null) + RightHandQuitEvent(new object(), e); + } + + /* + * Initialise l'événement et fait appel aux fonctions du listener quand l'utilisateur effectue un swipe right. + */ + public static void OnSwipeEvent(SwipeEventArgs e) + { + if (SwipeEvent != null) + SwipeEvent(new object(), e); + } + + /* + * Initialise l'événement et fait appel aux fonctions du listener quand l'utilisateur effectue un push. + */ + public static void OnPushEvent(PushEventArgs e) + { + if (PushEvent != null) + PushEvent(new object(), e); + } + + /* + * Initialise l'événement et fait appel aux fonctions du listener quand l'utilisateur effectue un saut. + */ + public static void OnJumpEvent(JumpEventArgs e) + { + if (JumpEvent != null) + JumpEvent(new object(), e); + } + + /* + * Initialise l'événement et fait appel aux fonctions du listener quand l'utilisateur se déplace + * dans la zone de détection. + */ + public static void OnUserPositionEvent(UserPositionEventArgs e) + { + if (UserPositionEvent != null) + UserPositionEvent(new object(), e); + } + } +}