Skip to content

Instantly share code, notes, and snippets.

@abhigarg
Forked from lamberta/camshifting.c
Created April 9, 2014 11:27
Show Gist options
  • Save abhigarg/10257474 to your computer and use it in GitHub Desktop.
Save abhigarg/10257474 to your computer and use it in GitHub Desktop.
#include "camshifting.h"
/* Create a camshift tracked object from a region in image. */
TrackedObj* create_tracked_object (IplImage* image, CvRect* region) {
TrackedObj* obj;
//allocate memory for tracked object struct
if((obj = malloc(sizeof *obj)) != NULL) {
//create-image: size(w,h), bit depth, channels
obj->hsv = cvCreateImage(cvGetSize(image), 8, 3);
obj->mask = cvCreateImage(cvGetSize(image), 8, 1);
obj->hue = cvCreateImage(cvGetSize(image), 8, 1);
obj->prob = cvCreateImage(cvGetSize(image), 8, 1);
int hist_bins = 30; //number of histogram bins
float hist_range[] = {0,180}; //histogram range
float* range = hist_range;
obj->hist = cvCreateHist(1, //number of hist dimensions
&hist_bins, //array of dimension sizes
CV_HIST_ARRAY, //representation format
&range, //array of ranges for bins
1); //uniformity flag
}
//create a new hue image
update_hue_image(image, obj);
float max_val = 0.f;
//create a histogram representation for the face
cvSetImageROI(obj->hue, *region);
cvSetImageROI(obj->mask, *region);
cvCalcHist(&obj->hue, obj->hist, 0, obj->mask);
cvGetMinMaxHistValue(obj->hist, 0, &max_val, 0, 0 );
cvConvertScale(obj->hist->bins, obj->hist->bins,
max_val ? 255.0/max_val : 0, 0);
cvResetImageROI(obj->hue);
cvResetImageROI(obj->mask);
//store the previous face location
obj->prev_rect = *region;
return obj;
}
/* Release resources from tracked object. */
void destroy_tracked_object (TrackedObj* obj) {
cvReleaseImage(&obj->hsv);
cvReleaseImage(&obj->hue);
cvReleaseImage(&obj->mask);
cvReleaseImage(&obj->prob);
cvReleaseHist(&obj->hist);
free(obj);
}
/* Given an image and tracked object, return box position. */
CvBox2D camshift_track_face (IplImage* image, TrackedObj* obj) {
CvConnectedComp components;
//create a new hue image
update_hue_image(image, obj);
//create a probability image based on the face histogram
cvCalcBackProject(&obj->hue, obj->prob, obj->hist);
cvAnd(obj->prob, obj->mask, obj->prob, 0);
//use CamShift to find the center of the new face probability
cvCamShift(obj->prob, obj->prev_rect,
cvTermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1),
&components, &obj->curr_box);
//update face location and angle
obj->prev_rect = components.rect;
obj->curr_box.angle = -obj->curr_box.angle;
return obj->curr_box;
}
void update_hue_image (const IplImage* image, TrackedObj* obj) {
//limits for calculating hue
int vmin = 65, vmax = 256, smin = 55;
//convert to HSV color model
cvCvtColor(image, obj->hsv, CV_BGR2HSV);
//mask out-of-range values
cvInRangeS(obj->hsv, //source
cvScalar(0, smin, MIN(vmin, vmax), 0), //lower bound
cvScalar(180, 256, MAX(vmin, vmax) ,0), //upper bound
obj->mask); //destination
//extract the hue channel, split: src, dest channels
cvSplit(obj->hsv, obj->hue, 0, 0, 0 );
}
#ifndef INC_OPENCV
#define INC_OPENCV
#include <stdio.h>
#include <cv.h>
#include <highgui.h>
#endif
#ifndef INC_CAMSHIFTING
#define INC_CAMSHIFTING
typedef struct {
IplImage* hsv; //input image converted to HSV
IplImage* hue; //hue channel of HSV image
IplImage* mask; //image for masking pixels
IplImage* prob; //face probability estimates for each pixel
CvHistogram* hist; //histogram of hue in original face image
CvRect prev_rect; //location of face in previous frame
CvBox2D curr_box; //current face location estimate
} TrackedObj;
TrackedObj* create_tracked_object (IplImage* image, CvRect* face_rect);
void destroy_tracked_object (TrackedObj* tracked_obj);
CvBox2D camshift_track_face (IplImage* image, TrackedObj* imgs);
void update_hue_image (const IplImage* image, TrackedObj* imgs);
#endif
#compile
gcc trackface.c camshifting.c -o trackface $(pkg-config --cflags --libs opencv)
/** Basic OpenCV example for face detecion and tracking with CamShift.
* First run Haar classifier face detection on every frame until finding face,
* then switch over to CamShift using face region to track.
**/
#include "trackface.h"
#include "camshifting.h"
#define OPENCV_DATA "/home/billy/src/opencv/opencv/data/haarcascades/"
char *classifer = OPENCV_DATA "haarcascade_frontalface_default.xml";
int main (int argc, char** argv) {
if (argc != 2) print_help(argv[0]); //check usage
//declare
CvHaarClassifierCascade* cascade = 0;
CvMemStorage* storage = 0;
char* window_name = "haar window";
//initialize
cascade = (CvHaarClassifierCascade*) cvLoad(classifer, 0, 0, 0 );
storage = cvCreateMemStorage(0);
//validate
assert(cascade && storage);
//create window
cvNamedWindow(window_name, CV_WINDOW_AUTOSIZE);
CvRect* face_rect = 0;
//video file?
CvCapture* capture = cvCaptureFromFile(argv[1]);
IplImage* image = 0;
if (capture) {
//run loop, exit on ESC
while (1) {
image = capture_video_frame(capture);
if (!image) break;
face_rect = detect_face(image, cascade, storage);
cvShowImage(window_name, image); //display
//face detected, exit loop and track
if (face_rect) break;
//exit program on ESC
if ((char)27 == cvWaitKey(10)) {
cvReleaseCapture(&capture);
cleanup(window_name, cascade, storage);
exit(0);
}
}
printf("Face detected, tracking with CamShift...\n");
TrackedObj* tracked_obj = create_tracked_object(image, face_rect);
CvBox2D face_box; //area to draw
//track detected face with camshift
while (1) {
//get next video frame
image = capture_video_frame(capture);
if (!image) break;
//track the face in the new frame
face_box = camshift_track_face(image, tracked_obj);
//outline face ellipse
cvEllipseBox(image, face_box, CV_RGB(255,0,0), 3, CV_AA, 0);
cvShowImage(window_name, image); //display
//exit on ESC
if ((char)27 == cvWaitKey(10)) break;
}
//free memory
destroy_tracked_object(tracked_obj);
cvReleaseCapture(&capture);
}
//release resources and exit
cleanup(window_name, cascade, storage);
}
/* Given an image and a classider, detect and return region. */
CvRect* detect_face (IplImage* image,
CvHaarClassifierCascade* cascade,
CvMemStorage* storage) {
CvRect* rect = 0;
//get a sequence of faces in image
CvSeq *faces = cvHaarDetectObjects(image, cascade, storage,
1.1, //increase search scale by 10% each pass
6, //require 6 neighbors
CV_HAAR_DO_CANNY_PRUNING, //skip regions unlikely to contain a face
cvSize(0, 0)); //use default face size from xml
//if one or more faces are detected, return the first one
if(faces && faces->total)
rect = (CvRect*) cvGetSeqElem(faces, 0);
return rect;
}
/* Capture frame and return a copy so not to write to source. */
IplImage* capture_video_frame (CvCapture* capture) {
//capture the next frame
frame_curr = cvQueryFrame(capture);
frame_copy = cvCreateImage(cvGetSize(frame_curr), 8, 3);
assert(frame_curr && frame_copy); //make sure it's there
//make copy of frame so we don't write to src
cvCopy(frame_curr, frame_copy, NULL);
frame_copy->origin = frame_curr->origin;
//invert if needed, 1 means the image is inverted
if (frame_copy->origin == 1) {
cvFlip(frame_copy, 0, 0);
frame_copy->origin = 0;
}
return frame_copy;
}
void cleanup (char* name,
CvHaarClassifierCascade* cascade,
CvMemStorage* storage) {
//cleanup and release resources
cvDestroyWindow(name);
if(cascade) cvReleaseHaarClassifierCascade(&cascade);
if(storage) cvReleaseMemStorage(&storage);
}
void print_help (char* name) {
printf("Usage: %s [video]\n", name);
exit(-1);
}
#ifndef INC_OPENCV
#define INC_OPENCV
#include <stdio.h>
#include <cv.h>
#include <highgui.h>
#endif
IplImage* capture_video_frame (CvCapture*);
CvRect* detect_face (IplImage*, CvHaarClassifierCascade*, CvMemStorage*);
void cleanup (char*, CvHaarClassifierCascade*, CvMemStorage*);
void print_help (char*);
//used by capture_video_frame, so we don't have to keep creating.
IplImage* frame_curr;
IplImage* frame_copy;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment