Created
November 17, 2015 22:05
-
-
Save joshuajnoble/a3c9f974602705a70eb1 to your computer and use it in GitHub Desktop.
convex finding in OF
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "ofxOpenCv.h" | |
#include "background_segm.hpp" | |
#include "ofMain.h" | |
class ofApp : public ofBaseApp{ | |
public: | |
void setup(); | |
void update(); | |
void draw(); | |
void keyPressed(int key); | |
void keyReleased(int key); | |
void mouseMoved(int x, int y ); | |
void mouseDragged(int x, int y, int button); | |
void mousePressed(int x, int y, int button); | |
void mouseReleased(int x, int y, int button); | |
void mouseEntered(int x, int y); | |
void mouseExited(int x, int y); | |
void windowResized(int w, int h); | |
void dragEvent(ofDragInfo dragInfo); | |
void gotMessage(ofMessage msg); | |
double dist(cv::Point x, cv::Point y); | |
pair<cv::Point,double> circleFromPoints(cv::Point p1, cv::Point p2, cv::Point p3); | |
cv::BackgroundSubtractorMOG2 bg; | |
cv::Mat frame, back, fore; | |
vector<pair<cv::Point,double> > palm_centers; | |
vector<vector<cv::Point> > contours; | |
cv::Point rough_palm_center; | |
int backgroundFrame=500; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ofApp.h" | |
//-------------------------------------------------------------- | |
void ofApp::setup(){ | |
} | |
//This function returns the radius and the center of the circle given 3 points | |
//If a circle cannot be formed , it returns a zero radius circle centered at (0,0) | |
pair<cv::Point,double> ofApp::circleFromPoints(cv::Point p1, cv::Point p2, cv::Point p3) | |
{ | |
double offset = pow(p2.x,2) +pow(p2.y,2); | |
double bc = ( pow(p1.x,2) + pow(p1.y,2) - offset )/2.0; | |
double cd = (offset - pow(p3.x, 2) - pow(p3.y, 2))/2.0; | |
double det = (p1.x - p2.x) * (p2.y - p3.y) - (p2.x - p3.x)* (p1.y - p2.y); | |
if (abs(det) < 0.0000001) | |
{ | |
cout<< "POINTS TOO CLOSE" << endl; | |
return make_pair(cv::Point(0,0),0); | |
} | |
double idet = 1/det; | |
double centerx = (bc * (p2.y - p3.y) - cd * (p1.y - p2.y)) * idet; | |
double centery = (cd * (p1.x - p2.x) - bc * (p2.x - p3.x)) * idet; | |
double radius = sqrt( pow(p2.x - centerx,2) + pow(p2.y-centery,2)); | |
return make_pair(cv::Point(centerx,centery),radius); | |
} | |
//This function returns the square of the euclidean distance between 2 points. | |
double ofApp::dist(cv::Point x, cv::Point y) | |
{ | |
return (x.x-y.x)*(x.x-y.x)+(x.y-y.y)*(x.y-y.y); | |
} | |
//-------------------------------------------------------------- | |
void ofApp::update() | |
{ | |
// clear all contours | |
vector<vector<cv::Point>>::iterator it; | |
for( it = contours.begin(); it != contours.end(); ++it) | |
{ | |
it->clear(); | |
} | |
contours.clear(); | |
if(backgroundFrame>0) | |
{ | |
bg.operator ()(frame,fore); | |
backgroundFrame--; | |
} | |
else | |
{ | |
bg.operator()(frame,fore,0); | |
} | |
//Get background image to display it | |
bg.getBackgroundImage(back); | |
//Enhance edges in the foreground by applying erosion and dilation | |
erode(fore, fore, cv::Mat()); | |
dilate(fore, fore, cv::Mat()); | |
//Find the contours in the foreground | |
findContours(fore,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE); | |
for(int i=0;i<contours.size();i++) | |
//Ignore all small insignificant areas | |
if(contourArea(contours[i])>=5000) | |
{ | |
//Draw contour | |
vector<vector<cv::Point> > tcontours; | |
tcontours.push_back(contours[i]); | |
drawContours(frame,tcontours,-1,cv::Scalar(0,0,255),2); | |
//Detect Hull in current contour | |
vector<vector<cv::Point> > hulls(1); | |
vector<vector<int> > hullsI(1); | |
convexHull(cv::Mat(tcontours[0]),hulls[0],false); | |
convexHull(cv::Mat(tcontours[0]),hullsI[0],false); | |
drawContours(frame,hulls,-1,cv::Scalar(0,255,0),2); | |
//Find minimum area rectangle to enclose hand | |
cv::RotatedRect rect=minAreaRect(cv::Mat(tcontours[0])); | |
//Find Convex Defects | |
vector<cv::Vec4i> defects; | |
if(hullsI[0].size()>0) | |
{ | |
cv::Point2f rect_points[4]; rect.points( rect_points ); | |
for( int j = 0; j < 4; j++ ) | |
line( frame, rect_points[j], rect_points[(j+1)%4], cv::Scalar(255,0,0), 1, 8 ); | |
cv::Point rough_palm_center; | |
convexityDefects(tcontours[0], hullsI[0], defects); | |
if(defects.size()>=3) | |
{ | |
vector<cv::Point> palm_points; | |
for(int j=0;j<defects.size();j++) | |
{ | |
int startidx=defects[j][0]; | |
cv::Point ptStart( tcontours[0][startidx] ); | |
int endidx=defects[j][1]; | |
cv::Point ptEnd( tcontours[0][endidx] ); | |
int faridx=defects[j][2]; | |
cv::Point ptFar( tcontours[0][faridx] ); | |
//Sum up all the hull and defect points to compute average | |
rough_palm_center += ptFar + ptStart+ ptEnd; | |
palm_points.push_back(ptFar); | |
palm_points.push_back(ptStart); | |
palm_points.push_back(ptEnd); | |
} | |
//Get palm center by 1st getting the average of all defect points, this is the rough palm center, | |
//Then U chose the closest 3 points ang get the circle radius and center formed from them which is the palm center. | |
rough_palm_center.x /= defects.size()*3; | |
rough_palm_center.y /= defects.size()*3; | |
cv::Point closest_pt=palm_points[0]; | |
vector<pair<double,int> > distvec; | |
for(int i=0;i<palm_points.size();i++) | |
distvec.push_back(make_pair(dist(rough_palm_center,palm_points[i]),i)); | |
sort(distvec.begin(),distvec.end()); | |
//Keep choosing 3 points till you find a circle with a valid radius | |
//As there is a high chance that the closes points might be in a linear line or too close that it forms a very large circle | |
pair<cv::Point,double> soln_circle; | |
for(int i=0;i+2<distvec.size();i++) | |
{ | |
cv::Point p1=palm_points[distvec[i+0].second]; | |
cv::Point p2=palm_points[distvec[i+1].second]; | |
cv::Point p3=palm_points[distvec[i+2].second]; | |
soln_circle=circleFromPoints(p1,p2,p3);//Final palm center,radius | |
if(soln_circle.second!=0) | |
break; | |
} | |
//Find avg palm centers for the last few frames to stabilize its centers, also find the avg radius | |
palm_centers.push_back(soln_circle); | |
if(palm_centers.size()>10) | |
palm_centers.erase(palm_centers.begin()); | |
cv::Point palm_center; | |
double radius=0; | |
for(int i=0;i<palm_centers.size();i++) | |
{ | |
palm_center+=palm_centers[i].first; | |
radius+=palm_centers[i].second; | |
} | |
palm_center.x/=palm_centers.size(); | |
palm_center.y/=palm_centers.size(); | |
radius/=palm_centers.size(); | |
//Draw the palm center and the palm circle | |
//The size of the palm gives the depth of the hand | |
// circle(frame,palm_center,5,Scalar(144,144,255),3); | |
// circle(frame,palm_center,radius,Scalar(144,144,255),2); | |
//Detect fingers by finding points that form an almost isosceles triangle with certain thesholds | |
int no_of_fingers=0; | |
for(int j=0;j<defects.size();j++) | |
{ | |
int startidx=defects[j][0]; cv::Point ptStart( tcontours[0][startidx] ); | |
int endidx=defects[j][1]; cv::Point ptEnd( tcontours[0][endidx] ); | |
int faridx=defects[j][2]; cv::Point ptFar( tcontours[0][faridx] ); | |
//X o--------------------------o Y | |
double Xdist=sqrt(dist(palm_center,ptFar)); | |
double Ydist=sqrt(dist(palm_center,ptStart)); | |
double length=sqrt(dist(ptFar,ptStart)); | |
double retLength = sqrt(dist(ptEnd,ptFar)); | |
//Play with these thresholds to improve performance | |
if(length<=3*radius&&Ydist>=0.4*radius&&length>=10&&retLength>=10&&max(length,retLength)/min(length,retLength)>=0.8) | |
if(min(Xdist,Ydist)/max(Xdist,Ydist)<=0.8) | |
{ | |
if((Xdist>=0.1*radius&&Xdist<=1.3*radius&&Xdist<Ydist)||(Ydist>=0.1*radius&&Ydist<=1.3*radius&&Xdist>Ydist)) | |
{} | |
//line( frame, ptEnd, ptFar, cv::Scalar(0,255,0), 1 ),no_of_fingers++; | |
} | |
} | |
no_of_fingers=min(5,no_of_fingers); | |
cout<<"NO OF FINGERS: "<<no_of_fingers<<endl; | |
} | |
} | |
} | |
} | |
//-------------------------------------------------------------- | |
void ofApp::draw(){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::keyPressed(int key){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::keyReleased(int key){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::mouseMoved(int x, int y ){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::mouseDragged(int x, int y, int button){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::mousePressed(int x, int y, int button){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::mouseReleased(int x, int y, int button){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::mouseEntered(int x, int y){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::mouseExited(int x, int y){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::windowResized(int w, int h){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::gotMessage(ofMessage msg){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::dragEvent(ofDragInfo dragInfo){ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment