|
@@ -1,115 +1,154 @@
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Drawing;
|
|
|
using System.Linq;
|
|
|
using System.Text;
|
|
|
using System.Threading.Tasks;
|
|
|
-using bbiwarg.Recognition.HandRecognition;
|
|
|
+using bbiwarg.Images;
|
|
|
using bbiwarg.Utility;
|
|
|
-using Emgu.CV.Structure;
|
|
|
+using bbiwarg.Recognition.FingerRecognition;
|
|
|
+using bbiwarg.Recognition.HandRecognition;
|
|
|
using Emgu.CV;
|
|
|
+using Emgu.CV.Structure;
|
|
|
|
|
|
namespace bbiwarg.Recognition.PalmRecognition
|
|
|
{
|
|
|
class PalmDetector
|
|
|
{
|
|
|
+ private DepthImage depthImage;
|
|
|
private List<Hand> hands;
|
|
|
- private List<Hand> palmHands;
|
|
|
|
|
|
- public List<Palm> Palms { get; private set; }
|
|
|
+ public List<Palm> Palms;
|
|
|
|
|
|
- public PalmDetector(List<Hand> hands)
|
|
|
+ public PalmDetector(DepthImage depthImage, List<Hand> hands)
|
|
|
{
|
|
|
+ this.depthImage = depthImage;
|
|
|
this.hands = hands;
|
|
|
|
|
|
- findPalmHands();
|
|
|
- createPalms();
|
|
|
+ findPalms();
|
|
|
}
|
|
|
|
|
|
- private void findPalmHands()
|
|
|
- {
|
|
|
- palmHands = new List<Hand>();
|
|
|
-
|
|
|
- foreach (Hand hand in hands)
|
|
|
- {
|
|
|
- if (hand.ThumbDefect != null)
|
|
|
- palmHands.Add(hand);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void createPalms()
|
|
|
+ private void findPalms()
|
|
|
{
|
|
|
Palms = new List<Palm>();
|
|
|
- foreach (Hand palmHand in palmHands)
|
|
|
+ foreach (Hand hand in hands)
|
|
|
{
|
|
|
- ConvexityDefect thumbDefect = palmHand.ThumbDefect;
|
|
|
- Vector2D directionWristFinger = thumbDefect.VectorLong.normalize();
|
|
|
- Vector2D directionFingerWrist = directionWristFinger.getInverse();
|
|
|
- Vector2D directionUpperLower = directionWristFinger.getOrthogonal(palmHand.Side == HandSide.Right);
|
|
|
- Vector2D directionLowerUpper = directionUpperLower.getInverse();
|
|
|
-
|
|
|
- //handLength
|
|
|
- Vector2D handLength = 1.5f * thumbDefect.VectorLong;
|
|
|
-
|
|
|
- //fingersUpper
|
|
|
- Vector2D fingersUpper = thumbDefect.OuterLong;
|
|
|
- bool handBelow = true;
|
|
|
- while (handBelow && fingersUpper.isInBound())
|
|
|
+ if (hand.Fingers.Count == 1)
|
|
|
{
|
|
|
- fingersUpper += directionWristFinger;
|
|
|
- Vector2D below = fingersUpper.copy();
|
|
|
- bool handBelowFound = false;
|
|
|
- while (!handBelowFound && below.getDistanceTo(fingersUpper) < 2 * Parameters.FingerMaxWidth && below.isInBound())
|
|
|
+ List<ConvexityDefect> convexityDefects = findConvexityDefects(hand);
|
|
|
+ ConvexityDefect thumbDefect = findThumbDefect(hand.Fingers[0], convexityDefects);
|
|
|
+ if (thumbDefect != null)
|
|
|
{
|
|
|
- handBelowFound = palmHand.isInside(below);
|
|
|
- below += directionUpperLower;
|
|
|
+ Palm palm = createPalm(hand, thumbDefect);
|
|
|
+ Palms.Add(palm);
|
|
|
}
|
|
|
- handBelow = handBelowFound;
|
|
|
}
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- //wristUpper
|
|
|
- Vector2D wristUpper = thumbDefect.Inner + 5 * directionFingerWrist;
|
|
|
- while (wristUpper.isInBound() && palmHand.isInside(wristUpper))
|
|
|
- wristUpper += directionFingerWrist;
|
|
|
-
|
|
|
- //handWidth
|
|
|
- Vector2D handWidth = getHandWidth(palmHand, directionUpperLower);
|
|
|
+ private List<ConvexityDefect> findConvexityDefects(Hand hand)
|
|
|
+ {
|
|
|
+ List<ConvexityDefect> convexityDefects;
|
|
|
+ using (MemStorage ms = new MemStorage())
|
|
|
+ {
|
|
|
+ Contour<Point> contour = hand.Mask.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL);
|
|
|
+ List<MCvConvexityDefect> mcvConvexityDefects = new List<MCvConvexityDefect>(contour.GetConvexityDefacts(ms, Emgu.CV.CvEnum.ORIENTATION.CV_CLOCKWISE));
|
|
|
|
|
|
- //wristLower
|
|
|
- Vector2D wristLower = wristUpper + handWidth;
|
|
|
+ convexityDefects = new List<ConvexityDefect>();
|
|
|
+ foreach (MCvConvexityDefect defect in mcvConvexityDefects)
|
|
|
+ convexityDefects.Add(new ConvexityDefect(defect));
|
|
|
+ }
|
|
|
+ return convexityDefects;
|
|
|
+ }
|
|
|
|
|
|
- //fingersLower
|
|
|
- Vector2D fingersLower = wristLower + 0.75f * handLength - 0.25f * handWidth;
|
|
|
+ private ConvexityDefect findThumbDefect(Finger thumb, List<ConvexityDefect> convexityDefects)
|
|
|
+ {
|
|
|
+ convexityDefects.Sort((cd1, cd2) => (cd2.Depth.CompareTo(cd1.Depth)));
|
|
|
+ foreach (ConvexityDefect defect in convexityDefects)
|
|
|
+ {
|
|
|
+ if (defect.isPossibleThumbDefect(thumb))
|
|
|
+ return defect;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
|
|
|
+ private Palm createPalm(Hand hand, ConvexityDefect thumbDefect)
|
|
|
+ {
|
|
|
+ HandSide side = determineHandSide(thumbDefect);
|
|
|
+ Vector2D wristUpper = findWristUpper(hand, thumbDefect);
|
|
|
+ Vector2D fingersUpper = findFingersUpper(hand, thumbDefect, side);
|
|
|
+ float palmWidth = findPalmWidth(hand, thumbDefect, side);
|
|
|
+ float palmLength = wristUpper.getDistanceTo(fingersUpper);
|
|
|
+ Vector2D directionWristFingers = thumbDefect.VectorLong.normalize();
|
|
|
+ Vector2D directionUpperLower = thumbDefect.VectorLong.getOrthogonal(side == HandSide.Right).normalize();
|
|
|
+ Vector2D wristLower = wristUpper.moveWithinBound(directionUpperLower, palmWidth);
|
|
|
+ Vector2D fingersLower = (wristUpper + 0.75f * palmLength * directionWristFingers + 0.75f * palmWidth * directionUpperLower).moveInBound((directionUpperLower + directionWristFingers).normalize());
|
|
|
+
|
|
|
+ return new Palm(hand, thumbDefect, side, wristUpper, fingersUpper, fingersLower, wristLower);
|
|
|
+ }
|
|
|
|
|
|
- Palm palm = new Palm(palmHand, wristUpper, wristLower, fingersLower, fingersUpper);
|
|
|
- Palms.Add(palm);
|
|
|
+ private HandSide determineHandSide(ConvexityDefect thumbDefect)
|
|
|
+ {
|
|
|
+ if (thumbDefect.VectorShort.crossProduct(thumbDefect.VectorLong) < 0)
|
|
|
+ return HandSide.Right;
|
|
|
+ else
|
|
|
+ return HandSide.Left;
|
|
|
+ }
|
|
|
|
|
|
+ private Vector2D findWristUpper(Hand hand, ConvexityDefect thumbDefect)
|
|
|
+ {
|
|
|
+ Vector2D wristDirection = thumbDefect.VectorLong.getInverse().normalize();
|
|
|
+ Vector2D wristUpper = thumbDefect.Inner.moveWithinBound(wristDirection, 5f);
|
|
|
+ Vector2D wristUpperNext = wristUpper + wristDirection;
|
|
|
+ while (wristUpperNext.isInBound() && hand.isInside(wristUpperNext))
|
|
|
+ {
|
|
|
+ wristUpper = wristUpperNext;
|
|
|
+ wristUpperNext += wristDirection;
|
|
|
}
|
|
|
+ return wristUpper;
|
|
|
}
|
|
|
|
|
|
- private Vector2D getHandWidth(Hand palmHand, Vector2D directionUpperLower)
|
|
|
+ private Vector2D findFingersUpper(Hand hand, ConvexityDefect thumbDefect, HandSide side)
|
|
|
{
|
|
|
- Vector2D lineStart = palmHand.ThumbDefect.Inner;
|
|
|
- Vector2D lineEnd = palmHand.ThumbDefect.OuterLong;
|
|
|
- Vector2D step = (lineEnd - lineStart) / Parameters.PalmNumPositionsForHandWidth;
|
|
|
- Vector2D currentStart = lineStart;
|
|
|
+ Vector2D fingersDirection = thumbDefect.VectorLong.normalize();
|
|
|
+ Vector2D lowerDirection = fingersDirection.getOrthogonal(side == HandSide.Right).normalize();
|
|
|
+ Vector2D fingersUpper = thumbDefect.OuterLong;
|
|
|
+ Vector2D fingersUpperNext = fingersUpper + fingersDirection;
|
|
|
+ bool handBelow = true;
|
|
|
+ while (handBelow && fingersUpperNext.isInBound())
|
|
|
+ {
|
|
|
+ fingersUpper = fingersUpperNext;
|
|
|
+ fingersUpperNext += fingersDirection;
|
|
|
+ Vector2D below = fingersUpper.copy();
|
|
|
+ handBelow = false;
|
|
|
+ int distance = 0;
|
|
|
+ while (!handBelow && distance < Parameters.FingerMaxWidth && below.isInBound())
|
|
|
+ {
|
|
|
+ handBelow = hand.isInside(below);
|
|
|
+ below += lowerDirection;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return fingersUpper;
|
|
|
+ }
|
|
|
|
|
|
- Vector2D maxWidth = null;
|
|
|
- float maxWidthLength = float.MinValue;
|
|
|
+ private float findPalmWidth(Hand hand, ConvexityDefect thumbDefect, HandSide side)
|
|
|
+ {
|
|
|
+ Vector2D lowerDirection = thumbDefect.VectorLong.getOrthogonal(side == HandSide.Right).normalize();
|
|
|
+ Vector2D current = thumbDefect.Inner;
|
|
|
+ Vector2D step = thumbDefect.VectorLong / Parameters.PalmNumPositionsForPalmWidth;
|
|
|
|
|
|
- for (int i = 0; i < Parameters.PalmNumPositionsForHandWidth; i++)
|
|
|
+ float maxWidth = float.MinValue;
|
|
|
+ for (int i = 0; i < Parameters.PalmNumPositionsForPalmWidth; i++)
|
|
|
{
|
|
|
- Vector2D lower = currentStart + 10 * directionUpperLower;
|
|
|
- while (lower.isInBound() && palmHand.isInside(lower))
|
|
|
- lower += directionUpperLower;
|
|
|
- Vector2D width = (lower - currentStart);
|
|
|
- float length = width.Length;
|
|
|
- if (length > maxWidthLength)
|
|
|
+ Vector2D lower = current.moveWithinBound(lowerDirection, 10f);
|
|
|
+ float width = current.getDistanceTo(lower);
|
|
|
+ while (lower.isInBound() && hand.isInside(lower))
|
|
|
{
|
|
|
- maxWidth = width;
|
|
|
- maxWidthLength = length;
|
|
|
+ lower += lowerDirection;
|
|
|
+ width += 1;
|
|
|
}
|
|
|
- currentStart += step;
|
|
|
+ maxWidth = Math.Max(maxWidth, width);
|
|
|
+ current += step;
|
|
|
}
|
|
|
return maxWidth;
|
|
|
}
|