PalmDetector.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. using System.Drawing;
  7. using Emgu.CV;
  8. using Emgu.CV.Structure;
  9. using bbiwarg.Utility;
  10. using bbiwarg.Images;
  11. using bbiwarg.Detectors.Fingers;
  12. namespace bbiwarg.Detectors.Palm
  13. {
  14. class PalmDetector
  15. {
  16. private int width, height;
  17. private DepthImage depthImage;
  18. private EdgeImage edgeImage;
  19. private PalmImage palmImage;
  20. private Image<Gray, Byte> handImage;
  21. private Image<Gray, Byte> pointingHandMask;
  22. private List<Finger> fingers;
  23. private Contour<Point> palmContour;
  24. private List<MCvConvexityDefect> convexityDefects;
  25. private bool valid = false;
  26. private Vector2D topLeft;
  27. private Vector2D topRight;
  28. private Vector2D bottomLeft;
  29. private Vector2D bottomRight;
  30. public Quadrangle PalmQuad;
  31. public PalmDetector(DepthImage depthImage, EdgeImage edgeImage, List<Finger> detectedFingers, PalmImage palmImage)
  32. {
  33. // TODO: determine which fingers are index or thumb-fingers and detect palm in thumb-hand
  34. width = depthImage.Width;
  35. height = depthImage.Height;
  36. this.depthImage = depthImage;
  37. this.edgeImage = edgeImage;
  38. this.palmImage = palmImage;
  39. // dst = (src > (MaxDepth - MinDepth)) ? 0 : 1
  40. handImage = depthImage.Image.ThresholdBinaryInv(new Gray(depthImage.MaxDepth - depthImage.MinDepth - 1), new Gray(1)).Convert<Gray, Byte>();
  41. fingers = getFingersWithoutThumb(detectedFingers);
  42. buildPointingHandMask();
  43. handImage = handImage.And(pointingHandMask);
  44. findLongestPalmContour();
  45. if (palmContour != null)
  46. {
  47. findConvexityDefactsSortedByDepth();
  48. removeConvexityDefectsNearFingerTips();
  49. findHandPoints();
  50. if (valid)
  51. {
  52. draw();
  53. }
  54. }
  55. }
  56. private List<Finger> getFingersWithoutThumb(List<Finger> detectedFingers)
  57. {
  58. Finger leftMost = null;
  59. float minX = float.MaxValue;
  60. foreach (Finger f in detectedFingers)
  61. {
  62. float midX = ((f.Hand + f.Tip) / 2).X;
  63. if (midX < minX)
  64. {
  65. minX = midX;
  66. leftMost = f;
  67. }
  68. }
  69. List<Finger> result = new List<Finger>();
  70. foreach (Finger f in detectedFingers)
  71. {
  72. if (f != leftMost)
  73. result.Add(f);
  74. }
  75. return result;
  76. }
  77. private void fillFingerSlices(Image<Gray, Byte> image, byte val)
  78. {
  79. foreach (Finger f in fingers)
  80. {
  81. foreach (FingerSlice s in f.SliceTrail.Slices)
  82. {
  83. image.Draw(new LineSegment2DF(s.Start, s.End), new Gray(val), 1);
  84. }
  85. }
  86. }
  87. private Finger getLongestFinger()
  88. {
  89. float maxLength = 0;
  90. Finger longest = null;
  91. foreach (Finger f in fingers)
  92. {
  93. if (f.LineSegment.Length > maxLength)
  94. {
  95. maxLength = f.LineSegment.Length;
  96. longest = f;
  97. }
  98. }
  99. return longest;
  100. }
  101. private Point getPointInPointingHand()
  102. {
  103. Finger finger = getLongestFinger();
  104. if (finger == null)
  105. return new Point(0, 0);
  106. Vector2D direction = (finger.Hand - finger.Tip).normalize();
  107. Vector2D pos = finger.Hand + 20 * direction;
  108. return new Point(HelperFunctions.thresholdRange<int>(0, width - 1, pos.IntX), HelperFunctions.thresholdRange<int>(0, height - 1, pos.IntY));
  109. }
  110. private void buildPointingHandMask()
  111. {
  112. pointingHandMask = new Image<Gray, byte>(width, height, new Gray(0));
  113. fillFingerSlices(pointingHandMask, 1);
  114. pointingHandMask = pointingHandMask.Dilate(1);
  115. // dst = (src > 0) ? 1 : 0;
  116. pointingHandMask = pointingHandMask.Or(edgeImage.Image.ThresholdBinary(new Gray(0), new Gray(1)));
  117. pointingHandMask = pointingHandMask.Dilate(1);
  118. MCvConnectedComp tmp = new MCvConnectedComp();
  119. // fill with value 2
  120. CvInvoke.cvFloodFill(pointingHandMask.Ptr, getPointInPointingHand(), new MCvScalar(2), new MCvScalar(0), new MCvScalar(0), out tmp, 0, IntPtr.Zero);
  121. // dst = (src > 1) ? 0 : 1 (src > 1 <-> src == 2 <-> src filled by flood fill)
  122. pointingHandMask = pointingHandMask.ThresholdBinaryInv(new Gray(1), new Gray(1));
  123. pointingHandMask = pointingHandMask.Erode(1);
  124. fillFingerSlices(pointingHandMask, 0);
  125. pointingHandMask = pointingHandMask.Erode(2);
  126. }
  127. private void findLongestPalmContour()
  128. {
  129. Contour<Point> contour = handImage.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL);
  130. palmContour = contour;
  131. double maxPerimeter = 0;
  132. while (contour != null)
  133. {
  134. if (contour.Perimeter > maxPerimeter)
  135. {
  136. maxPerimeter = contour.Perimeter;
  137. palmContour = contour;
  138. }
  139. contour = contour.HNext;
  140. }
  141. }
  142. private void findConvexityDefactsSortedByDepth()
  143. {
  144. convexityDefects = new List<MCvConvexityDefect>(palmContour.GetConvexityDefacts(new MemStorage(), Emgu.CV.CvEnum.ORIENTATION.CV_CLOCKWISE));
  145. convexityDefects.Sort(delegate(MCvConvexityDefect d1, MCvConvexityDefect d2)
  146. {
  147. if (d1.Depth < d2.Depth)
  148. return 1;
  149. else if (d1.Depth > d2.Depth)
  150. return -1;
  151. return 0;
  152. });
  153. }
  154. private void removeConvexityDefectsNearFingerTips()
  155. {
  156. List<MCvConvexityDefect> newDefects = new List<MCvConvexityDefect>();
  157. foreach (MCvConvexityDefect d in convexityDefects)
  158. {
  159. float minFingerTipDist = float.MaxValue;
  160. foreach (Finger f in fingers)
  161. {
  162. float dist = f.Tip.getDistanceTo(new Vector2D(d.DepthPoint));
  163. if (dist < minFingerTipDist)
  164. minFingerTipDist = dist;
  165. }
  166. if (minFingerTipDist > 20)
  167. newDefects.Add(d);
  168. }
  169. convexityDefects = newDefects;
  170. }
  171. private void findHandPoints()
  172. {
  173. if (convexityDefects.Count > 0)
  174. {
  175. MCvConvexityDefect thumbDefect = convexityDefects[0];
  176. Vector2D thumb = new Vector2D(thumbDefect.DepthPoint);
  177. Vector2D thumbDefectStart = new Vector2D(thumbDefect.StartPoint);
  178. Vector2D thumbDefectEnd = new Vector2D(thumbDefect.EndPoint);
  179. Vector2D handLength, handWidth;
  180. if (thumb.getDistanceTo(thumbDefectStart) > thumb.getDistanceTo(thumbDefectEnd))
  181. {
  182. //right hand
  183. handLength = thumbDefectStart - thumb;
  184. handWidth = 0.8f * new Vector2D(-handLength.Y, handLength.X);
  185. topLeft = thumbDefectStart;
  186. bottomLeft = thumb - 0.4f * handLength;
  187. bottomRight = bottomLeft + handWidth;
  188. topRight = bottomRight + 1.2f * handLength - 0.3f * handWidth;
  189. }
  190. else
  191. {
  192. //left hand
  193. handLength = thumbDefectEnd - thumb;
  194. handWidth = 0.8f * new Vector2D(handLength.Y, -handLength.X);
  195. topRight = thumbDefectEnd;
  196. bottomRight = thumb - 0.4f * handLength;
  197. bottomLeft = bottomRight + handWidth;
  198. topLeft = bottomLeft + 1.2f * handLength - 0.3f * handWidth;
  199. }
  200. PalmQuad = new Quadrangle(bottomLeft, topLeft, topRight, bottomRight);
  201. valid = true;
  202. }
  203. else
  204. {
  205. PalmQuad = null;
  206. valid = false;
  207. }
  208. }
  209. private void draw()
  210. {
  211. palmImage.drawContour(palmContour);
  212. if (PalmQuad != null)
  213. {
  214. Vector2D[] vertices = PalmQuad.Vertices;
  215. for (int i = 0; i < 4; ++i)
  216. palmImage.drawLine(new LineSegment2DF(vertices[i], vertices[(i + 1) % 4]), PalmImageState.palmRect);
  217. palmImage.drawGrid(new Vector2D(vertices[0]), new Vector2D(vertices[1]), new Vector2D(vertices[2]), new Vector2D(vertices[3]));
  218. }
  219. }
  220. }
  221. }