Created
July 6, 2009 14:19
-
-
Save blacklight/141455 to your computer and use it in GitHub Desktop.
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
/** | |
* 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