Basic Haar cascade/CamShift tracking.

This commit is contained in:
Andrzej Kapolka 2013-06-19 18:02:26 -07:00
parent d4d662b3f5
commit 0dc0552f50
3 changed files with 26237 additions and 13 deletions

File diff suppressed because it is too large Load diff

View file

@ -19,9 +19,11 @@
#include "Webcam.h"
using namespace cv;
using namespace std;
// register OpenCV matrix type with Qt metatype system
int matMetaType = qRegisterMetaType<Mat>("cv::Mat");
int rotatedRectMetaType = qRegisterMetaType<RotatedRect>("cv::RotatedRect");
Webcam::Webcam() : _enabled(false), _frameTextureID(0) {
// the grabber simply runs as fast as possible
@ -69,6 +71,17 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) {
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
glBegin(GL_LINE_LOOP);
Point2f facePoints[4];
_faceRect.points(facePoints);
float xScale = previewWidth / (float)_frameWidth;
float yScale = PREVIEW_HEIGHT / (float)_frameHeight;
glVertex2f(left + facePoints[0].x * xScale, top + facePoints[0].y * yScale);
glVertex2f(left + facePoints[1].x * xScale, top + facePoints[1].y * yScale);
glVertex2f(left + facePoints[2].x * xScale, top + facePoints[2].y * yScale);
glVertex2f(left + facePoints[3].x * xScale, top + facePoints[3].y * yScale);
glEnd();
char fps[20];
sprintf(fps, "FPS: %d", (int)(roundf(_frameCount * 1000000.0f / (usecTimestampNow() - _startTimestamp))));
drawtext(left, top + PREVIEW_HEIGHT + 20, 0.10, 0, 1, 0, fps);
@ -83,25 +96,26 @@ Webcam::~Webcam() {
delete _grabber;
}
void Webcam::setFrame(const Mat& frame) {
void Webcam::setFrame(const Mat& frame, const RotatedRect& faceRect) {
IplImage image = frame;
glPixelStorei(GL_UNPACK_ROW_LENGTH, image.widthStep);
glPixelStorei(GL_UNPACK_ROW_LENGTH, image.widthStep / 3);
if (_frameTextureID == 0) {
glGenTextures(1, &_frameTextureID);
glBindTexture(GL_TEXTURE_2D, _frameTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, _frameWidth = image.width, _frameHeight = image.height, 0, GL_LUMINANCE,
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, _frameWidth = image.width, _frameHeight = image.height, 0, GL_BGR,
GL_UNSIGNED_BYTE, image.imageData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
printLog("Capturing webcam at %dx%d.\n", _frameWidth, _frameHeight);
} else {
glBindTexture(GL_TEXTURE_2D, _frameTextureID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _frameWidth, _frameHeight, GL_LUMINANCE, GL_UNSIGNED_BYTE, image.imageData);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _frameWidth, _frameHeight, GL_BGR, GL_UNSIGNED_BYTE, image.imageData);
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// update our frame count for fps computation
// store our face rect, update our frame count for fps computation
_faceRect = faceRect;
_frameCount++;
const int MAX_FPS = 60;
@ -119,6 +133,9 @@ void Webcam::setFrame(const Mat& frame) {
QTimer::singleShot(qMax((int)remaining / 1000, 0), _grabber, SLOT(grabFrame()));
}
FrameGrabber::FrameGrabber() : _capture(0), _searchWindow(0, 0, 0, 0) {
}
FrameGrabber::~FrameGrabber() {
if (_capture != 0) {
cvReleaseCapture(&_capture);
@ -139,6 +156,11 @@ void FrameGrabber::grabFrame() {
#ifdef __APPLE__
configureCamera(0x5ac, 0x8510, false, 0.99, 0.5, 0.5, 0.5, true, 0.5);
#endif
switchToResourcesParentIfRequired();
if (!_faceCascade.load("resources/haarcascades/haarcascade_frontalface_alt.xml")) {
printLog("Failed to load Haar cascade for face tracking.\n");
}
}
IplImage* image = cvQueryFrame(_capture);
if (image == 0) {
@ -153,10 +175,42 @@ void FrameGrabber::grabFrame() {
return;
}
// convert to grayscale and equalize
Mat frame = image, grayFrame;
cvtColor(frame, grayFrame, CV_BGR2GRAY);
equalizeHist(grayFrame, grayFrame);
QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame", Q_ARG(cv::Mat, grayFrame));
// if we don't have a search window (yet), try using the face cascade
Mat frame = image;
int channels = 0;
float ranges[] = { 0, 180 };
const float* range = ranges;
if (_searchWindow.area() == 0) {
vector<Rect> faces;
_faceCascade.detectMultiScale(frame, faces, 1.1, 6);
if (!faces.empty()) {
_searchWindow = faces.front();
updateHSVFrame(frame);
Mat faceHsv(_hsvFrame, _searchWindow);
Mat faceMask(_mask, _searchWindow);
int sizes = 30;
calcHist(&faceHsv, 1, &channels, faceMask, _histogram, 1, &sizes, &range);
double min, max;
minMaxLoc(_histogram, &min, &max);
_histogram.convertTo(_histogram, -1, (max == 0.0) ? 0.0 : 255.0 / max);
}
}
RotatedRect faceRect;
if (_searchWindow.area() > 0) {
updateHSVFrame(frame);
calcBackProject(&_hsvFrame, 1, &channels, _histogram, _backProject, &range);
bitwise_and(_backProject, _mask, _backProject);
faceRect = CamShift(_backProject, _searchWindow, TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1));
_searchWindow = faceRect.boundingRect();
}
QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame",
Q_ARG(cv::Mat, frame), Q_ARG(cv::RotatedRect, faceRect));
}
void FrameGrabber::updateHSVFrame(const Mat& frame) {
cvtColor(frame, _hsvFrame, CV_BGR2HSV);
inRange(_hsvFrame, Scalar(0, 55, 65), Scalar(180, 256, 256), _mask);
}

View file

@ -36,7 +36,7 @@ public:
public slots:
void setEnabled(bool enabled);
void setFrame(const cv::Mat& image);
void setFrame(const cv::Mat& image, const cv::RotatedRect& faceRect);
private:
@ -47,6 +47,7 @@ private:
int _frameWidth;
int _frameHeight;
GLuint _frameTextureID;
cv::RotatedRect _faceRect;
long long _startTimestamp;
int _frameCount;
@ -59,7 +60,7 @@ class FrameGrabber : public QObject {
public:
FrameGrabber() : _capture(0) { }
FrameGrabber();
virtual ~FrameGrabber();
public slots:
@ -68,10 +69,18 @@ public slots:
private:
void updateHSVFrame(const cv::Mat& frame);
CvCapture* _capture;
cv::CascadeClassifier _faceCascade;
cv::Mat _hsvFrame;
cv::Mat _mask;
cv::SparseMat _histogram;
cv::Mat _backProject;
cv::Rect _searchWindow;
};
Q_DECLARE_METATYPE(cv::Mat)
Q_DECLARE_METATYPE(cv::RotatedRect)
#endif /* defined(__interface__Webcam__) */