ImageSaliencyDetector::ImageSaliencyDetector(const cv::Mat1f& src) { if (src.empty()) { throw std::invalid_argument("ImageSaliencyDetector: Source image cannot be empty!"); } setSourceImage(src); mDensityEstimates.resize(mSrcImage.rows); for (int i = 0; i < mSrcImage.rows; ++i) { mDensityEstimates[i].resize(mSrcImage.cols); } }
std::vector<cv::Point2i> detectFingertips(cv::Mat1f z, float zMin = 0.0f, float zMax = 0.75f, cv::Mat1f& debugFrame = cv::Mat1f()) { using namespace cv; using namespace std; bool debug = !debugFrame.empty(); vector<Point2i> fingerTips; Mat handMask = z < zMax & z > zMin; std::vector<std::vector<cv::Point> > contours; findContours(handMask.clone(), contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); // we are cloning here since method will destruct the image if (contours.size()) { for (int i=0; i<contours.size(); i++) { vector<Point> contour = contours[i]; Mat contourMat = Mat(contour); double area = cv::contourArea(contourMat); if (area > 3000) { // possible hand Scalar center = mean(contourMat); Point centerPoint = Point(center.val[0], center.val[1]); vector<Point> approxCurve; cv::approxPolyDP(contourMat, approxCurve, 20, true); vector<int> hull; cv::convexHull(Mat(approxCurve), hull); // find upper and lower bounds of the hand and define cutoff threshold (don't consider lower vertices as fingers) int upper = 640, lower = 0; for (int j=0; j<hull.size(); j++) { int idx = hull[j]; // corner index if (approxCurve[idx].y < upper) upper = approxCurve[idx].y; if (approxCurve[idx].y > lower) lower = approxCurve[idx].y; } float cutoff = lower - (lower - upper) * 0.1f; // find interior angles of hull corners for (int j=0; j<hull.size(); j++) { int idx = hull[j]; // corner index int pdx = idx == 0 ? approxCurve.size() - 1 : idx - 1; // predecessor of idx int sdx = idx == approxCurve.size() - 1 ? 0 : idx + 1; // successor of idx Point v1 = approxCurve[sdx] - approxCurve[idx]; Point v2 = approxCurve[pdx] - approxCurve[idx]; float angle = acos( (v1.x*v2.x + v1.y*v2.y) / (norm(v1) * norm(v2)) ); // low interior angle + within upper 90% of region -> we got a finger if (angle < 1 && approxCurve[idx].y < cutoff) { int u = approxCurve[idx].x; int v = approxCurve[idx].y; fingerTips.push_back(Point2i(u,v)); if (debug) { cv::circle(debugFrame, approxCurve[idx], 10, Scalar(1), -1); } } } if (debug) { // draw cutoff threshold cv::line(debugFrame, Point(center.val[0]-100, cutoff), Point(center.val[0]+100, cutoff), Scalar(1.0f)); // draw approxCurve for (int j=0; j<approxCurve.size(); j++) { cv::circle(debugFrame, approxCurve[j], 10, Scalar(1.0f)); if (j != 0) { cv::line(debugFrame, approxCurve[j], approxCurve[j-1], Scalar(1.0f)); } else { cv::line(debugFrame, approxCurve[0], approxCurve[approxCurve.size()-1], Scalar(1.0f)); } } // draw approxCurve hull for (int j=0; j<hull.size(); j++) { cv::circle(debugFrame, approxCurve[hull[j]], 10, Scalar(1.0f), 3); if(j == 0) { cv::line(debugFrame, approxCurve[hull[j]], approxCurve[hull[hull.size()-1]], Scalar(1.0f)); } else { cv::line(debugFrame, approxCurve[hull[j]], approxCurve[hull[j-1]], Scalar(1.0f)); } } } } } } return fingerTips; }