TouchDetector.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. using BBIWARG.Images;
  2. using BBIWARG.Input.InputHandling;
  3. using BBIWARG.Recognition.FingerRecognition;
  4. using BBIWARG.Recognition.PalmRecognition;
  5. using BBIWARG.Utility;
  6. using Emgu.CV;
  7. using Emgu.CV.Structure;
  8. using Emgu.CV.UI;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Drawing;
  12. namespace BBIWARG.Recognition.TouchRecognition
  13. {
  14. /// <summary>
  15. /// Detects touches by flood filling around a small area around each finger tip and counting the number of affected pixels. If the finger is touching or slightly hovering above something, the flood fill spreads into the touched object and the number of affected pixels is higher.
  16. /// </summary>
  17. internal class TouchDetector
  18. {
  19. /// <summary>
  20. /// the depth image of the current frame
  21. /// </summary>
  22. private DepthImage depthImage;
  23. /// <summary>
  24. /// the fingers of the current frame
  25. /// </summary>
  26. private List<Finger> fingers;
  27. /// <summary>
  28. /// the palms of the current frame
  29. /// </summary>
  30. private List<Palm> palms;
  31. /// <summary>
  32. /// the detected touches in the current frame
  33. /// </summary>
  34. private List<Touch> touches;
  35. /// <summary>
  36. /// Detects touches in the current frame and stores them in frameData.detectedTouchEvents
  37. /// </summary>
  38. /// <param name="frameData">the current frame</param>
  39. public void detectTouches(FrameData frameData)
  40. {
  41. depthImage = frameData.DepthImage;
  42. fingers = frameData.TrackedFingers;
  43. palms = frameData.TrackedPalms;
  44. touches = new List<Touch>();
  45. if (palms.Count > 0)
  46. {
  47. foreach (Finger finger in fingers)
  48. {
  49. Touch touch = detectTouch(finger);
  50. if (touch != null)
  51. touches.Add(touch);
  52. }
  53. }
  54. frameData.DetectedTouches = touches;
  55. }
  56. /// <summary>
  57. /// Detects if a finger is touching a palm and either returns a new Touch or null
  58. /// </summary>
  59. /// <param name="finger">the fingers</param>
  60. /// <returns>a new Touch or null</returns>
  61. private Touch detectTouch(Finger finger)
  62. {
  63. Vector2D tipPoint = finger.TipPoint;
  64. Vector2D direction = finger.Direction;
  65. Vector2D tipPointInside = tipPoint.moveWithinBound(depthImage.Size, direction.getInverse(), Parameters.TouchTipInsideFactor);
  66. Vector2D tipPointOutside = tipPoint.moveWithinBound(depthImage.Size, direction, Parameters.TouchTipOutsideFactor);
  67. foreach (Palm palm in palms)
  68. {
  69. if (palm.isInside(tipPointOutside))
  70. {
  71. Image<Gray, byte> touchMask = getTouchMask(tipPointInside);
  72. int touchPixels = touchMask.CountNonzero()[0];
  73. int numPixels = touchMask.Width * touchMask.Height;
  74. float touchValue = touchPixels / (float)numPixels;
  75. if (touchValue > Parameters.TouchMinTouchValue)
  76. {
  77. Touch touch = new Touch(tipPointOutside, finger, palm);
  78. return touch;
  79. }
  80. }
  81. }
  82. return null;
  83. }
  84. /// <summary>
  85. /// Gets an image of a small area around the desired touch point (copied from the depth image)
  86. /// </summary>
  87. /// <param name="touchPoint">the touch position</param>
  88. /// <returns>image of the touch area around the touch position</returns>
  89. private Image<Gray, byte> getTouchMask(Vector2D touchPoint)
  90. {
  91. int minX = Math.Max(touchPoint.IntX - 2 * Parameters.TouchAreaSize / 3, 0);
  92. int maxX = Math.Min(touchPoint.IntX + Parameters.TouchAreaSize / 3, depthImage.Size.Width - 1);
  93. int minY = Math.Max(touchPoint.IntY - 2 * Parameters.TouchAreaSize / 3, 0);
  94. int maxY = Math.Min(touchPoint.IntY + Parameters.TouchAreaSize / 3, depthImage.Size.Height - 1);
  95. Vector2D relTouchPoint = new Vector2D(touchPoint.IntX - minX, touchPoint.IntY - minY);
  96. Rectangle rect = new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1);
  97. Image<Gray, byte> touchArea = depthImage.Image.Copy(rect);
  98. Image<Gray, byte> touchMask = new Image<Gray, byte>(rect.Width + 2, rect.Height + 2);
  99. MCvConnectedComp comp = new MCvConnectedComp();
  100. CvInvoke.cvFloodFill(touchArea, relTouchPoint, new MCvScalar(255), new MCvScalar(Parameters.TouchFloodfillDownDiff), new MCvScalar(Parameters.TouchFloodfillUpDiff), out comp, Emgu.CV.CvEnum.CONNECTIVITY.EIGHT_CONNECTED, Emgu.CV.CvEnum.FLOODFILL_FLAG.DEFAULT, touchMask);
  101. Rectangle cropRect = new Rectangle(1, 1, rect.Width, rect.Height);
  102. return touchMask.Copy(cropRect);
  103. }
  104. }
  105. }