// Get xyz physical coordinates private BallCoords XYZLocation(DepthFrame depthFrame, int xavg, int yavg) { BallCoords ballLocn = new BallCoords(); if (depthFrame == null) { return(ballLocn); } using (KinectBuffer depthFrameData = depthFrame.LockImageBuffer()) { CameraSpacePoint[] camSpacePoints = new CameraSpacePoint[1920 * 1080]; this.coordinateMapper.MapColorFrameToCameraSpaceUsingIntPtr(depthFrameData.UnderlyingBuffer, depthFrameData.Size, camSpacePoints); List <float> xvals = new List <float>(); List <float> yvals = new List <float>(); List <float> zvals = new List <float>(); int Vcount = 0; // Find ball in camera space for (int i = -40; i < 40; i++) { for (int j = -40; j < 40; j++) { if (yavg + i > tableLevel) { int tempIndex = (yavg + i) * 1920 + xavg + j; if (camSpacePoints[tempIndex].Z > GlobalClass.minZ && camSpacePoints[tempIndex].Z < 3.6) { xvals.Add(camSpacePoints[tempIndex].X); yvals.Add(camSpacePoints[tempIndex].Y); zvals.Add(camSpacePoints[tempIndex].Z); Vcount++; } } } } if (Vcount > 0) { ballLocn.X = FindMedian(xvals); ballLocn.Y = FindMedian(yvals); ballLocn.Z = FindMedian(zvals); ballLocn.Time = DateTime.Now; } else { ballLocn.X = 0; ballLocn.Y = 0; ballLocn.Z = 0; } return(ballLocn); } }
// Calculate ball speed in m/s private double BallSpeed(BallCoords currLocn, BallCoords prevLocn) { double deltat = currLocn.Time.Subtract(prevLocn.Time).TotalSeconds; if (currLocn.X != 0 && currLocn.Y != 0 && prevLocn.X != 0 && prevLocn.Y != 0 && deltat > 0.025 && deltat < 0.07) { double deltax = currLocn.X - prevLocn.X; double deltay = currLocn.Y - prevLocn.Y; double distance = Math.Sqrt(deltax * deltax + deltay * deltay); SpeedData[SpeedIndex] = currLocn.X + " " + prevLocn.X + " " + currLocn.Y + " " + prevLocn.Y + " " + distance + " " + deltat; SpeedIndex++; return(distance / deltat); } else { return(0); } }
// Color frame analysis With ball action analysis private void Frame_Arrived(object sender, MultiSourceFrameArrivedEventArgs e) { bool changeDir = false; if (scoreDelay != DateTime.MinValue) { if ((float)DateTime.Now.Subtract(this.scoreDelay).TotalSeconds < 2) { pause = true; } else { scoreDelay = DateTime.MinValue; pause = false; } } if (!gameOver && !pause) { MultiSourceFrame multiSourceFrame = e.FrameReference.AcquireFrame(); if (inVolley) { // If too much time passes without bounce/return, someone missed if (this.hitTime != DateTime.MinValue) { if ((float)DateTime.Now.Subtract(this.hitTime).TotalSeconds > 1.5) { if (this.Direction == "Left" && bounce1) { Score("P2", "Time Limit - no return"); } else if (this.Direction == "Left") { Score("P1", "Time Limit - no bounce"); } else if (this.Direction == "Right" && bounce1) { Score("P1", "Time Limit - no return"); } else { Score("P2", "Time Limit - no bounce"); } } } // Get Ball xy coordinates DataPoint BallLocation = FindBall(multiSourceFrame); int xavg = (int)BallLocation.X; int yavg = (int)BallLocation.Y; // Check if location seems reasonable, set xavg to 1 (default no ball found value) if not if (AllData.Count > 0) { if (!ReasonableLocation(BallLocation)) { xavg = 1; } } // If good data point, analyze it if (xavg > 1) { // Get ball xyz coordinates using (DepthFrame depthFrame = multiSourceFrame.DepthFrameReference.AcquireFrame()) { CurrentXYZ = XYZLocation(depthFrame, xavg, yavg); } // Off (or rather under) table if (yavg < tableLevel - 100) { if (bounce1) { if (this.Direction == "Left") { Score("P2", "Below Table - no return"); } else { Score("P1", "Below Table - no return"); } } else if (served) { if (this.Direction == "Left") { Score("P1", "Below Table - no bounce"); } else { Score("P2", "Below Table - no bounce"); } } else { ServeIsBad(); } } // Determine direction and do game processing float xdelta = 0; float ydelta = 0; if (AllData.Count > 0) { xdelta = AllData[AllData.Count - 1].X - xavg; ydelta = AllData[AllData.Count - 1].Y - yavg; } if (served) { // Horizontal direction determination and direction change detection if (xdelta > 10) { if (this.Direction == "Right") { ChangeDirection(ydelta); changeDir = true; } this.Direction = "Left"; PossibleNet = false; } else if (xdelta < -10) { if (this.Direction == "Left") { ChangeDirection(ydelta); changeDir = true; } this.Direction = "Right"; PossibleNet = false; } // Check for possible hitting net - two frames at net means a score else if (xavg - netLocation < 20 && xavg - netLocation > 0 && Direction == "Left") { if (PossibleNet) { Score("P1", "Hit Net"); } else { PossibleNet = true; } } else if (xavg - netLocation > -20 && xavg - netLocation < 0 && Direction == "Right") { if (PossibleNet) { Score("P2", "Hit Net"); } else { PossibleNet = true; } } // Vertical direction and bounce detection if (ydelta > 5) { this.VertDir = "Down"; PossibleBounce = false; } else if (ydelta < -5) { if (this.VertDir == "Down" && !changeDir && yavg - tableLevel < 100) // Log possible bounce { PossibleBounce = true; this.hitTime = DateTime.Now; this.tempBounce = new DataPoint(xavg, yavg, 0, 0); } else if (PossibleBounce) // if no direction change one frame later, possible bounce is a bounce { if (!changeDir) { // Handle bounce processing if (PreviousXYZ.X != 0 && PreviousXYZ.Y != 0 && PreviousXYZ.Z != 0 && PreviousXYZ.Z > GlobalClass.minZ && this.serveBounce) { HitLocation bounceXYZ = new HitLocation(); bounceXYZ.X = PreviousXYZ.X; bounceXYZ.Y = PreviousXYZ.Y; bounceXYZ.Z = PreviousXYZ.Z; bounceXYZ.Volley = VolleyNumber; this.Bounces.Add(bounceXYZ); } Bounce(new DataPoint(tempBounce.X, tempBounce.Y, 0, (float)(DateTime.Now.Subtract(this.timeStarted).TotalSeconds))); } PossibleBounce = false; } this.VertDir = "Up"; } else if (PossibleBounce) { PossibleBounce = false; } else if (ydelta <= 0) { if (this.VertDir == "Down" && !changeDir && yavg - tableLevel < 100) // Log possible bounce { PossibleBounce = true; this.hitTime = DateTime.Now; this.tempBounce = new DataPoint(xavg, yavg, 0, 0); } } } else if (inVolley) // if not served yet, check for serve hit { // check for low, shallow bounce serve int xStartDelta = 0; if (Server == "P1") { xStartDelta = xavg - (int)startLocation.X; } else { xStartDelta = (int)startLocation.X - xavg; } if (xStartDelta > 50) { served = true; VolleyStartTime = DateTime.Now; if (ydelta > 10) { VertDir = "Down"; } else if (yavg - tableLevel < 50) { VertDir = "Up"; serveBounce = true; } if (xdelta > 0) { this.Direction = "Left"; } else { this.Direction = "Right"; } } // Serve defined as moving in x and negative y (slower serve, easy bounce detection) else if ((xdelta > 10 || xdelta < 10) && ydelta > 10 && xStartDelta > 50) { this.served = true; VolleyStartTime = DateTime.Now; this.VertDir = "Down"; if (xdelta > 0) { this.Direction = "Left"; } else { this.Direction = "Right"; } } } // Add current location to points list this.AllData.Add(new DataPoint(xavg, yavg, 0, (float)(DateTime.Now.Subtract(this.timeStarted).TotalSeconds))); double currentSpeed = 0; // Check ball speed if (PreviousXYZ != null) { currentSpeed = BallSpeed(CurrentXYZ, PreviousXYZ); } if (currentSpeed > maxSpeed) { maxSpeed = currentSpeed; } PreviousXYZ = CurrentXYZ; } } else // Check for ball in start position to start volley { // Get Ball xy coordinates DataPoint BallLocation = FindBall(multiSourceFrame); int xavg = (int)BallLocation.X; int yavg = (int)BallLocation.Y; CheckStartPosition(xavg, yavg); } } }