Skip to content

Instantly share code, notes, and snippets.

@joshuajnoble
Created November 17, 2015 22:05
Show Gist options
  • Save joshuajnoble/a3c9f974602705a70eb1 to your computer and use it in GitHub Desktop.
Save joshuajnoble/a3c9f974602705a70eb1 to your computer and use it in GitHub Desktop.
convex finding in OF
#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;
};
#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