CvPoint2D32f getPupilCenter(Mat &eye_box){ //find x and y gradients Mat gradientX = computeGradient(eye_box); Mat gradientY = computeGradient(eye_box.t()).t(); //normalize and threshold the gradient Mat mags = matrixMagnitude(gradientX, gradientY); //create a blurred and inverted image for weighting Mat weight; bitwise_not(eye_box, weight); blur(weight, weight, Size(2,2)); //weight the magnitudes, convert to 8-bit for thresholding weight.convertTo(weight, CV_32F); mags = mags.mul(weight); normalize(mags, mags, 0, 1, NORM_MINMAX, CV_32F); mags.convertTo(mags, CV_8UC1, 255); //threshold using Otsu's method threshold(mags, mags, 0, 255, THRESH_BINARY | THRESH_OTSU); //convert to CV_32S and filter gradients mags.convertTo(mags, CV_32S); gradientY = gradientY.mul(mags); gradientX = gradientX.mul(mags); //resize arrays to same size resize(gradientX, gradientX, Size(EYE_FRAME_SIZE, EYE_FRAME_SIZE), 0, 0, INTER_NEAREST); resize(gradientY, gradientY, Size(EYE_FRAME_SIZE, EYE_FRAME_SIZE), 0, 0, INTER_NEAREST); resize(weight, weight, Size(EYE_FRAME_SIZE, EYE_FRAME_SIZE), 0, 0, INTER_NEAREST); //imshow("gradY", gradientY * 255); //imshow("weight", weight / 255); //run the algorithm: // for each possible gradient location // Note: these loops are reversed from the way the paper does them // it evaluates every possible center for each gradient location instead of // every possible gradient location for every center. Mat out = Mat::zeros(weight.rows,weight.cols, CV_32F); float max_val = 0; //for all pixels in the image for (int y = 0; y < EYE_FRAME_SIZE; ++y) { const int *grad_x = gradientX.ptr<int>(y), *grad_y = gradientY.ptr<int>(y); for (int x = 0; x < EYE_FRAME_SIZE; ++x) { int gX = grad_x[x], gY = grad_y[x]; if (gX == 0 && gY == 0) { continue; } //for all possible centers for (int cy = 0; cy < EYE_FRAME_SIZE; ++cy) { float *Or = out.ptr<float>(cy); const float *Wr = weight.ptr<float>(cy); for (int cx = 0; cx < EYE_FRAME_SIZE; ++cx) { //ignore center of box if (x == cx && y == cy) { continue; } //create a vector from the possible center to the gradient origin int dx = x - cx; int dy = y - cy; //compute dot product using lookup table float dotProduct; if(dx > 0 && dy > 0){ dotProduct = dpX[dx+EYE_FRAME_SIZE*dy]*gX + dpY[dx+EYE_FRAME_SIZE*dy]*gY; }else if(dx > 0){ dotProduct = dpX[dx-EYE_FRAME_SIZE*dy]*gX - dpY[dx-EYE_FRAME_SIZE*dy]*gY; }else if(dy > 0){ dotProduct = -dpX[-dx+EYE_FRAME_SIZE*dy]*gX - dpY[-dx+EYE_FRAME_SIZE*dy]*gY; }else{ dotProduct = -dpX[-dx-EYE_FRAME_SIZE*dy]*gX - dpY[-dx-EYE_FRAME_SIZE*dy]*gY; } //ignore negative dot products as they point away from eye if(dotProduct <= 0.0){ continue; } //square and multiply by the weight Or[cx] += dotProduct * dotProduct * Wr[cx]; //compare with max if(Or[cx] > max_val){ max_val = Or[cx]; } } } } } //resize for debugging resize(out, out, Size(500,500), 0, 0, INTER_NEAREST); out = 255 * out / max_val; //imshow("calc", out / 255); //histogram setup Mat hist; int histSize = 256; float range[] = { 0, 256 } ; const float* histRange = { range }; //calculate the histogram calcHist(&out,1, 0, Mat(), hist, 1, &histSize, &histRange, true, //uniform true //accumulate ); //get cutoff for top 10 pixels float top_end_sum = 0; int top_end = 0.92 * 255; for (int i = 255; i > 0; i--) { top_end_sum += hist.at<float>(i); if(top_end_sum > 3000){ top_end = i; break; } } //draw image for debugging Mat histImage(400, 512, CV_8UC3, Scalar(0,0,0)); int bin_w = cvRound( (double) 512/histSize ); normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1, Mat()); /// Draw for each channel for( int i = 1; i < histSize; i++) { line(histImage, Point(bin_w*(i), 400 - cvRound(hist.at<float>(i))), Point(bin_w*(i), 400), Scalar(i, i, i), 2, 8, 0); } //imshow("hist", histImage); //threshold to get just the pupil //printf("top_end: %d\n", top_end); threshold(out, out, top_end, 255, THRESH_TOZERO); //calc center of mass float sum = 0; float sum_x = 0; float sum_y = 0; for (int y = 0; y < out.rows; ++y) { float* row = out.ptr<float>(y); for (int x = 0; x < out.cols; ++x) { float val = row[x]*row[x]; if(val > 0){ sum += val; sum_x += val*x; sum_y += val*y; } } } Size eye_box_size = eye_box.size(); Size out_size = out.size(); //cout << "Size1: "+to_string(eye_box_size.width)+","+to_string(eye_box_size.height)+"\n"; //cout << "Size2: "+to_string(out_size.width)+","+to_string(out_size.height)+"\n"; float x_scale = (float) eye_box_size.width / out_size.width; float y_scale = (float) eye_box_size.height / out_size.height; CvPoint2D32f max = cvPoint2D32f(x_scale*sum_x/sum, y_scale*sum_y/sum); //circle(out, max, 3, 0); //imshow("thresh", out / 255); return max; }
cv::Point findEyeCenter(cv::Mat face, cv::Rect eye, std::string debugWindow) { 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<double>(y), *Yr = gradientY.ptr<double>(y); const double *Mr = mags.ptr<double>(y); 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<unsigned char>(y); 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<unsigned char>(y); const double *Xr = gradientX.ptr<double>(y), *Yr = gradientY.ptr<double>(y); 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); }
cv::Point findEyeCenter(cv::Mat face, cv::Rect eye, std::string debugWindow) { cv::Mat eyeROIUnscaled = face(eye); cv::Mat eyeROI; if(GBStatus) { cv::GaussianBlur( face, face, cv::Size(3,3), 0, 0, cv::BORDER_DEFAULT ); } if(debugL1 && GBStatus && (debugWindow == costado)) { std::string gaussianBlur_window = "GaussianBlur"; cv::namedWindow(gaussianBlur_window,CV_WINDOW_NORMAL); cv::moveWindow(gaussianBlur_window, gaussBlurx, gaussBlury); imshow(gaussianBlur_window,face); } scaleToFastSize(eyeROIUnscaled, eyeROI); // draw eye region rectangle(face,eye,1234); //-- Find the gradient cv::Mat gradientX; cv::Mat gradientY; if(sobel) { cv::Sobel( eyeROIUnscaled, gradientX, ddepth, 1, 0, 3, scale, delta, cv::BORDER_DEFAULT ); // cv::Sobel( (eyeROI.t()).t(), gradientY, ddepth, 0, 1, 3, scale, delta, cv::BORDER_DEFAULT ); cv::Sobel (eyeROIUnscaled, gradientY, ddepth, 0, 1, 3, scale, delta, cv::BORDER_DEFAULT ); } if(maxgradient) { gradientX = computeMatXGradient(eyeROI); gradientY = computeMatXGradient(eyeROI.t()).t(); } calculos("Gradient Y",gradientY); cv::Mat abs_grad_x, abs_grad_y; cv::convertScaleAbs( gradientX, abs_grad_x ); cv::convertScaleAbs( gradientY, abs_grad_y ); calculos("Valor absoluto Gradient Y",abs_grad_y); // cv::Mat gradientY = computeMatXGradient(eyeROI); //-- Normalize and threshold the gradient // compute all the magnitudes cv::Mat mags; if(debugL6) { mags = matrixMagnitude(gradientX, gradientY); } else { addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, mags ); } calculos("Sobel",mags); //compute the threshold double gradientThresh = computeDynamicThreshold(mags, kGradientThreshold); //double gradientThresh = kGradientThreshold; //double gradientThresh = 0; //normalize if(debugL1 && (debugWindow == costado)) { /////////// std::string eyeROI_window = debugWindow; /////////// cv::namedWindow(eyeROI_window,CV_WINDOW_NORMAL); /////////// cv::moveWindow(eyeROI_window, 640, 350); /////////// imshow(eyeROI_window,eyeROI); std::string Sobel_window = "Salida del Filtro"; cv::namedWindow(Sobel_window,CV_WINDOW_NORMAL); cv::moveWindow(Sobel_window, sobelx, sobely); imshow(Sobel_window,mags); } for (int y = 0; y < eyeROI.rows; ++y) { double *Xr = gradientX.ptr<double>(y), *Yr = gradientY.ptr<double>(y); const double *Mr = mags.ptr<double>(y); 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; } } } if(debugL4) { // 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<unsigned char>(y); for (int x = 0; x < weight.cols; ++x) { row[x] = (255 - row[x]); } } if(debugL4) { 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); if(debugL4) { imshow("outSum",outSum); } for (int y = 0; y < weight.rows; ++y) { const unsigned char *Wr = weight.ptr<unsigned char>(y); const double *Xr = gradientX.ptr<double>(y), *Yr = gradientY.ptr<double>(y); 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); if(debugL4) { imshow(debugWindow,outSum); } //-- 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); if(debugL4) { imshow(debugWindow + " Mask",mask); // imshow(debugWindow,out); //redo max; } cv::minMaxLoc(out, NULL,&maxVal,NULL,&maxP,mask); } cv::circle(eyeROI, maxP, 3,cv::Scalar(0,0,255)); if(debugL1 && (debugWindow == costado)) { calculos("EyeROI", eyeROI); std::string eyeROI_window = debugWindow; cv::namedWindow(eyeROI_window,CV_WINDOW_NORMAL); cv::moveWindow(eyeROI_window, 640, 350); imshow(eyeROI_window,eyeROI); int c = cv::waitKey(10); if( (char)c == 'g' ) { imwrite("ojo.png",eyeROIUnscaled); } } return unscalePoint(maxP,eye); }