Skip to content

Instantly share code, notes, and snippets.

@blacklight
Created July 6, 2009 14:19
Show Gist options
  • Save blacklight/141455 to your computer and use it in GitHub Desktop.
Save blacklight/141455 to your computer and use it in GitHub Desktop.
/**
* FULL PACKAGE: -> http://blacklight.gotdns.org/prog/headtracking.tar.bz2
*/
/*
* Little project that uses OpenCV libraries and Haar cascade classifiers
* to recognize a face in a video stream (i.e. from a cam) and uses this
* knowledge to move mouse's cursor. This way you can move your mouse
* pointer just by moving your head in front of your webcam. This source
* was made to be completely modular. So you can track not just faces, but
* any kind of object to move your cursor, just changing the XML file
* containing Haar classifiers used by the program. So, you can move your
* cursor using your hand, your eyes, your nose, and whatever your fantasy
* may suggest.
*/
/*************************************************************************************
* HeadTracker v.0.1alpha *
* Copyright (C) 2009, BlackLight *
* *
* This program is free software: you can redistribute it and/or modify it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation, either version 3 of the License, or (at your *
* option) any later version. This program is distributed in the hope that *
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See *
* the GNU General Public License for more details. You should have received *
* a copy of the GNU General Public License along with this program. If not, *
* see <http://www.gnu.org/licenses/>. *
**************************************************************************************/
#include <cv.h>
#include <highgui.h>
#include <X11/Intrinsic.h>
#include <X11/extensions/XTest.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#define ABS(x) ((x<0) ? -(x) : (x))
#define MAXBUTTONCODES 3
typedef enum { Pressed, Released } ButtonStatus;
static CvMemStorage* storage = 0;
static CvHaarClassifierCascade* cascade = 0;
Display *disp;
Screen *screen;
Window root;
Window father, child;
ButtonStatus bs = Released;
int width, height;
char* cascade_name =
"haarcascade_frontalface_alt.xml";
CvCapture* capture = 0;
int getScreenResolution(int *width, int *height) {
*width = XWidthOfScreen (screen);
*height = XHeightOfScreen(screen);
return 0;
}
int getPointerPos (int *x, int *y) {
int dummyx, dummyy, mask;
if (!XQueryPointer(disp, root, &father, &child, x, y, &dummyx, &dummyy, &mask))
return -1;
XFlush(disp);
return 0;
}
int setPointerPos (int x, int y) {
if (!XWarpPointer(disp, None, root, 0, 0, width, height, x, y))
return -1;
XFlush(disp);
return 0;
}
int leftClick (Boolean set) {
if ((set && bs == Pressed) || (!set && bs == Released))
return 1;
bs = (set) ? Pressed : Released;
if (!XTestFakeButtonEvent(disp, 1, ((bs == Pressed) ? True : False), CurrentTime))
return -1;
return 0;
}
void detect_and_draw( IplImage* img, int *posx, int *posy ) {
int scale = 1;
IplImage* temp = cvCreateImage( cvSize(img->width/scale,img->height/scale), 8, 3 );
CvPoint pt1, pt2;
int i;
cvClearMemStorage(storage);
if (cascade) {
CvSeq* faces = cvHaarDetectObjects( img, cascade, storage,
1.1, 2, CV_HAAR_DO_CANNY_PRUNING,
cvSize(40, 40) );
for( i = 0; i < (faces ? faces->total : 0); i++ ) {
CvRect* r = (CvRect*)cvGetSeqElem( faces, i );
pt1.x = r->x*scale;
pt2.x = (r->x+r->width)*scale;
pt1.y = r->y*scale;
pt2.y = (r->y+r->height)*scale;
*posx = (r->x + (r->width/2))*scale;
*posy = (r->y + (r->height/2))*scale;
if ((ABS(pt2.x-pt1.x)/640.0) >= 0.37) {
cvRectangle( img, pt1, pt2, CV_RGB(0,0,255), 3, 8, 0 );
leftClick(True);
} else {
cvRectangle( img, pt1, pt2, CV_RGB(255,0,0), 3, 8, 0 );
leftClick(False);
}
}
}
cvShowImage( "Smart detection", img );
cvReleaseImage( &temp );
}
int cvInit() {
if (!(cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 ))) {
fprintf( stderr, "ERROR: Could not load classifier cascade\n" );
return -1;
}
storage = cvCreateMemStorage(0);
capture = cvCaptureFromCAM(0);
cvNamedWindow( "Smart detection", 1 );
}
int cvCaptureFrame(int *posx, int *posy) {
IplImage *frame, *frame_copy = 0;
if(!cvGrabFrame(capture)) {
cvReleaseImage( &frame_copy );
cvReleaseCapture( &capture );
return -1;
}
frame = cvRetrieveFrame(capture);
if(!frame) {
cvReleaseImage( &frame_copy );
cvReleaseCapture( &capture );
return -1;
}
if(!frame_copy)
frame_copy = cvCreateImage( cvSize(frame->width,frame->height),
IPL_DEPTH_8U, frame->nChannels );
if( frame->origin == IPL_ORIGIN_TL )
cvCopy( frame, frame_copy, 0 );
else
cvFlip( frame, frame_copy, 0 );
cvFlip( frame, frame_copy, 1 );
detect_and_draw(frame_copy, posx, posy);
if( cvWaitKey( 10 ) >= 0 ) {
cvReleaseImage( &frame_copy );
cvReleaseCapture( &capture );
return -1;
}
return 0;
}
int main( int argc, char** argv ) {
int ch;
FILE *fp;
while ((ch=getopt(argc,argv,"c:v:"))>0) {
switch (ch) {
case 'c':
cascade_name = strdup(optarg);
break;
}
}
if (!(fp=fopen(cascade_name,"r"))) {
fprintf( stderr,
"Error: Unable to open %s. Usage: %s [-c <haar_classifiers_xml_file>]\n" );
return -1;
}
fclose(fp);
if (cvInit()<0)
return -1;
if (capture) {
int headx=-1, heady=-1;
int prev_headx=headx, prev_heady=heady;
int curx, cury;
if (!(disp = XOpenDisplay(NULL)))
return -1;
screen = XDefaultScreenOfDisplay(disp);
root = DefaultRootWindow(disp);
if (getScreenResolution(&width, &height)<0) {
printf ("Fatal error while getting screen resolution\n");
return -1;
}
setPointerPos(width/2, height/2);
while (cvCaptureFrame(&headx, &heady)>=0) {
getPointerPos(&curx,&cury);
if (headx>0 && heady>0 && prev_headx>0 && prev_heady>0) {
curx -= 1.8*((prev_headx - headx)*(width/640));
cury -= 5.0*((prev_heady - heady)*(height/480));
setPointerPos(curx,cury);
}
prev_headx = headx;
prev_heady = heady;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment