|
@@ -4,11 +4,10 @@ using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Threading.Tasks;
|
|
-using System.Drawing;
|
|
|
|
-using Emgu.CV;
|
|
|
|
-using Emgu.CV.Structure;
|
|
|
|
using bbiwarg.Images;
|
|
using bbiwarg.Images;
|
|
using bbiwarg.Utility;
|
|
using bbiwarg.Utility;
|
|
|
|
+using Emgu.CV.Structure;
|
|
|
|
+using Emgu.CV;
|
|
|
|
|
|
namespace bbiwarg.Detectors.Fingers
|
|
namespace bbiwarg.Detectors.Fingers
|
|
{
|
|
{
|
|
@@ -17,169 +16,203 @@ namespace bbiwarg.Detectors.Fingers
|
|
private DepthImage depthImage;
|
|
private DepthImage depthImage;
|
|
private EdgeImage edgeImage;
|
|
private EdgeImage edgeImage;
|
|
private FingerImage fingerImage;
|
|
private FingerImage fingerImage;
|
|
- private Image<Gray, byte> fingerPointsImage;
|
|
+ private List<FingerSliceTrail> horizontalFingerSliceTrails;
|
|
- private List<Line2D> fingerLines;
|
|
+ private List<FingerSliceTrail> verticalFingerSliceTrails;
|
|
private List<Finger> fingers;
|
|
private List<Finger> fingers;
|
|
|
|
+ public List<Finger> Fingers { get { return fingers; } private set { fingers = value; } }
|
|
|
|
|
|
|
|
|
|
- public FingerDetector(DepthImage depthImage, EdgeImage edgeImage, FingerImage fingerImage) {
|
|
+ public FingerDetector(DepthImage depthImage, EdgeImage edgeImage, FingerImage fingerImage)
|
|
|
|
+ {
|
|
this.depthImage = depthImage;
|
|
this.depthImage = depthImage;
|
|
this.edgeImage = edgeImage;
|
|
this.edgeImage = edgeImage;
|
|
this.fingerImage = fingerImage;
|
|
this.fingerImage = fingerImage;
|
|
|
|
|
|
- Stopwatch sw = new Stopwatch();
|
|
+ findHorizontalFingerSliceTrails();
|
|
- sw.Start();
|
|
+ findVerticalFingerSliceTrails();
|
|
-
|
|
+ transformFingerSliceTrailsToFingers();
|
|
- findFingerPoints();
|
|
+ }
|
|
-
|
|
|
|
- sw.Stop();
|
|
|
|
- Console.WriteLine("findFingerPoints:" + sw.ElapsedMilliseconds);
|
|
|
|
- sw.Restart();
|
|
|
|
|
|
|
|
- findFingerLines();
|
|
+ private void findHorizontalFingerSliceTrails()
|
|
|
|
+ {
|
|
|
|
|
|
- sw.Stop();
|
|
+ horizontalFingerSliceTrails = new List<FingerSliceTrail>();
|
|
- Console.WriteLine("findFingerLines:" + sw.ElapsedMilliseconds);
|
|
|
|
- sw.Restart();
|
|
|
|
|
|
|
|
|
|
+ int width = depthImage.getWidth();
|
|
|
|
+ int height = depthImage.getHeight();
|
|
|
|
|
|
- findFingers();
|
|
+ int minFingerSize = 10;
|
|
|
|
+ int maxFingerSize = 30;
|
|
|
|
|
|
- sw.Stop();
|
|
+
|
|
- Console.WriteLine("findFingers:" + sw.ElapsedMilliseconds);
|
|
+ for (int y = 0; y < height; y++)
|
|
|
|
+ {
|
|
|
|
+ int x = 0;
|
|
|
|
+ while (x <= width - minFingerSize)
|
|
|
|
+ {
|
|
|
|
+ if (edgeImage.isEdgeAt(x, y) && depthImage.getDepthAt(x, y) > depthImage.getDepthAt(x + 1, y))
|
|
|
|
+ {
|
|
|
|
+ int sliceX1 = x;
|
|
|
|
+ int maxSliceX2 = Math.Min(sliceX1 + maxFingerSize, width - 1);
|
|
|
|
+ x++;
|
|
|
|
+ while (x <= maxSliceX2)
|
|
|
|
+ {
|
|
|
|
+ if (edgeImage.isEdgeAt(x, y))
|
|
|
|
+ {
|
|
|
|
+ int sliceX2 = x;
|
|
|
|
+ Vector2D sliceStart = new Vector2D(Math.Max(sliceX1 - 1, 0), y);
|
|
|
|
+ Vector2D sliceEnd = new Vector2D(Math.Min(sliceX2 + 1, width - 1), y);
|
|
|
|
+ FingerSlice fingerSlice = new FingerSlice(sliceStart, sliceEnd);
|
|
|
|
+ if (fingerSlice.Size > minFingerSize && fingerSliceDepthTest(fingerSlice))
|
|
|
|
+ {
|
|
|
|
+ addHorizontalFingerSlice(fingerSlice);
|
|
|
|
+ fingerImage.drawLine(fingerSlice.Line, FingerImageState.possibleFingerSlice);
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ else x++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ x++;
|
|
|
|
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- public List<Finger> getFingers() {
|
|
+ private void findVerticalFingerSliceTrails()
|
|
- return fingers;
|
|
+ {
|
|
- }
|
|
+ verticalFingerSliceTrails = new List<FingerSliceTrail>();
|
|
|
|
|
|
- private void findFingerPoints() {
|
|
|
|
int width = depthImage.getWidth();
|
|
int width = depthImage.getWidth();
|
|
int height = depthImage.getHeight();
|
|
int height = depthImage.getHeight();
|
|
|
|
|
|
- fingerPointsImage = new Image<Gray, byte>(width, height);
|
|
+ int minFingerSize = 10;
|
|
-
|
|
+ int maxFingerSize = 30;
|
|
- for (int y = 0; y < height; y++) {
|
|
+
|
|
- for (int x = 0; x < width; x++) {
|
|
+
|
|
- if (edgeImage.isEdgeAt(x, y))
|
|
+ for (int x = 0; x < width; x++)
|
|
|
|
+ {
|
|
|
|
+ int y = 0;
|
|
|
|
+ while (y <= height - minFingerSize)
|
|
|
|
+ {
|
|
|
|
+ if (edgeImage.isEdgeAt(x, y) && depthImage.getDepthAt(x, y) > depthImage.getDepthAt(x, y + 1))
|
|
{
|
|
{
|
|
- Vector2D startPoint = new Vector2D(x, y);
|
|
+ int sliceY1 = y;
|
|
- searchFingerPoint(startPoint, new Vector2D(1, 0), fingerPointsImage);
|
|
+ int maxSliceY2 = Math.Min(sliceY1 + maxFingerSize, height - 1);
|
|
- searchFingerPoint(startPoint, new Vector2D(0, 1), fingerPointsImage);
|
|
+ y++;
|
|
-
|
|
+ while (y <= maxSliceY2)
|
|
-
|
|
+ {
|
|
|
|
+ if (edgeImage.isEdgeAt(x, y))
|
|
|
|
+ {
|
|
|
|
+ int sliceY2 = y;
|
|
|
|
+ Vector2D sliceStart = new Vector2D(x, Math.Max(sliceY1 - 1, 0));
|
|
|
|
+ Vector2D sliceEnd = new Vector2D(x, Math.Min(sliceY2 + 1, height - 1));
|
|
|
|
+ FingerSlice fingerSlice = new FingerSlice(sliceStart, sliceEnd);
|
|
|
|
+ if (fingerSlice.Size > minFingerSize && fingerSliceDepthTest(fingerSlice))
|
|
|
|
+ {
|
|
|
|
+ addVerticalFingerSlice(fingerSlice);
|
|
|
|
+ fingerImage.drawLine(fingerSlice.Line, FingerImageState.possibleFingerSlice);
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ else y++;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+ else
|
|
|
|
+ y++;
|
|
|
|
+
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- private void searchFingerPoint(Vector2D start, Vector2D direction, Image<Gray, byte> fingerPointsImage) {
|
|
+ private void transformFingerSliceTrailsToFingers()
|
|
- int width = fingerPointsImage.Width;
|
|
+ {
|
|
- int height = fingerPointsImage.Height;
|
|
+ int minNumSlices = 20;
|
|
- float minFingerSize = 10;
|
|
|
|
- float maxFingerSize = 30;
|
|
|
|
- direction = direction.normalize();
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- bool edgeFound = false;
|
|
|
|
- Vector2D edge = start + minFingerSize * direction;
|
|
|
|
- float index = minFingerSize;
|
|
|
|
- float maxXIndex = (direction.X == 0) ? float.MaxValue : ((direction.X > 0) ? ((width - start.X) / direction.X) : (-start.X / direction.X));
|
|
|
|
- float maxYIndex = (direction.Y == 0) ? float.MaxValue : ((direction.Y > 0) ? ((height - start.Y) / direction.Y) : (-start.Y / direction.Y));
|
|
|
|
|
|
|
|
- float maxIndex = Math.Min(maxFingerSize, Math.Min(maxXIndex, maxYIndex));
|
|
+ Fingers = new List<Finger>();
|
|
|
|
+ List<FingerSliceTrail> fingerSliceTrails = new List<FingerSliceTrail>();
|
|
|
|
+ fingerSliceTrails.AddRange(horizontalFingerSliceTrails);
|
|
|
|
+ fingerSliceTrails.AddRange(verticalFingerSliceTrails);
|
|
|
|
|
|
- if (index < maxIndex)
|
|
+ foreach (FingerSliceTrail trail in fingerSliceTrails)
|
|
{
|
|
{
|
|
- Int16 depthStart = depthImage.getDepthAt((int)start.X, (int)start.Y);
|
|
+ if (trail.NumSlices >= minNumSlices)
|
|
- Int16 depthEdge = depthImage.getDepthAt((int)edge.X, (int)edge.Y);
|
|
|
|
-
|
|
|
|
- if (depthStart > depthEdge)
|
|
|
|
{
|
|
{
|
|
- while (!edgeFound && index < maxIndex)
|
|
+ Finger finger = new Finger(trail);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ bool addFinger = true;
|
|
|
|
+ for (int i = fingers.Count - 1; i >= 0; i--)
|
|
{
|
|
{
|
|
- if (edgeImage.isEdgeAt((int)edge.X, (int)edge.Y))
|
|
+ Finger f = fingers[i];
|
|
- edgeFound = true;
|
|
+ if (finger.getSimilarity(f) > 0.8)
|
|
- else
|
|
|
|
{
|
|
{
|
|
- index++;
|
|
+ if (finger.SliceTrail.NumSlices > f.SliceTrail.NumSlices)
|
|
- edge += direction;
|
|
+ fingers.RemoveAt(i);
|
|
|
|
+ else
|
|
|
|
+ addFinger = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (addFinger)
|
|
|
|
+ {
|
|
|
|
+ fingers.Add(finger);
|
|
|
|
+ fingerImage.drawFinger(finger, FingerImageState.fingerDetected);
|
|
|
|
+ }
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
+
|
|
}
|
|
}
|
|
-
|
|
|
|
- if (edgeFound && fingerDepthTest(start, edge))
|
|
|
|
- {
|
|
|
|
- Vector2D mid = 0.5f * (start + edge);
|
|
|
|
- fingerPointsImage.Data[(int)mid.Y, (int)mid.X, 0] = byte.MaxValue;
|
|
|
|
- fingerImage.setFingerAt((int)mid.X, (int)mid.Y, FingerImageState.possibleFinger);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
|
|
- private bool fingerDepthTest(Vector2D p1, Vector2D p2) {
|
|
|
|
- Vector2D mid = 0.5f * (p1 + p2);
|
|
|
|
- Int16 depthP1 = depthImage.getDepthAt((int)p1.X, (int)p1.Y);
|
|
|
|
- Int16 depthMid = depthImage.getDepthAt((int)mid.X, (int)mid.Y);
|
|
|
|
- Int16 depthP2 = depthImage.getDepthAt((int)p2.X, (int)p2.Y);
|
|
|
|
- return (depthP1 > depthMid && depthMid < depthP2);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- private void findFingerLines() {
|
|
+ private bool fingerSliceDepthTest(FingerSlice fingerSlice)
|
|
- float maxCombinableAngle = (float) (Math.PI * 20 / 180);
|
|
+ {
|
|
- float maxCombinableParallelDistance = 10;
|
|
+ Int16 depthStart = depthImage.getDepthAt((int)fingerSlice.Start.X, (int)fingerSlice.Start.Y);
|
|
- float maxCombinableVerticalDistance = 10;
|
|
+ Int16 depthMid = depthImage.getDepthAt((int)fingerSlice.Mid.X, (int)fingerSlice.Mid.Y);
|
|
|
|
+ Int16 depthEnd = depthImage.getDepthAt((int)fingerSlice.End.X, (int)fingerSlice.End.Y);
|
|
|
|
+ return (depthStart > depthMid && depthMid < depthEnd);
|
|
|
|
+ }
|
|
|
|
|
|
- fingerPointsImage = fingerPointsImage.Dilate(1);
|
|
+ private void addHorizontalFingerSlice(FingerSlice slice)
|
|
- LineSegment2D[] lineSegments = fingerPointsImage.HoughLinesBinary(1, Math.PI / 90, 10, 20, 2)[0];
|
|
+ {
|
|
|
|
+ int maxYGap = 5;
|
|
|
|
+ int maxXDifference = 5;
|
|
|
|
|
|
- fingerLines = new List<Line2D>();
|
|
+ bool assigned = false;
|
|
- foreach (LineSegment2D lineSegment in lineSegments)
|
|
+ foreach (FingerSliceTrail trail in horizontalFingerSliceTrails)
|
|
{
|
|
{
|
|
- Line2D line = new Line2D(new Vector2D(lineSegment.P1), new Vector2D(lineSegment.P2));
|
|
+ FingerSlice trailEnd = trail.End;
|
|
-
|
|
+ if (slice.Mid.Y - trailEnd.Mid.Y <= maxYGap && Math.Abs(trailEnd.Mid.X - slice.Mid.X) <= maxXDifference)
|
|
- List<Line2D> combineableLines = new List<Line2D>();
|
|
+ {
|
|
- foreach(Line2D fingerLine in fingerLines) {
|
|
+ trail.addSlice(slice);
|
|
- float angle = line.getAngleBetween(fingerLine);
|
|
+ assigned = true;
|
|
- if (angle <= maxCombinableAngle) {
|
|
|
|
- float parallelDistance = line.getParallelDistanceTo(fingerLine);
|
|
|
|
- if (parallelDistance <= maxCombinableParallelDistance) {
|
|
|
|
- float verticalDistance = line.getVerticalDistanceTo(fingerLine);
|
|
|
|
- if (verticalDistance <= maxCombinableVerticalDistance) {
|
|
|
|
- combineableLines.Add(fingerLine);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (combineableLines.Count == 0) {
|
|
|
|
- fingerLines.Add(line);
|
|
|
|
- }
|
|
|
|
- else {
|
|
|
|
- foreach (Line2D mergableLine in combineableLines) {
|
|
|
|
- fingerLines.Remove(mergableLine);
|
|
|
|
- }
|
|
|
|
- combineableLines.Add(line);
|
|
|
|
- fingerLines.Add(new Line2D(combineableLines));
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (!assigned)
|
|
|
|
+ horizontalFingerSliceTrails.Add(new FingerSliceTrail(slice));
|
|
}
|
|
}
|
|
|
|
|
|
- private void findFingers()
|
|
+ private void addVerticalFingerSlice(FingerSlice slice)
|
|
{
|
|
{
|
|
- float minLength = 20;
|
|
+ int maxXGap = 4;
|
|
- float minNumCombinedLines = 2;
|
|
+ int maxYDifference = 4;
|
|
|
|
|
|
- fingers = new List<Finger>();
|
|
+ bool assigned = false;
|
|
- foreach (Line2D line in fingerLines) {
|
|
+ foreach (FingerSliceTrail trail in verticalFingerSliceTrails)
|
|
- if (line.Length >= minLength && line.NumCombinedLines >= minNumCombinedLines)
|
|
+ {
|
|
|
|
+ FingerSlice trailEnd = trail.End;
|
|
|
|
+ if (slice.Mid.X - trailEnd.Mid.X <= maxXGap && Math.Abs(trailEnd.Mid.Y - slice.Mid.Y) <= maxYDifference)
|
|
{
|
|
{
|
|
- fingers.Add(new Finger(line));
|
|
+ trail.addSlice(slice);
|
|
- fingerImage.drawLine(line, FingerImageState.fingerDetected);
|
|
+ assigned = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (!assigned)
|
|
|
|
+ verticalFingerSliceTrails.Add(new FingerSliceTrail(slice));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|