/// <summary> /// Calculates the smaller angle between two points. /// </summary> /// <param name="point1">Source point.</param> /// <param name="point2">Source point.</param> /// <returns>Returns the angle in radians.</returns> public static float angle(PointFT point1, PointFT point2) { return((float)Math.Acos((point1.X * point2.X + point1.Y * point2.Y) / (length(point1) * length(point2)))); }
/// <summary> /// Copy and create a new instance of PointFT. /// </summary> /// <param name="other">The PointFT to copy.</param> public PointFT(PointFT other) { this.X = other.X; this.Y = other.Y; }
/// <summary> /// Calculates the Euclidean distance between two points. /// </summary> /// <param name="point1">Source point.</param> /// <param name="point2">Source point.</param> /// <returns>Returns the distance between the points squared.</returns> public static float distanceEuclideanSquared(PointFT point1, PointFT point2) { return((point1.X - point2.X) * (point1.X - point2.X) + (point1.Y - point2.Y) * (point1.Y - point2.Y)); }
/// <summary> /// Calculates the dot product of two points. Returns a floating point value between -1 and 1. /// </summary> /// <param name="point1">Source point.</param> /// <param name="point2">Source point.</param> /// <returns>Returns the dot product of the source points.</returns> public static float dot(PointFT point1, PointFT point2) { return((point1.X * point2.X + point1.Y * point2.Y) / (length(point1) * length(point2))); }
/// <summary> /// Calculates the Euclidean distance between two points. /// </summary> /// <param name="point1">Source point.</param> /// <param name="point2">Source point.</param> /// <returns>Returns the distance between the points.</returns> public static float distanceEuclidean(PointFT point1, PointFT point2) { return((float)Math.Sqrt((point1.X - point2.X) * (point1.X - point2.X) + (point1.Y - point2.Y) * (point1.Y - point2.Y))); }
/// <summary> /// Calculate the number of points you have to cross to go from one point to another. /// </summary> /// <param name="point1">Source point.</param> /// <param name="point2">Source point.</param> /// <returns>Returns the distance between the points.</returns> public static int distance(PointFT point1, PointFT point2) { return(Math.Abs(point1.X - point2.X) + Math.Abs(point1.Y - point2.Y)); }
/// <summary> /// Returns the length of the source point. /// </summary> /// <returns>The length of the point.</returns> public static float length(PointFT point1) { return((float)Math.Sqrt(point1.X * point1.X + point1.Y * point1.Y)); }
private List <Hand> localizeHands(bool[][] valid) { int i, j, k; List <Hand> hands = new List <Hand>(); List <PointFT> insidePoints = new List <PointFT>(); List <PointFT> contourPoints = new List <PointFT>(); bool[][] contour = new bool[valid.Length][]; for (i = 0; i < valid.Length; ++i) { contour[i] = new bool[valid[0].Length]; } // Divide points in contour and inside points int count = 0; for (i = 1; i < valid.Length - 1; ++i) { for (j = 1; j < valid[i].Length - 1; ++j) { if (valid[i][j]) { // Count the number of valid adjacent points count = this.numValidPixelAdjacent(ref i, ref j, ref valid); if (count == 4) // Inside { insidePoints.Add(new PointFT(i, j)); } else // Contour { contour[i][j] = true; contourPoints.Add(new PointFT(i, j)); } } } } // Create the sorted contour list, using the turtle algorithm for (i = 0; i < contourPoints.Count; ++i) { Hand hand = new Hand(); // If it is a possible start point if (contour[contourPoints[i].X][contourPoints[i].Y]) { // Calculate the contour hand.contour = CalculateFrontier(ref valid, contourPoints[i], ref contour); // Check if the contour is big enough to be a hand if (hand.contour.Count / (contourPoints.Count * 1.0f) > 0.20f && hand.contour.Count > settings.k) { // Calculate the container box hand.calculateContainerBox(settings.containerBoxReduction); // Add the hand to the list hands.Add(hand); } // Don't look for more hands, if we reach the limit if (hands.Count >= settings.maxTrackedHands) { break; } } } // Allocate the inside points to the correct hand using its container box //List<int> belongingHands = new List<int>(); for (i = 0; i < insidePoints.Count; ++i) { for (j = 0; j < hands.Count; ++j) { if (hands[j].isPointInsideContainerBox(insidePoints[i])) { hands[j].inside.Add(insidePoints[i]); //belongingHands.Add(j); } } // A point can only belong to one hand, if not we don't take that point into account /*if (belongingHands.Count == 1) * { * hands[belongingHands.ElementAt(0)].inside.Add(insidePoints[i]); * } * belongingHands.Clear();*/ } // Find the center of the palm float min, max, distance = 0; for (i = 0; i < hands.Count; ++i) { max = float.MinValue; for (j = 0; j < hands[i].inside.Count; j += settings.findCenterInsideJump) { min = float.MaxValue; for (k = 0; k < hands[i].contour.Count; k += settings.findCenterInsideJump) { distance = PointFT.distanceEuclidean(hands[i].inside[j], hands[i].contour[k]); if (!hands[i].isCircleInsideContainerBox(hands[i].inside[j], distance)) { continue; } if (distance < min) { min = distance; } if (min < max) { break; } } if (max < min && min != float.MaxValue) { max = min; hands[i].palm = hands[i].inside[j]; } } } // Find the fingertips PointFT p1, p2, p3, pAux, r1, r2; int size; double angle; int jump; for (i = 0; i < hands.Count; ++i) { // Check if there is a point at the beginning to avoid checking the last ones of the list max = hands[i].contour.Count; size = hands[i].contour.Count; jump = (int)(size * settings.fingertipFindJumpPerc); for (j = 0; j < settings.k; j += 1) { p1 = hands[i].contour[(j - settings.k + size) % size]; p2 = hands[i].contour[j]; p3 = hands[i].contour[(j + settings.k) % size]; r1 = p1 - p2; r2 = p3 - p2; angle = PointFT.angle(r1, r2); if (angle > 0 && angle < settings.theta) { pAux = p3 + ((p1 - p3) / 2); if (PointFT.distanceEuclideanSquared(pAux, hands[i].palm) > PointFT.distanceEuclideanSquared(hands[i].contour[j], hands[i].palm)) { continue; } hands[i].fingertips.Add(hands[i].contour[j]); max = hands[i].contour.Count + j - jump; max = Math.Min(max, hands[i].contour.Count); j += jump; break; } } // Continue with the rest of the points for ( ; j < max; j += settings.findFingertipsJump) { p1 = hands[i].contour[(j - settings.k + size) % size]; p2 = hands[i].contour[j]; p3 = hands[i].contour[(j + settings.k) % size]; r1 = p1 - p2; r2 = p3 - p2; angle = PointFT.angle(r1, r2); if (angle > 0 && angle < settings.theta) { pAux = p3 + ((p1 - p3) / 2); if (PointFT.distanceEuclideanSquared(pAux, hands[i].palm) > PointFT.distanceEuclideanSquared(hands[i].contour[j], hands[i].palm)) { continue; } hands[i].fingertips.Add(hands[i].contour[j]); j += jump; } } } return(hands); }
public void depthFrameReady(object sender, DepthImageFrameReadyEventArgs e) { // Get the depth frame from Kinect DepthImageFrame frame = e.OpenDepthImageFrame(); // Check that the frame is not null if (frame == null) { return; } // Calculate the real distance for every pixel in the depth image int[] distances = generateDistances(frame); // Return a 0 or 1 matrix, which contains wich pixels are near enough bool[][] near = generateValidMatrix(frame, distances); // Return the tracked hands based on the near pixels hands = localizeHands(near); byte[] pixels = new byte[frame.PixelDataLength * 4]; // Free last depth Matrix Marshal.FreeHGlobal(depthPtr); depthPtr = Marshal.AllocHGlobal(pixels.Length); Marshal.Copy(pixels, 0, depthPtr, pixels.Length); // Create the bitmap int height = near.Length; int width = 0; if (near.Length > 0) { width = near[0].Length; } int stride = width * 4; depthImage = new Bitmap( width, height, stride, PixelFormat.Format32bppRgb, depthPtr); // Calculate 3D points for the hands for (int i = 0; i < hands.Count; ++i) { hands[i].calculate3DPoints(settings.screenWidth, settings.screenHeight, distances); } // Call the rest of the functions afterDepthReady(); // Draw fingertips and palm center Graphics gBmp = Graphics.FromImage(depthImage); Brush blueBrush = new SolidBrush(Color.Blue); Brush redBrush = new SolidBrush(Color.Red); for (int i = 0; i < hands.Count; ++i) { // Red point which is the center of the palm gBmp.FillEllipse(redBrush, hands[i].palm.Y - 5, hands[i].palm.X - 5, 10, 10); // Draw inside points //for (int j = 0; j < hands[i].inside.Length; ++j) //{ // Point p = hands[i].inside.Get(j); // depthImage.SetPixel(p.Y, p.X, Color.Blue); //} // Draw the contour of the hands for (int j = 0; j < hands[i].contour.Count; ++j) { PointFT p = hands[i].contour[j]; depthImage.SetPixel(p.Y, p.X, Color.Red); } // Blue points which represent the fingertips for (int j = 0; j < hands[i].fingertips.Count; ++j) { if (hands[i].fingertips[j].X != -1) { gBmp.FillEllipse(blueBrush, hands[i].fingertips[j].Y - 5, hands[i].fingertips[j].X - 5, 10, 10); } } } blueBrush.Dispose(); redBrush.Dispose(); gBmp.Dispose(); }