mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 13:28:09 +02:00
Basic Haar cascade/CamShift tracking.
This commit is contained in:
parent
d4d662b3f5
commit
0dc0552f50
3 changed files with 26237 additions and 13 deletions
26161
interface/resources/haarcascades/haarcascade_frontalface_alt.xml
Normal file
26161
interface/resources/haarcascades/haarcascade_frontalface_alt.xml
Normal file
File diff suppressed because it is too large
Load diff
|
@ -19,9 +19,11 @@
|
||||||
#include "Webcam.h"
|
#include "Webcam.h"
|
||||||
|
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
// register OpenCV matrix type with Qt metatype system
|
// register OpenCV matrix type with Qt metatype system
|
||||||
int matMetaType = qRegisterMetaType<Mat>("cv::Mat");
|
int matMetaType = qRegisterMetaType<Mat>("cv::Mat");
|
||||||
|
int rotatedRectMetaType = qRegisterMetaType<RotatedRect>("cv::RotatedRect");
|
||||||
|
|
||||||
Webcam::Webcam() : _enabled(false), _frameTextureID(0) {
|
Webcam::Webcam() : _enabled(false), _frameTextureID(0) {
|
||||||
// the grabber simply runs as fast as possible
|
// the grabber simply runs as fast as possible
|
||||||
|
@ -69,6 +71,17 @@ void Webcam::renderPreview(int screenWidth, int screenHeight) {
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
glDisable(GL_TEXTURE_2D);
|
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];
|
char fps[20];
|
||||||
sprintf(fps, "FPS: %d", (int)(roundf(_frameCount * 1000000.0f / (usecTimestampNow() - _startTimestamp))));
|
sprintf(fps, "FPS: %d", (int)(roundf(_frameCount * 1000000.0f / (usecTimestampNow() - _startTimestamp))));
|
||||||
drawtext(left, top + PREVIEW_HEIGHT + 20, 0.10, 0, 1, 0, fps);
|
drawtext(left, top + PREVIEW_HEIGHT + 20, 0.10, 0, 1, 0, fps);
|
||||||
|
@ -83,25 +96,26 @@ Webcam::~Webcam() {
|
||||||
delete _grabber;
|
delete _grabber;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Webcam::setFrame(const Mat& frame) {
|
void Webcam::setFrame(const Mat& frame, const RotatedRect& faceRect) {
|
||||||
IplImage image = frame;
|
IplImage image = frame;
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, image.widthStep);
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, image.widthStep / 3);
|
||||||
if (_frameTextureID == 0) {
|
if (_frameTextureID == 0) {
|
||||||
glGenTextures(1, &_frameTextureID);
|
glGenTextures(1, &_frameTextureID);
|
||||||
glBindTexture(GL_TEXTURE_2D, _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);
|
GL_UNSIGNED_BYTE, image.imageData);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
printLog("Capturing webcam at %dx%d.\n", _frameWidth, _frameHeight);
|
printLog("Capturing webcam at %dx%d.\n", _frameWidth, _frameHeight);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
glBindTexture(GL_TEXTURE_2D, _frameTextureID);
|
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);
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
glBindTexture(GL_TEXTURE_2D, 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++;
|
_frameCount++;
|
||||||
|
|
||||||
const int MAX_FPS = 60;
|
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()));
|
QTimer::singleShot(qMax((int)remaining / 1000, 0), _grabber, SLOT(grabFrame()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FrameGrabber::FrameGrabber() : _capture(0), _searchWindow(0, 0, 0, 0) {
|
||||||
|
}
|
||||||
|
|
||||||
FrameGrabber::~FrameGrabber() {
|
FrameGrabber::~FrameGrabber() {
|
||||||
if (_capture != 0) {
|
if (_capture != 0) {
|
||||||
cvReleaseCapture(&_capture);
|
cvReleaseCapture(&_capture);
|
||||||
|
@ -139,6 +156,11 @@ void FrameGrabber::grabFrame() {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
configureCamera(0x5ac, 0x8510, false, 0.99, 0.5, 0.5, 0.5, true, 0.5);
|
configureCamera(0x5ac, 0x8510, false, 0.99, 0.5, 0.5, 0.5, true, 0.5);
|
||||||
#endif
|
#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);
|
IplImage* image = cvQueryFrame(_capture);
|
||||||
if (image == 0) {
|
if (image == 0) {
|
||||||
|
@ -153,10 +175,42 @@ void FrameGrabber::grabFrame() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert to grayscale and equalize
|
// if we don't have a search window (yet), try using the face cascade
|
||||||
Mat frame = image, grayFrame;
|
Mat frame = image;
|
||||||
cvtColor(frame, grayFrame, CV_BGR2GRAY);
|
int channels = 0;
|
||||||
equalizeHist(grayFrame, grayFrame);
|
float ranges[] = { 0, 180 };
|
||||||
|
const float* range = ranges;
|
||||||
QMetaObject::invokeMethod(Application::getInstance()->getWebcam(), "setFrame", Q_ARG(cv::Mat, grayFrame));
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
void setEnabled(bool enabled);
|
void setEnabled(bool enabled);
|
||||||
void setFrame(const cv::Mat& image);
|
void setFrame(const cv::Mat& image, const cv::RotatedRect& faceRect);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ private:
|
||||||
int _frameWidth;
|
int _frameWidth;
|
||||||
int _frameHeight;
|
int _frameHeight;
|
||||||
GLuint _frameTextureID;
|
GLuint _frameTextureID;
|
||||||
|
cv::RotatedRect _faceRect;
|
||||||
|
|
||||||
long long _startTimestamp;
|
long long _startTimestamp;
|
||||||
int _frameCount;
|
int _frameCount;
|
||||||
|
@ -59,7 +60,7 @@ class FrameGrabber : public QObject {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FrameGrabber() : _capture(0) { }
|
FrameGrabber();
|
||||||
virtual ~FrameGrabber();
|
virtual ~FrameGrabber();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@ -68,10 +69,18 @@ public slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void updateHSVFrame(const cv::Mat& frame);
|
||||||
|
|
||||||
CvCapture* _capture;
|
CvCapture* _capture;
|
||||||
cv::CascadeClassifier _faceCascade;
|
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::Mat)
|
||||||
|
Q_DECLARE_METATYPE(cv::RotatedRect)
|
||||||
|
|
||||||
#endif /* defined(__interface__Webcam__) */
|
#endif /* defined(__interface__Webcam__) */
|
||||||
|
|
Loading…
Reference in a new issue