|
@@ -11,21 +11,16 @@ using Emgu.CV.Structure;
|
|
|
using bbiwarg.Utility;
|
|
|
using bbiwarg.Images;
|
|
|
using bbiwarg.Detectors.FingerDetection;
|
|
|
+using bbiwarg.Detectors.HandDetection;
|
|
|
using bbiwarg.Graphics;
|
|
|
|
|
|
namespace bbiwarg.Detectors.PalmDetection
|
|
|
{
|
|
|
class PalmDetector
|
|
|
{
|
|
|
- private int width, height;
|
|
|
private OutputImage outputImage;
|
|
|
- private EdgeImage edgeImage;
|
|
|
-
|
|
|
- private Image<Gray, Byte> handImage;
|
|
|
- private Image<Gray, Byte> pointingHandMask;
|
|
|
-
|
|
|
- private List<Finger> fingers;
|
|
|
|
|
|
+ private Hand palmHand, pointingHand;
|
|
|
private Contour<Point> palmContour;
|
|
|
private List<MCvConvexityDefect> convexityDefects;
|
|
|
private Vector2D thumbDefectStart;
|
|
@@ -34,176 +29,65 @@ namespace bbiwarg.Detectors.PalmDetection
|
|
|
|
|
|
private Kalman2DPositionFilter thumbDefectDepthFilter, thumbDefectStartFilter, thumbDefectEndFilter;
|
|
|
|
|
|
- private Vector2D topLeft;
|
|
|
- private Vector2D topRight;
|
|
|
- private Vector2D bottomLeft;
|
|
|
- private Vector2D bottomRight;
|
|
|
-
|
|
|
public Quadrangle PalmQuad { get; private set; }
|
|
|
|
|
|
public PalmDetector()
|
|
|
{
|
|
|
- // TODO: determine which fingers are index or thumb-fingers and detect palm in thumb-hand
|
|
|
thumbDefectDepthFilter = new Kalman2DPositionFilter(1.0e-2f, 1.0e-2f);
|
|
|
thumbDefectStartFilter = new Kalman2DPositionFilter(1.0e-2f, 1.0e-2f);
|
|
|
thumbDefectEndFilter = new Kalman2DPositionFilter(1.0e-2f, 1.0e-2f);
|
|
|
}
|
|
|
|
|
|
- public void findPalmQuad(DepthImage depthImage, EdgeImage edgeImage, OutputImage outputImage, List<Finger> trackedFingers)
|
|
|
+ public void findPalmQuad(OutputImage outputImage, List<Hand> hands)
|
|
|
{
|
|
|
- this.width = depthImage.Width;
|
|
|
- this.height = depthImage.Height;
|
|
|
-
|
|
|
- i1 = new OutputImage(width, height);
|
|
|
- i2 = new OutputImage(width, height);
|
|
|
- i3 = new OutputImage(width, height);
|
|
|
- i4 = new OutputImage(width, height);
|
|
|
- i5 = new OutputImage(width, height);
|
|
|
- i6 = new OutputImage(width, height);
|
|
|
- i7 = new OutputImage(width, height);
|
|
|
- i8 = new OutputImage(width, height);
|
|
|
- i9 = new OutputImage(width, height);
|
|
|
-
|
|
|
- if (trackedFingers.Count >= 2)
|
|
|
- {
|
|
|
- this.edgeImage = edgeImage;
|
|
|
- this.outputImage = outputImage;
|
|
|
-
|
|
|
- // dst = (src > (MaxDepth - MinDepth)) ? 0 : 1
|
|
|
- handImage = depthImage.Image.ThresholdBinaryInv(new Gray(depthImage.MaxDepth - depthImage.MinDepth - 1), new Gray(1)).Convert<Gray, Byte>();
|
|
|
+ this.outputImage = outputImage;
|
|
|
|
|
|
- fingers = getFingersWithoutThumb(trackedFingers);
|
|
|
- buildPointingHandMask();
|
|
|
- handImage = handImage.And(pointingHandMask);
|
|
|
-
|
|
|
- findLongestPalmContour();
|
|
|
- if (palmContour != null)
|
|
|
+ if (hands.Count == 2)
|
|
|
+ {
|
|
|
+ if (hands[0].Fingers.Count > 1)
|
|
|
{
|
|
|
- findConvexityDefectsSortedByDepth();
|
|
|
- removeConvexityDefectsCausedByFingers();
|
|
|
-
|
|
|
- findHandPoints();
|
|
|
+ pointingHand = hands[0];
|
|
|
+ palmHand = hands[1];
|
|
|
}
|
|
|
- }
|
|
|
- draw();
|
|
|
- }
|
|
|
-
|
|
|
- public void reset()
|
|
|
- {
|
|
|
- thumbDefectDepthFilter.reset();
|
|
|
- thumbDefectStartFilter.reset();
|
|
|
- thumbDefectEndFilter.reset();
|
|
|
- lastThumbDefectDepth = null;
|
|
|
- }
|
|
|
-
|
|
|
- private List<Finger> getFingersWithoutThumb(List<Finger> detectedFingers)
|
|
|
- {
|
|
|
- Finger leftMost = null;
|
|
|
- float minX = float.MaxValue;
|
|
|
- foreach (Finger f in detectedFingers)
|
|
|
- {
|
|
|
- float midX = ((f.Hand + f.Tip) / 2).X;
|
|
|
- if (midX < minX)
|
|
|
+ else if (hands[1].Fingers.Count > 1)
|
|
|
{
|
|
|
- minX = midX;
|
|
|
- leftMost = f;
|
|
|
+ pointingHand = hands[1];
|
|
|
+ palmHand = hands[0];
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- List<Finger> result = new List<Finger>();
|
|
|
- foreach (Finger f in detectedFingers)
|
|
|
- {
|
|
|
- if (f != leftMost)
|
|
|
- result.Add(f);
|
|
|
- }
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- private void fillFingerSlices(Image<Gray, Byte> image, byte val)
|
|
|
- {
|
|
|
- foreach (Finger f in fingers)
|
|
|
- {
|
|
|
- //foreach (FingerSlice s in f.SliceTrail.Slices)
|
|
|
+ else if (hands[0].Fingers[0].LineSegment.Length > hands[1].Fingers[0].LineSegment.Length)
|
|
|
{
|
|
|
- image.Draw(new LineSegment2DF(f.SliceTrail.Slices[0].Start, f.SliceTrail.Slices[0].End), new Gray(val), 1);
|
|
|
+ pointingHand = hands[0];
|
|
|
+ palmHand = hands[1];
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private Finger getLongestFinger()
|
|
|
- {
|
|
|
- float maxLength = 0;
|
|
|
- Finger longest = null;
|
|
|
- foreach (Finger f in fingers)
|
|
|
- {
|
|
|
-
|
|
|
- if (f.LineSegment.Length > maxLength)
|
|
|
+ else
|
|
|
{
|
|
|
- maxLength = f.LineSegment.Length;
|
|
|
- longest = f;
|
|
|
+ pointingHand = hands[1];
|
|
|
+ palmHand = hands[0];
|
|
|
}
|
|
|
- }
|
|
|
- return longest;
|
|
|
- }
|
|
|
|
|
|
- private Point getPointInPointingHand()
|
|
|
- {
|
|
|
- Finger finger = getLongestFinger();
|
|
|
- if (finger == null)
|
|
|
- return new Point(0, 0);
|
|
|
- Vector2D direction = (finger.Hand - finger.Tip).normalize();
|
|
|
- Vector2D pos = finger.Hand + direction;
|
|
|
+ findLongestPalmContour();
|
|
|
+ findConvexityDefectsSortedByDepth();
|
|
|
+ removeConvexityDefectsCausedByFingers();
|
|
|
+ findHandPoints();
|
|
|
+ }
|
|
|
|
|
|
- while (pos.isWithin(0, 0, width - 1, height - 1) && pointingHandMask.Data[pos.IntY, pos.IntX, 0] != 0)
|
|
|
- pos += direction;
|
|
|
-
|
|
|
- i3.fillCircle(pos.IntX, pos.IntY, 3, Color.Red);
|
|
|
-
|
|
|
- return pos;
|
|
|
+ draw();
|
|
|
}
|
|
|
|
|
|
- public OutputImage i1, i2, i3, i4, i5, i6, i7, i8, i9;
|
|
|
- private void buildPointingHandMask()
|
|
|
+ public void reset()
|
|
|
{
|
|
|
- pointingHandMask = new Image<Gray, byte>(width, height, new Gray(0));
|
|
|
-
|
|
|
- // dst = (src > 0) ? 1 : 0;
|
|
|
- pointingHandMask = pointingHandMask.Or(edgeImage.Image);
|
|
|
- i1.Image[0] = i1.Image[1] = i1.Image[2] = 255 * pointingHandMask;
|
|
|
-
|
|
|
- pointingHandMask = pointingHandMask.Dilate(2);
|
|
|
- i2.Image[0] = i2.Image[1] = i2.Image[2] = 255 * pointingHandMask;
|
|
|
-
|
|
|
- fillFingerSlices(pointingHandMask, 1);
|
|
|
- i3.Image[0] = i3.Image[1] = i3.Image[2] = 255 * pointingHandMask;
|
|
|
-
|
|
|
- //pointingHandMask = pointingHandMask.Dilate(1);
|
|
|
- //i4.Image[0] = i4.Image[1] = i4.Image[2] = 255 * pointingHandMask;
|
|
|
-
|
|
|
- MCvConnectedComp tmp = new MCvConnectedComp();
|
|
|
- // fill with value 2
|
|
|
- CvInvoke.cvFloodFill(pointingHandMask.Ptr, getPointInPointingHand(), new MCvScalar(2), new MCvScalar(0), new MCvScalar(0), out tmp, 0, IntPtr.Zero);
|
|
|
-
|
|
|
- // dst = (src > 1) ? 0 : 1 (src > 1 <-> src == 2 <-> src filled by flood fill)
|
|
|
- pointingHandMask = pointingHandMask.ThresholdBinaryInv(new Gray(1), new Gray(1));
|
|
|
- i5.Image[0] = i5.Image[1] = i5.Image[2] = 255 * pointingHandMask;
|
|
|
-
|
|
|
- pointingHandMask = pointingHandMask.Erode(6);
|
|
|
- i6.Image[0] = i6.Image[1] = i6.Image[2] = 255 * pointingHandMask;
|
|
|
-
|
|
|
- fillFingerSlices(pointingHandMask, 0);
|
|
|
- i7.Image[0] = i7.Image[1] = i7.Image[2] = 255 * pointingHandMask;
|
|
|
-
|
|
|
- pointingHandMask = pointingHandMask.Erode(2);
|
|
|
- i8.Image[0] = i8.Image[1] = i8.Image[2] = 255 * pointingHandMask;
|
|
|
-
|
|
|
- // only debug
|
|
|
- i9.Image[0] = i9.Image[1] = i9.Image[2] = 255 * handImage.And(pointingHandMask);
|
|
|
+ thumbDefectDepthFilter.reset();
|
|
|
+ thumbDefectStartFilter.reset();
|
|
|
+ thumbDefectEndFilter.reset();
|
|
|
+ lastThumbDefectDepth = null;
|
|
|
+ palmContour = null;
|
|
|
+ PalmQuad = null;
|
|
|
}
|
|
|
|
|
|
private void findLongestPalmContour()
|
|
|
{
|
|
|
- Contour<Point> contour = handImage.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL);
|
|
|
+ Contour<Point> contour = palmHand.Mask.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
|
|
|
+ Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL);
|
|
|
|
|
|
palmContour = contour;
|
|
|
double maxPerimeter = 0;
|
|
@@ -236,15 +120,16 @@ namespace bbiwarg.Detectors.PalmDetection
|
|
|
List<MCvConvexityDefect> newDefects = new List<MCvConvexityDefect>();
|
|
|
foreach (MCvConvexityDefect d in convexityDefects)
|
|
|
{
|
|
|
- bool remove = false;
|
|
|
+ bool fingerIntersectsStartEnd = false;
|
|
|
float minFingerLineDist = float.MaxValue;
|
|
|
- foreach (Finger f in fingers)
|
|
|
+ foreach (Finger f in pointingHand.Fingers)
|
|
|
{
|
|
|
Utility.LineSegment2D defectLine = new Utility.LineSegment2D(new Vector2D(d.StartPoint), new Vector2D(d.EndPoint));
|
|
|
Vector2D intersection = defectLine.Line.getIntersection(f.LineSegment.Line);
|
|
|
- if (intersection.isInBox(defectLine.P1, defectLine.P2) && intersection.isInBox(f.LineSegment.P1, f.LineSegment.P2))
|
|
|
+ if (intersection != null &&
|
|
|
+ intersection.isInBox(defectLine.P1, defectLine.P2) && intersection.isInBox(f.LineSegment.P1, f.LineSegment.P2))
|
|
|
{
|
|
|
- remove = true;
|
|
|
+ fingerIntersectsStartEnd = true;
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -254,7 +139,7 @@ namespace bbiwarg.Detectors.PalmDetection
|
|
|
minFingerLineDist = dist;
|
|
|
}
|
|
|
|
|
|
- if (minFingerLineDist >= Constants.PalmMinDefectMidFingerLineDistance && !remove)
|
|
|
+ if (minFingerLineDist >= Constants.PalmMinDefectMidFingerLineDistance && !fingerIntersectsStartEnd)
|
|
|
newDefects.Add(d);
|
|
|
}
|
|
|
convexityDefects = newDefects;
|
|
@@ -268,13 +153,20 @@ namespace bbiwarg.Detectors.PalmDetection
|
|
|
Vector2D start = new Vector2D(d.StartPoint);
|
|
|
Vector2D end = new Vector2D(d.EndPoint);
|
|
|
|
|
|
+ float l1 = (depth - start).Length;
|
|
|
+ float l2 = (depth - end).Length;
|
|
|
+ float lengthQuotient = Math.Max(l1, l2) / Math.Min(l1, l2);
|
|
|
+
|
|
|
float angle = (float) ((depth - start).getAngleBetween(depth - end) * 180 / Math.PI);
|
|
|
|
|
|
- if (angle <= Constants.PalmMaxThumbDefectAngle)
|
|
|
+ if (angle <= Constants.PalmMaxThumbDefectAngle &&
|
|
|
+ lengthQuotient >= Constants.PalmMinThumbDefectLengthQuotient && lengthQuotient <= Constants.PalmMaxThumbDefectLengthQuotient &&
|
|
|
+ d.Depth >= Constants.PalmMinTumbDefectDepth)
|
|
|
{
|
|
|
return d;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
return null;
|
|
|
}
|
|
|
|
|
@@ -282,14 +174,11 @@ namespace bbiwarg.Detectors.PalmDetection
|
|
|
{
|
|
|
MCvConvexityDefect? thumbDefect = findThumbDefect();
|
|
|
|
|
|
- if (convexityDefects.Count > 0 && (thumbDefect != null || thumbDefectDepthFilter.Initialized))
|
|
|
+ if (thumbDefect != null)
|
|
|
{
|
|
|
- if (thumbDefect != null)
|
|
|
- {
|
|
|
- thumbDefectDepth = new Vector2D(thumbDefect.Value.DepthPoint);
|
|
|
- thumbDefectStart = new Vector2D(thumbDefect.Value.StartPoint);
|
|
|
- thumbDefectEnd = new Vector2D(thumbDefect.Value.EndPoint);
|
|
|
- }
|
|
|
+ thumbDefectDepth = new Vector2D(thumbDefect.Value.DepthPoint);
|
|
|
+ thumbDefectStart = new Vector2D(thumbDefect.Value.StartPoint);
|
|
|
+ thumbDefectEnd = new Vector2D(thumbDefect.Value.EndPoint);
|
|
|
|
|
|
if (!thumbDefectDepthFilter.Initialized)
|
|
|
{
|
|
@@ -306,26 +195,26 @@ namespace bbiwarg.Detectors.PalmDetection
|
|
|
|
|
|
lastThumbDefectDepth = thumbDefectDepth;
|
|
|
|
|
|
- Vector2D handLength, handWidth;
|
|
|
- if (thumbDefectDepth.getDistanceTo(thumbDefectStart) > thumbDefectDepth.getDistanceTo(thumbDefectEnd))
|
|
|
+ Vector2D handLength, handWidth, topLeft, bottomLeft, bottomRight, topRight;
|
|
|
+ if (palmHand.Side == Hand.HandSide.Left)
|
|
|
{
|
|
|
- //right hand
|
|
|
+ //left hand
|
|
|
handLength = thumbDefectStart - thumbDefectDepth;
|
|
|
- handWidth = 0.8f * new Vector2D(-handLength.Y, handLength.X);
|
|
|
- topLeft = thumbDefectStart;
|
|
|
+ handWidth = 0.85f * new Vector2D(-handLength.Y, handLength.X);
|
|
|
+ topLeft = thumbDefectStart+0.15f*handLength;
|
|
|
bottomLeft = thumbDefectDepth - 0.4f * handLength;
|
|
|
bottomRight = bottomLeft + handWidth;
|
|
|
topRight = bottomRight + 1.2f * handLength - 0.3f * handWidth;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- //left hand
|
|
|
+ //right hand
|
|
|
handLength = thumbDefectEnd - thumbDefectDepth;
|
|
|
- handWidth = 0.8f * new Vector2D(handLength.Y, -handLength.X);
|
|
|
- topRight = thumbDefectEnd;
|
|
|
+ handWidth = 0.85f * new Vector2D(handLength.Y, -handLength.X);
|
|
|
+ topRight = thumbDefectEnd+0.15f*handLength;
|
|
|
bottomRight = thumbDefectDepth - 0.4f * handLength;
|
|
|
bottomLeft = bottomRight + handWidth;
|
|
|
- topLeft = bottomLeft + 1.2f * handLength - 0.3f * handWidth;
|
|
|
+ topLeft = bottomLeft + 1.15f * handLength - 0.35f * handWidth;
|
|
|
}
|
|
|
|
|
|
PalmQuad = new Quadrangle(bottomLeft, topLeft, topRight, bottomRight);
|
|
@@ -334,8 +223,6 @@ namespace bbiwarg.Detectors.PalmDetection
|
|
|
|
|
|
private void draw()
|
|
|
{
|
|
|
-
|
|
|
-
|
|
|
if (palmContour != null && palmContour.Count<Point>() > 0) {
|
|
|
outputImage.drawContour(palmContour, Constants.PalmConturColor);
|
|
|
outputImage.drawPoints(palmContour.GetConvexHull(Emgu.CV.CvEnum.ORIENTATION.CV_CLOCKWISE), Constants.PalmConvexHullColor);
|