Skip to content

Commit 0821e1f

Browse files
committed
Version 0.9.2: re-implemented SVM using Allen's suggested feature extraction method;
added options in demo to turn on/off SVM, edge connection test, area display; disabled second pass of hand detection for efficiency concerns; modified several default parameter values in ObjectParams; fixed two minor bugs in hand detection removed #include<stdafx.h> from header files and moved definitions to version.h to speed up external project compilation
1 parent d21833a commit 0821e1f

28 files changed

+19371
-26583
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ project( OpenARK )
44

55
set(OpenARK_VERSION_MAJOR 0)
66
set(OpenARK_VERSION_MINOR 9)
7-
set(OpenARK_VERSION_PATCH 1)
7+
set(OpenARK_VERSION_PATCH 2)
88

99
set(SVM_PATHS "\"svm/\", \"../svm/\", \"\"")
1010

Converter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#pragma once
22

3-
#include "stdafx.h"
43
#include "version.h"
54

5+
#include <opencv2/core.hpp>
6+
67
//Intel RealSense 3D SDK libraries
78
#include "RealSense/SampleReader.h"
89
#include "RealSense/Session.h"

DepthCamera.cpp

Lines changed: 12 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,15 @@ namespace ark {
8989
cv::Mat pendingClusters;
9090
if (elim_planes) pendingClusters = cv::Mat::zeros(R, C, CV_8U);
9191

92+
// number of 'pending' clusters
93+
uchar pendingClusterCount = 0;
94+
9295
float bestHandDist = FLT_MAX;
9396

9497
// 2. eliminate large planes
9598

96-
const int DOMINANT_PLANE_MIN_POINTS = params->dominantPlaneMinPoints * R * C /
99+
// compute exact constant
100+
static const int DOMINANT_PLANE_MIN_POINTS = params->dominantPlaneMinPoints * R * C /
97101
(params->normalResolution * params->normalResolution);
98102

99103
if (elim_planes){
@@ -107,41 +111,14 @@ namespace ark {
107111
}
108112
}
109113

110-
// 3. first pass: try to detect hands directly
111-
// number of 'pending' clusters
112-
uchar pendingClusterCount = 0;
114+
// 3. detect hands directly
113115
boost::shared_ptr<Hand> bestHandObject =
114-
detectHandHelper(xyzMap, hands, params, &bestHandDist, &pendingClusters,
115-
&pendingClusterCount);
116-
117-
// 4. second pass: eliminate planes and try again
118-
if (elim_planes) {
119-
cv::Vec3b color;
120-
121-
for (uchar k = 0; k < pendingClusterCount; ++k) {
122-
color = util::paletteColor(k);
123-
124-
uchar maskColor = 255 - k;
125-
126-
std::vector<Vec3f> equations;
127-
std::vector<VecP2iPtr> points;
128-
std::vector<VecV3fPtr> pointsXYZ;
129-
130-
detectPlaneHelper(xyzMap, equations, points, pointsXYZ, params, nullptr,
131-
&pendingClusters, maskColor);
132-
133-
if (equations.size()) {
134-
for (uint i = 0; i < equations.size(); ++i) {
135-
util::removePlane(xyzMap, equations[i], params->handPlaneMinNorm);
136-
}
137-
138-
bestHandObject = detectHandHelper(xyzMap, hands, params,
139-
&bestHandDist, nullptr, nullptr, &pendingClusters, maskColor);
140-
}
141-
}
142-
}
116+
detectHandHelper(xyzMap, hands, params, &bestHandDist,
117+
elim_planes ? &pendingClusters : nullptr,
118+
elim_planes ? &pendingClusterCount : nullptr);
143119

144120
if (bestHandObject != nullptr) {
121+
// if no hands surpass 'high confidence threshold', at least add one hand
145122
hands.push_back(bestHandObject);
146123
}
147124

@@ -629,8 +606,8 @@ namespace ark {
629606
if (best_hand_dist) closestHandDist = *best_hand_dist;
630607

631608
// 2. try to directly cluster objects using flood fill
632-
std::vector<Point2i> allIjPoints(R * C + 1);
633-
std::vector<Vec3f> allXyzPoints(R * C + 1);
609+
std::vector<Point2i> allIjPoints(R * C);
610+
std::vector<Vec3f> allXyzPoints(R * C);
634611

635612
// compute the minimum number of points in a cluster according to params
636613
const int CLUSTER_MIN_POINTS = (int) (params->handClusterMinPoints * R * C);

DepthCamera.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ namespace ark {
388388
cv::Mat ampMapBuf;
389389
cv::Mat flagMapBuf;
390390

391-
/** Mutexes to ensure thread safety while updating images
391+
/** Mutex to ensure thread safety while updating images
392392
* (mutable = modificable even to const methods)
393393
*/
394394
mutable std::mutex imageMutex;

FrameObject.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
#pragma once
22

3-
#include "stdafx.h"
43
#include "version.h"
54

5+
#include <opencv2/core.hpp>
6+
#include <vector>
7+
68
// OpenARK headers
79
#include "version.h"
810
#include "ObjectParams.h"

Hand.cpp

Lines changed: 87 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
#include "stdafx.h"
21
#include "version.h"
32

43
#include "Util.h"
54
#include "Hand.h"
65
#include "Visualizer.h"
6+
#include "HandClassifier.h"
77

88
// limited to file scope
99
namespace {
@@ -54,8 +54,8 @@ namespace {
5454

5555
namespace ark {
5656

57-
// Initialize the SVM hand classifier
58-
classifier::HandClassifier & Hand::handClassifier = classifier::SVMHandClassifier(SVM_PATHS);
57+
// Initialize the SVM hand validator
58+
static classifier::SVMHandValidator & handValidator = classifier::SVMHandValidator(SVM_PATHS);
5959

6060
Hand::Hand() : FrameObject() { }
6161

@@ -93,30 +93,18 @@ namespace ark {
9393
}
9494
}
9595

96-
bool Hand::checkForHand()
96+
bool Hand::checkForHand()
9797
{
9898
#ifdef DEBUG
9999
cv::Mat visual = cv::Mat::zeros(fullMapSize.height, fullMapSize.width, CV_8UC3);
100+
cv::Mat defectVisual = cv::Mat::zeros(fullMapSize.height, fullMapSize.width, CV_8UC3);
100101
#endif
101102
if (points->size() == 0 || num_points == 0) {
102103
return false;
103104
}
104105

105106
computeContour(xyzMap, points.get(), points_xyz.get(), topLeftPt, num_points);
106107

107-
// recompute convex hull based on new contour
108-
convexHull.clear(); getConvexHull();
109-
110-
// Begin by computing defects
111-
std::vector<cv::Vec4i> defects;
112-
113-
if (indexHull.size() > 3)
114-
{
115-
std::vector<int> tmpHull;
116-
117-
cv::convexityDefects(contour, indexHull, defects);
118-
}
119-
120108
// ** Find center of contour **
121109
Point2i centroid = findCenter(contour) - topLeftPt;
122110

@@ -125,7 +113,7 @@ namespace ark {
125113

126114
// Find radius and center point of largest inscribed circle above center
127115
Vec3f topPt = util::averageAroundPoint(xyzMap, (*points)[0] - topLeftPt,
128-
params->xyzAverageSize);
116+
params->xyzAverageSize);
129117

130118
// radius of largest inscribed circle
131119
double cirrad;
@@ -243,6 +231,10 @@ namespace ark {
243231
return false;
244232
}
245233

234+
if (contour[wristL].x > contour[wristR].x) {
235+
std::swap(wristL, wristR);
236+
}
237+
246238
wristL_ij = contour[wristL];
247239
wristR_ij = contour[wristR];
248240
wristL_xyz = util::averageAroundPoint(xyzMap,
@@ -268,7 +260,54 @@ namespace ark {
268260
return false;
269261
}
270262

263+
// (finished detecting wrist)
264+
265+
// ** Remove everything below wrist **
266+
267+
std::vector<Point2i> aboveWristPointsIJ;
268+
std::vector<Vec3f> aboveWristPointsXYZ;
269+
270+
if (wristR_ij.x != wristL_ij.x) {
271+
double slope = (double)(wristR_ij.y - wristL_ij.y) / (wristR_ij.x - wristL_ij.x);
272+
273+
for (int i = 0; i < num_points; ++i) {
274+
const Point2i & pt = (*points)[i];
275+
double y_hat = wristL_ij.y + (pt.x - wristL_ij.x) * slope;
276+
277+
Vec3f & vec = xyzMap.at<Vec3f>(pt - topLeftPt);
278+
279+
if (pt.y > y_hat) {
280+
vec = 0;
281+
}
282+
else {
283+
aboveWristPointsIJ.push_back(pt);
284+
aboveWristPointsXYZ.push_back(vec);
285+
}
286+
}
287+
}
288+
289+
num_points = (int)aboveWristPointsIJ.size();
290+
points->swap(aboveWristPointsIJ);
291+
points_xyz->swap(aboveWristPointsXYZ);
292+
293+
// recompute contour
294+
computeContour(xyzMap, points.get(), points_xyz.get(), topLeftPt, num_points);
295+
271296
// ** Detect fingers **
297+
298+
// recompute convex hull based on new contour
299+
convexHull.clear(); getConvexHull();
300+
301+
// compute defects
302+
std::vector<cv::Vec4i> defects;
303+
304+
if (indexHull.size() > 3)
305+
{
306+
std::vector<int> tmpHull;
307+
308+
cv::convexityDefects(contour, indexHull, defects);
309+
}
310+
272311
// sort all defects found by angle
273312
DefectComparer comparer(contour, defects, this->palmCenterIJ);
274313
std::sort(defects.begin(), defects.end(), comparer);
@@ -290,19 +329,11 @@ namespace ark {
290329
// contains info about the defect
291330
cv::Vec4i defect = defects[i];
292331

293-
// skip if defect is under wrist
294-
if (direction == -1) {
295-
if (wristL <= wristR) {
296-
if (defect[2] >= wristL && defect[2] <= wristR) continue;
297-
}
298-
else if (defect[2] <= wristR || defect[2] >= wristL) continue;
299-
}
300-
else {
301-
if (wristL <= wristR) {
302-
if (defect[2] <= wristL || defect[2] >= wristR) continue;
303-
}
304-
else if (defect[2] >= wristR && defect[2] <= wristL) continue;
305-
}
332+
#ifdef DEBUG
333+
cv::line(defectVisual, contour[defects[i][0]], contour[defects[i][2]], cv::Scalar(0, 255, 0));
334+
cv::line(defectVisual, contour[defects[i][1]], contour[defects[i][2]], cv::Scalar(0, 0, 255));
335+
cv::circle(defectVisual, contour[defects[i][2]], 5, cv::Scalar(255,255,255), 2);
336+
#endif
306337

307338
// point on convex hull where defect begins. fingertip candidate
308339
Point2i start = contour[defect[0]] - topLeftPt;
@@ -397,6 +428,8 @@ namespace ark {
397428
cv::Scalar(0, 255, 255), 2);
398429
cv::rectangle(visual, wristL_ij - Point2i(10, 10), wristL_ij + Point2i(10, 10),
399430
cv::Scalar(0, 255, 255), 2);
431+
432+
cv::imshow("[Hand Defects Debug]", defectVisual);
400433
#endif
401434

402435
// select fingers from candidates
@@ -684,20 +717,28 @@ namespace ark {
684717
cv::imshow("[Hand Debug]", visual);
685718
#endif
686719

720+
size_t nFin = this->fingersIJ.size();
721+
687722
// report not hand if there are too few/many fingers
688-
if (this->fingersIJ.size() > 6 || this->fingersIJ.size() < 1) {
723+
if (nFin > 6 || nFin < 1) {
689724
return false;
690725
}
726+
727+
// find dominant direction
728+
729+
if (nFin > 0) {
730+
Point2f fingCen = this->fingersIJ[nFin / 2];
731+
if (nFin % 2 == 0) {
732+
fingCen += Point2f(this->fingersIJ[nFin / 2 - 1]);
733+
fingCen /= 2.0f;
734+
}
691735

692-
// Final SVM check
693-
if (params->handUseSVM && handClassifier.isTrained()) {
694-
this->isHand = true;
695-
696-
std::vector<double> features =
697-
classifier::HandClassifier::extractHandFeatures(*this, xyzMap, topLeftPt, 1.0,
698-
fullMapSize.width);
736+
this->dominantDir = fingCen - Point2f(this->palmCenterIJ);
737+
}
699738

700-
this->svmConfidence = handClassifier.classify(features);
739+
// Final SVM check
740+
if (params->handUseSVM && handValidator.isTrained()) {
741+
this->svmConfidence = handValidator.classify(*this, xyzMap, 32, 5, topLeftPt, fullMapSize.width);
701742
if (this->svmConfidence < params->handSVMConfidenceThresh) {
702743
// SVM confidence value below threshold, reverse decision & destroy the hand instance
703744
return false;
@@ -868,7 +909,12 @@ namespace ark {
868909
return circleRadius;
869910
}
870911

871-
double Hand::getSVMConfidence() const
912+
Point2f Hand::getDominantDirection() const
913+
{
914+
return dominantDir;
915+
}
916+
917+
float Hand::getSVMConfidence() const
872918
{
873919
return svmConfidence;
874920
}

Hand.h

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
#pragma once
2-
#include "stdafx.h"
2+
33
#include "FrameObject.h"
44
#include "FramePlane.h"
5-
#include "HandClassifier.h"
65
#include "version.h"
76

87
namespace ark {
@@ -112,10 +111,16 @@ namespace ark {
112111
*/
113112
double getCircleRadius() const;
114113

114+
/**
115+
* Get a unit vector pointing towards the "dominant" direction of the hand
116+
* i.e. from the center towards the fingers
117+
*/
118+
Point2f getDominantDirection() const;
119+
115120
/**
116121
* Get the SVM confidence rating of this object, [0.0, 1.0] (higher = more likely to be hand)
117122
*/
118-
double getSVMConfidence() const;
123+
float getSVMConfidence() const;
119124

120125
/**
121126
* True if hand cluster touches the bottom edge
@@ -172,11 +177,6 @@ namespace ark {
172177
*/
173178
bool isValidHand() const;
174179

175-
/**
176-
* SVM Hand classifier instance
177-
*/
178-
static classifier::HandClassifier & handClassifier;
179-
180180
protected:
181181
/**
182182
* Amount the gray map is scaled by for contour finding.
@@ -243,11 +243,16 @@ namespace ark {
243243
*/
244244
double circleRadius;
245245

246+
/**
247+
* stores the dominant direction of hand
248+
*/
249+
Point2f dominantDir;
250+
246251
/**
247252
* The confidence value (in [0, 1]) assigned to this hand by the SVM classifier,
248253
* higher = more likely to be hand
249254
*/
250-
double svmConfidence;
255+
float svmConfidence;
251256

252257
/**
253258
* Whether the hand object is actually valid
@@ -266,7 +271,6 @@ namespace ark {
266271
*/
267272
bool rightEdgeConnected = false;
268273

269-
270274
};
271275

272276
/* Shared pointer for Hand **/

0 commit comments

Comments
 (0)