PalmDetector.cs 9.1 KB

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