眼球追踪
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
?#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include
#include
#include
using namespace std;
//programme parameters
const int kFastEyeWidth=50;
const bool kEnableWeight = true;
const float kWeightDivisor = 150.0;
const double kGradientThreshold = 50.0;
const int kWeightBlurSize = 5;
const bool kEnablePostProcess = true;
const float kPostProcessThreshold = 0.97;
const bool kPlotVectorField = false;
// Size constants
const int kEyePercentTop = 25;
const int kEyePercentSide = 13;
const int kEyePercentHeight = 30;
const int kEyePercentWidth = 35;
bool inMat(cv::Point p,int rows,int cols) {
return p.x >= 0 && p.x < cols && p.y >= 0 && p.y < rows;
}
bool floodShouldPushPoint(const cv::Point &np, const cv::Mat &mat) {
return inMat(np, mat.rows, mat.cols);
}
cv::Mat floodKillEdges(cv::Mat &mat) {
rectangle(mat,cv::Rect(0,0,mat.cols,mat.rows),255);
cv::Mat mask(mat.rows, mat.cols, CV_8U, 255);
std::queue
toDo.push(cv::Point(0,0));
while (!toDo.empty()) {
cv::Point p = toDo.front();
toDo.pop();
if (mat.at
continue;
}
// add in every direction
cv::Point np(p.x + 1, p.y); // right
if (floodShouldPushPoint(np, mat)) toDo.push(np);
np.x = p.x - 1; np.y = p.y; // left
if (floodShouldPushPoint(np, mat)) toDo.push(np);
np.x = p.x; np.y = p.y + 1; // down
if (floodShouldPushPoint(np, mat)) toDo.push(np);
np.x = p.x; np.y = p.y - 1; // up
if (floodShouldPushPoint(np, mat)) toDo.push(np);
// kill it
mat.at
mask.at
}
return mask;
}
//return a threshold stdDevFactor * stdDev + meanMagnGrad[0] to remove all the gradients that are below this threshold
double computeDynamicThreshold(const cv::Mat &mat, double stdDevFactor) {
cv::Scalar stdMagnGrad, meanMagnGrad;//standard deviation and mean
cv::meanStdDev(mat, meanMagnGrad, stdMagnGrad);
double stdDev = stdMagnGrad[0] / sqrt((double)(mat.rows*mat.cols));
return stdDevFactor * stdDev + meanMagnGrad[0];
}
//compute the magnitude of gradient
cv::Mat matrixMagnitude(const cv::Mat &matX, const cv::Mat &matY) {
cv::Mat mags(matX.rows,matX.cols,CV_64F);
for (int y = 0; y < matX.rows; ++y) {
const double *Xr = matX.ptr
double *Mr = mags.ptr
for (int x = 0; x < matX.cols; ++x) {
double gX = Xr[x], gY = Yr[x];
double magnitude = sqrt((gX * gX) + (gY * gY));
Mr[x] = magnitude;
}
}
return mags;
}
cv::Point unscalePoint(cv::Point p, cv::Rect origSize) {
float ratio = (((float)kFastEyeWidth)/origSize.width);
int x = cvRound(p.x / ratio);
int y = cvRound(p.y / ratio);
return cv::Point(x,y);
}
void scaleToFastSize(const cv::Mat &src,cv::Mat &dst) {
cv::resize(src, dst, cv::Size(kFastEyeWidth,(((float)kFastEyeWidth)/src.cols) * src.rows));
}
cv::Mat computeMatXGradient(const cv::Mat &mat) {
cv::Mat out(mat.rows,mat.cols,CV_64F);
for (int y = 0; y < mat.rows; ++y) {
const uchar *Mr = mat.ptr
double *Or = out.ptr
Or[0] = Mr[1] - Mr[0];
for (int x = 1; x < mat.cols - 1; ++x) {
Or[x] = (Mr[x+1] - Mr[x-1])/2.0;
}
Or[mat.cols-1] = Mr[mat.cols-1] - Mr[mat.cols-2];
}
return out;
}
void testPossibleCentersFormula(int x, int y, unsigned char weight,double gx, double gy, cv::Mat &out) {
// for all possible centers
for (int cy = 0; cy < out.rows; ++cy) {
double *Or = out.ptr
for (int cx = 0; cx < out.cols; ++cx) {
if (x == cx && y == cy) {
continue;
}
// create a vector from the possible center to the gradient origin
double dx = x - cx;
double dy = y - cy;
// normalize d
double magnitude = sqrt((dx * dx) + (dy * dy));
dx = dx / magnitude;
dy = dy / magnitude;
double dotProduct = dx*gx + dy*gy;
dotProduct = std::max(0.0,dotProduct);
// square and multiply by the weight
if (kEnableWeight) {
Or[cx] += dotProduct * dotProduct * (weight/kWeightDivisor);
} else {
Or[cx] += dotProduct * dotProduct;
}
}
}
}
cv::Point findEyeCenter(cv::Mat face, cv::Rect eye) {
cv::Mat eyeROIUnscaled = face(eye);
cv::Mat eyeROI;
scaleToFastSize(eyeROIUnscaled, eyeROI);
// draw eye region
rectangle(face,eye,1234);
//-- Find the gradient
cv::Mat gradientX = computeMatXGradient(eyeROI);
cv::Mat gradientY = computeMatXGradient(eyeROI.t()).t();
//-- Normalize and threshold the gradient
// compute all the magnitudes
cv::Mat mags = matrixMagnitude(gradientX, gradientY);
//compute the threshold
double gradientThresh = computeDynamicThreshold(mags, kGradientThreshold);
//double gradientThresh = kGradientThreshold;
//double gradientThresh = 0;
//normalize
for (int y = 0; y < eyeROI.rows; ++y) {
double *Xr = gradientX.ptr
const double *Mr = mags.ptr
for (int x = 0; x < eyeROI.cols; ++x) {
double gX = Xr[x], gY = Yr[x];
double magnitude = Mr[x];
if (magnitude > gradientThresh) {
Xr[x] = gX/magnitude;
Yr[x] = gY/magnitude;
} else {
Xr[x] = 0.0;
Yr[x] = 0.0;
}
}
}
//imshow(debugWindow,gradientX);
//-- Create a blurred and inverted image for weighting
cv::Mat weight;
GaussianBlur( eyeROI, weight, cv::Size( kWeightBlurSize, kWeightBlurSize ), 0, 0 );
for (int y = 0; y < weight.rows; ++y) {
unsigned char *row = weight.ptr
for (int x = 0; x < weight.cols; ++x) {
row[x] = (255 - row[x]);
}
}
//imshow(debugWindow,weight);
//-
- Run the algorithm!
cv::Mat outSum = cv::Mat::zeros(eyeROI.rows,eyeROI.cols,CV_64F);
// for each possible center
printf("Eye Size: %ix%i\n",outSum.cols,outSum.rows);
for (int y = 0; y < weight.rows; ++y) {
const unsigned char *Wr = weight.ptr
const double *Xr = gradientX.ptr
for (int x = 0; x < weight.cols; ++x) {
double gX = Xr[x], gY = Yr[x];
if (gX == 0.0 && gY == 0.0) {
continue;
}
testPossibleCentersFormula(x, y, Wr[x], gX, gY, outSum);
}
}
// scale all the values down, basically averaging them
double numGradients = (weight.rows*weight.cols);
cv::Mat out;
outSum.convertTo(out, CV_32F,1.0/numGradients);
//imshow(debugWindow,out);
//-- Find the maximum point
cv::Point maxP;
double maxVal;
cv::minMaxLoc(out, NULL,&maxVal,NULL,&maxP);
//-- Flood fill the edges
if(kEnablePostProcess) {
cv::Mat floodClone;
//double floodThresh = computeDynamicThreshold(out, 1.5);
double floodThresh = maxVal * kPostProcessThreshold;
cv::threshold(out, floodClone, floodThresh, 0.0f, cv::THRESH_TOZERO);
if(kPlotVectorField) {
//plotVecField(gradientX, gradientY, floodClone);
imwrite("eyeFrame.png",eyeROIUnscaled);
}
cv::Mat mask = floodKillEdges(floodClone);
//imshow(debugWindow + " Mask",mask);
//imshow(debugWindow,out);
// redo max
cv::minMaxLoc(out, NULL,&maxVal,NULL,&maxP,mask);
}
return unscalePoint(maxP,eye);
}
//分类器路径
const char* cascade_name = "C:/OpenCV2.4.3/data/haarcascades/haarcascade_frontalface_alt.xml";
void detect_and_draw( IplImage* img )
{
//face detection
static CvMemStorage* storage = 0;
// 初始化分类器
static CvHaarClassifierCascade* cascade = 0;
int scale = 1;
CvPoint pt1, pt2;
int i;
//调用检测算法
cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );
if( !cascade )
{
fprintf( stderr, "ERROR: Could not load classifier cascade\n" );
return;
}
storage = cvCreateMemStorage(0);
cvNamedWindow( "result", 1 );
cvClearMemStorage( storage );
if( cascade )
{
//检测人脸
CvSeq* faces = cvHaarDetectObjects( img,cascade,storage,1.1, 2, CV_HAAR_DO_CANNY_PRUNING,cvSize(150,150) );
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;
cvRectangle( img, pt1, pt2, CV_RGB(255,0,0), 3, 8, 0 );
int eye_region_width = r->width * (kEyePercentWidth/100.0);
int eye_region_height = r->width * (kEyePercentHeight/100.0);
int eye_region_top = r->height * (kEyePercentTop/100.0);
cv::Rect leftEyeRegion=cv::Rect(r->width*(kEyePercentSide/100.0),
eye_region_top,eye_region_width,eye_region_height);
cv::Rect rightEyeRe
gion=cv::Rect(r->width - eye_region_width - r->width*(kEyePercentSide/100.0),
eye_region_top,eye_region_width,eye_region_height);
cvSetImageROI(img,cvRect(r->x,r->y,r->width,r->height));
cvSaveImage("F:/faceROI.jpg",img);
cv::Mat faceROI=cv::imread("F:/faceROI.jpg");
cv::cvtColor(faceROI,faceROI,CV_RGB2GRAY);
cv::Point leftPupil=findEyeCenter(faceROI,leftEyeRegion);
cv::Point rightPupil=findEyeCenter(faceROI,rightEyeRegion);
leftPupil.x=leftPupil.x+r->width*(kEyePercentSide/100.0);
leftPupil.y=leftPupil.y+eye_region_top;
rightPupil.x=rightPupil.x+r->width - eye_region_width - r->width*(kEyePercentSide/100.0);
rightPupil.y=rightPupil.y+eye_region_top;
cvCircle(img,leftPupil,2,CV_RGB(255,0,0),1,8);
cvCircle(img,rightPupil,2,CV_RGB(255,0,0),1,8);
cvResetImageROI(img);
cout<
}
cvShowImage( "result", img );
}
int main( int argc, char** argv )
{
CvCapture* capture = 0;
capture = cvCreateCameraCapture(-1);
if(!capture){
return -1; }
while(1) {
IplImage* frame = cvQueryFrame( capture );
if( !frame ) break;
detect_and_draw(frame);
char c = cvWaitKey(1);
if( c == 107 ) break;//107=k
}
cvReleaseCapture( &capture );
cvDestroyWindow("result");
return 0;
}