PalmDetector.cs 8.8 KB

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