|
@@ -17,150 +17,141 @@ namespace bbiwarg.Detectors.Fingers
|
|
private DepthImage depthImage;
|
|
private DepthImage depthImage;
|
|
private EdgeImage edgeImage;
|
|
private EdgeImage edgeImage;
|
|
private FingerImage fingerImage;
|
|
private FingerImage fingerImage;
|
|
- private List<Vector<int>> possibleFingerPoints;
|
|
|
|
- private List<Finger> possibleFingers;
|
|
|
|
private List<Finger> fingers;
|
|
private List<Finger> fingers;
|
|
|
|
+ Image<Gray, byte> fingerPoints;
|
|
|
|
|
|
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;
|
|
|
|
|
|
- findPossibleFingerPoints();
|
|
|
|
- findPossibleFingers();
|
|
|
|
- setFingers();
|
|
|
|
- setFingerPoints();
|
|
|
|
|
|
+ Stopwatch sw = new Stopwatch();
|
|
|
|
+ sw.Start();
|
|
|
|
+
|
|
|
|
+ findFingerPoints();
|
|
|
|
+
|
|
|
|
+ sw.Stop();
|
|
|
|
+ Console.WriteLine("findFingerPoints:" + sw.ElapsedMilliseconds);
|
|
|
|
+ sw.Restart();
|
|
|
|
+
|
|
|
|
+ findFingers();
|
|
|
|
+
|
|
|
|
+ sw.Stop();
|
|
|
|
+ Console.WriteLine("findFingers:" + sw.ElapsedMilliseconds);
|
|
|
|
+
|
|
}
|
|
}
|
|
|
|
|
|
public List<Finger> getFingers() {
|
|
public List<Finger> getFingers() {
|
|
return fingers;
|
|
return fingers;
|
|
}
|
|
}
|
|
|
|
|
|
- private void findPossibleFingerPoints()
|
|
|
|
- {
|
|
|
|
|
|
+ private void findFingerPoints() {
|
|
int width = depthImage.getWidth();
|
|
int width = depthImage.getWidth();
|
|
int height = depthImage.getHeight();
|
|
int height = depthImage.getHeight();
|
|
- int maxFingerSize = 30;
|
|
|
|
- int minFingerSize = 10;
|
|
|
|
- possibleFingerPoints = new List<Vector<int>>();
|
|
|
|
|
|
|
|
|
|
+ fingerPoints = new Image<Gray, byte>(width, height);
|
|
|
|
+
|
|
for (int y = 0; y < height; y++) {
|
|
for (int y = 0; y < height; y++) {
|
|
for (int x = 0; x < width; x++) {
|
|
for (int x = 0; x < width; x++) {
|
|
- if (edgeImage.isEdgeAt(x,y)) {
|
|
|
|
- //search horizontal
|
|
|
|
- bool edgeRightFound = false;
|
|
|
|
- int edgeRightX = x + minFingerSize;
|
|
|
|
- while (!edgeRightFound && edgeRightX < width) {
|
|
|
|
- if (edgeImage.isEdgeAt(edgeRightX,y))
|
|
|
|
- edgeRightFound = true;
|
|
|
|
- else
|
|
|
|
- edgeRightX++;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (edgeRightFound){
|
|
|
|
- int midX = (edgeRightX + x) / 2;
|
|
|
|
- Int16 depthLeft = depthImage.getDepthAt(x, y);
|
|
|
|
- Int16 depthMid = depthImage.getDepthAt(midX, y);
|
|
|
|
- Int16 depthRight = depthImage.getDepthAt(edgeRightX, y);
|
|
|
|
-
|
|
|
|
- if ((edgeRightX - x) < maxFingerSize && depthLeft > depthMid && depthMid < depthRight) {
|
|
|
|
- possibleFingerPoints.Add(new Vector<int>(midX, y, depthMid));
|
|
|
|
- fingerImage.setFingerAt(midX, y, FingerImageState.possibleFinger);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- //search vertical
|
|
|
|
- bool edgeBottomFound = false;
|
|
|
|
- int edgeBottomY = y + minFingerSize;
|
|
|
|
- while (!edgeBottomFound && edgeBottomY < height) {
|
|
|
|
- if (edgeImage.isEdgeAt(x, edgeBottomY))
|
|
|
|
- edgeBottomFound = true;
|
|
|
|
- else
|
|
|
|
- edgeBottomY++;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (edgeBottomFound) {
|
|
|
|
- int midY = (edgeBottomY + y) / 2;
|
|
|
|
- Int16 depthTop = depthImage.getDepthAt(x, y);
|
|
|
|
- Int16 depthMid = depthImage.getDepthAt(x, midY);
|
|
|
|
- Int16 depthBottom = depthImage.getDepthAt(x, edgeBottomY);
|
|
|
|
-
|
|
|
|
- if ((edgeBottomY - y) < maxFingerSize && depthTop > depthMid && depthMid < depthBottom) {
|
|
|
|
- possibleFingerPoints.Add(new Vector<int>(x, midY, depthMid));
|
|
|
|
- fingerImage.setFingerAt(x, midY, FingerImageState.possibleFinger);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- }
|
|
|
|
|
|
+ if (edgeImage.isEdgeAt(x, y))
|
|
|
|
+ {
|
|
|
|
+ Vector2D startPoint = new Vector2D(x, y);
|
|
|
|
+ searchFingerPoint(startPoint, new Vector2D(1, 0), fingerPoints);
|
|
|
|
+ searchFingerPoint(startPoint, new Vector2D(0, 1), fingerPoints);
|
|
|
|
+ //searchFingerPoint(startPoint, new Vector2D(1, -1), fingerPoints);
|
|
|
|
+ //searchFingerPoint(startPoint, new Vector2D(1, 1), fingerPoints);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- private void findPossibleFingers()
|
|
|
|
- {
|
|
|
|
- int width = depthImage.getWidth();
|
|
|
|
- int height = depthImage.getHeight();
|
|
|
|
- float fingerCylinderRadius = 5f;
|
|
|
|
- possibleFingers = new List<Finger>();
|
|
|
|
-
|
|
|
|
- foreach (Vector<int> pfp in possibleFingerPoints) {
|
|
|
|
- //find nearest finger
|
|
|
|
- List<Finger> nearFingerClusters = new List<Finger>();
|
|
|
|
-
|
|
|
|
- for (int i = 0; i < possibleFingers.Count; i++)
|
|
|
|
- {
|
|
|
|
- if (possibleFingers[i].isWithinDistance(pfp, fingerCylinderRadius)) {
|
|
|
|
- nearFingerClusters.Add(possibleFingers[i]);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (nearFingerClusters.Count == 0) {
|
|
|
|
- List<Vector<int>> fingerPoints = new List<Vector<int>>();
|
|
|
|
- fingerPoints.Add(pfp);
|
|
|
|
- possibleFingers.Add(new Finger(fingerPoints));
|
|
|
|
- }
|
|
|
|
- else if (nearFingerClusters.Count == 1)
|
|
|
|
- nearFingerClusters[0].addFingerPoint(pfp);
|
|
|
|
|
|
+ private void searchFingerPoint(Vector2D start, Vector2D direction, Image<Gray, byte> fingerPointsImage) {
|
|
|
|
+ int width = fingerPointsImage.Width;
|
|
|
|
+ int height = fingerPointsImage.Height;
|
|
|
|
+ float minFingerSize = 10;
|
|
|
|
+ float maxFingerSize = 30;
|
|
|
|
+ float stepSize = direction.getLength();
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ 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));
|
|
|
|
+
|
|
|
|
+ while (!edgeFound && index < maxIndex) {
|
|
|
|
+ if (edgeImage.isEdgeAt((int)edge.x, (int)edge.y))
|
|
|
|
+ edgeFound = true;
|
|
else {
|
|
else {
|
|
- //merge fingers
|
|
|
|
- List<Vector<int>> fingerPoints = new List<Vector<int>>();
|
|
|
|
- fingerPoints.Add(pfp);
|
|
|
|
- for (int j = 0; j < nearFingerClusters.Count; j++) {
|
|
|
|
- fingerPoints.Concat<Vector<int>>(nearFingerClusters[j].getFingerPoints());
|
|
|
|
- possibleFingers.Remove(nearFingerClusters[j]);
|
|
|
|
- }
|
|
|
|
- possibleFingers.Add(new Finger(fingerPoints));
|
|
|
|
|
|
+ index += stepSize;
|
|
|
|
+ edge += direction;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ 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 void setFingers()
|
|
|
|
- {
|
|
|
|
|
|
+ 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 findFingers()
|
|
|
|
+ {
|
|
int width = depthImage.getWidth();
|
|
int width = depthImage.getWidth();
|
|
int height = depthImage.getHeight();
|
|
int height = depthImage.getHeight();
|
|
- float minFingerLength = 15.0f;
|
|
|
|
- int minNumFingerPoints = 15;
|
|
|
|
|
|
+
|
|
|
|
+ fingerPoints = fingerPoints.Dilate(1);
|
|
|
|
+
|
|
|
|
+ double rhoResolution = 1;
|
|
|
|
+ double thetaResolution = Math.PI / 90.0;
|
|
|
|
+ int threshold = 10;
|
|
|
|
+ double minLineWidth = 20;
|
|
|
|
+ double gapBetweenLines = 2;
|
|
|
|
+
|
|
|
|
+ LineSegment2D[] lines = fingerPoints.HoughLinesBinary(rhoResolution, thetaResolution, threshold, minLineWidth, gapBetweenLines)[0];
|
|
|
|
+
|
|
fingers = new List<Finger>();
|
|
fingers = new List<Finger>();
|
|
|
|
|
|
- foreach (Finger finger in possibleFingers)
|
|
|
|
- {
|
|
|
|
- float length = finger.getLength();
|
|
|
|
- int numFingerPoints = finger.getNumFingerPoints();
|
|
|
|
- if (length > minFingerLength && numFingerPoints > minNumFingerPoints)
|
|
|
|
- {
|
|
|
|
|
|
+ foreach (LineSegment2D line in lines) {
|
|
|
|
+ Vector2D p1 = new Vector2D(line.P1.X, line.P1.Y);
|
|
|
|
+ Vector2D p2 = new Vector2D(line.P2.X, line.P2.Y);
|
|
|
|
+ Finger finger = new Finger(p1, p2);
|
|
|
|
+
|
|
|
|
+ List<Finger> mergeableFingers = new List<Finger>();
|
|
|
|
+
|
|
|
|
+ foreach (Finger f in fingers) {
|
|
|
|
+ if (finger.isMergeable(f))
|
|
|
|
+ mergeableFingers.Add(f);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (mergeableFingers.Count == 0)
|
|
|
|
+ fingers.Add(finger);
|
|
|
|
+ else if (mergeableFingers.Count == 1)
|
|
|
|
+ mergeableFingers[0].mergeFingers(finger);
|
|
|
|
+ else {
|
|
|
|
+ foreach (Finger mf in mergeableFingers) {
|
|
|
|
+ finger.mergeFingers(mf);
|
|
|
|
+ fingers.Remove(mf);
|
|
|
|
+ }
|
|
fingers.Add(finger);
|
|
fingers.Add(finger);
|
|
}
|
|
}
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ }
|
|
|
|
|
|
- private void setFingerPoints() {
|
|
|
|
- //fingerImage.setFingers(fingers, FingerImageState.fingerDetected);
|
|
|
|
- foreach (Finger finger in fingers) {
|
|
|
|
- Vector<float> lineEndPoint1 = finger.getLineEndPoint1();
|
|
|
|
- Vector<float> lineEndPoint2 = finger.getLineEndPoint2();
|
|
|
|
- fingerImage.drawLine(lineEndPoint1, lineEndPoint2, FingerImageState.fingerDetected);
|
|
|
|
|
|
+ //draw fingers in fingerImage
|
|
|
|
+ foreach(Finger finger in fingers) {
|
|
|
|
+ fingerImage.drawLine(finger.getTipPoint(), finger.getHandPoint(), FingerImageState.fingerDetected);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|