I'm writing a program using C++ and OpenCV. It's actually my first so what I'm asking is probably something very basic I've overlooked. Much of it is copied - not copy+pasted mind you, but copied by hand, going line by line, understanding what each line was doing as I wrote it - from some of OpenCV's tutorials. I'll paste the code below.
The problem I'm encountering is that as soon as the webcam starts trying to implement facial recognition, everything just SLOWS. DOWN. As I understand it, its because the .exe is trying to read from two MASSIVE .xml files every frame update, but I don't have any idea how to fix it. It was worse before I constrained the height, width, and framerate of the video.
If anyone has any ideas at this point, I'd love to hear them. I'm very new to software programming - until now I've mostly done web development, so I'm not used to worrying about system memory and other factors.
Thanks in advance!
EDIT: Here are my system specs: Mac, OSX 10.9.4, 2.5 GHz Intel Core i5, 4 GB 1600 MHz DDR3 RAM.
#include "opencv2/objdetect.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;
/** Function Headers */
void detectAndDisplay( Mat frame );
/** Global variables */
String face_cascade_name = "haarcascade_frontalface_alt.xml";
String eyes_cascade_name = "haarcascade_eye_tree_eyeglasses.xml";
CascadeClassifier face_cascade;
CascadeClassifier eyes_cascade;
String window_name = "Capture - Face detection";
/** #function main */
int main( void )
{
cv::VideoCapture capture;
Mat frame;
//-- 1. Load the cascades
if( !face_cascade.load( face_cascade_name ) ){ printf("--(!)Error loading face cascade\n"); return -1; };
if( !eyes_cascade.load( eyes_cascade_name ) ){ printf("--(!)Error loading eyes cascade\n"); return -1; };
//-- 2. Read the video stream
capture.open( -1 );
if ( ! capture.isOpened() ) { printf("--(!)Error opening video capture\n"); return -1; }
capture.set(CV_CAP_PROP_FRAME_WIDTH,640);
capture.set(CV_CAP_PROP_FRAME_HEIGHT,480);
capture.set(CV_CAP_PROP_FPS, 15);
while ( capture.read(frame) )
{
if( frame.empty() )
{
printf(" --(!) No captured frame -- Break!");
break;
}
//-- 3. Apply the classifier to the frame
detectAndDisplay( frame );
int c = waitKey(10);
if( (char)c == 27 ) { break; } // escape
}
return 0;
}
/** #function detectAndDisplay */
void detectAndDisplay( Mat frame )
{
std::vector<Rect> faces;
Mat frame_gray;
cvtColor( frame, frame_gray, COLOR_BGR2GRAY );
equalizeHist( frame_gray, frame_gray );
//-- Detect faces
face_cascade.detectMultiScale( frame_gray, faces, 1.1, 2, 0|CASCADE_SCALE_IMAGE, Size(30, 30) );
for ( size_t i = 0; i < faces.size(); i++ )
{
Point center( faces[i].x + faces[i].width/2, faces[i].y + faces[i].height/2 );
ellipse( frame, center, Size( faces[i].width/2, faces[i].height/2 ), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );
Mat faceROI = frame_gray( faces[i] );
std::vector<Rect> eyes;
//-- In each face, detect eyes
eyes_cascade.detectMultiScale( faceROI, eyes, 1.1, 2, 0 |CASCADE_SCALE_IMAGE, Size(30, 30) );
for ( size_t j = 0; j < eyes.size(); j++ )
{
Point eye_center( faces[i].x + eyes[j].x + eyes[j].width/2, faces[i].y + eyes[j].y + eyes[j].height/2 );
int radius = cvRound( (eyes[j].width + eyes[j].height)*0.25 );
circle( frame, eye_center, radius, Scalar( 255, 0, 0 ), 4, 8, 0 );
}
}
//-- Show what you got
imshow( window_name, frame );
}
A quick solution would be to replace:
eyes_cascade.detectMultiScale( faceROI, eyes, 1.1, 2, 0 |CASCADE_SCALE_IMAGE, Size(30, 30) );
by
eyes_cascade.detectMultiScale( faceROI, eyes, 1.3, 2, 0 |CASCADE_SCALE_IMAGE, Size(60, 60), Size(350, 350) );
1.3 is the scale factor, Size(60, 60) the min windows size and Size(350, 350) the max one. It means basically that it will start to search for 60*60 faces then increase size by oldWindowSize*1.3 until it reach 350*350. It is assumed there that your faces are min 60*60 and max 350 * 350.
You can tune it even more depending what you want. The minSize will have a the most impact on performance as well as scale (but 1.3 is already high). The maxSize will have less impact.
After this update, your prog should be twice faster or decrease CPU usage by half. However, I am still surprise that with your current tunings and you computer you have performances problems...
Give us a feedback if it works.
Related
I want to show up a cross in the middle of my screen and change the size of it pressing some keys on my keyboard.
For example ,
if I press b , the cross should become big.
if I press s, the cross should become small.
if I press m, the cross should become medium.
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
VideoCapture cap(0);
while(true)
{
Mat frame;
// Capture frame-by-frame
cap >> frame;
// If the frame is empty, break immediately
if (frame.empty())
break;
// for converting the frame to grayscale
Mat gray;
cvtColor( frame, gray , COLOR_BGR2GRAY);
line( frame, Point( 300, 240 ), Point( 340,240), Scalar( 0, 0, 255 ), 2, 4 );
line( frame, Point( 320, 260), Point( 320, 220), Scalar( 0, 0, 255 ), 2, 4);
imshow(" figure ",frame);
char c=(char)waitKey(25);
if(c==27)
break;
}
cap.release();
destroyAllWindows();
return 0;
}
please help me with this
My proposition is to introduce 'scale' variable that will be modified by key presses to calculate start and end points for both lines. Just assume those points are defined as [start point] = [middle point] - [scale] * [scale factor] and [end point] = [middle point] + [scale] * [scale factor]. So it would look like:
VideoCapture cap(0);
int size = 2;
bool drawCross = 1;
while(true)
{
Mat frame;
// Capture frame-by-frame
cap >> frame;
// If the frame is empty, break immediately
if (frame.empty())
break;
// for converting the frame to grayscale
Mat gray;
cvtColor( frame, gray , COLOR_BGR2GRAY);
if (drawCross) {
line( frame, Point( 320 - 10*size, 240 ), Point( 320 + 10*size,240), Scalar( 0, 0, 255 ), 2, 4 );
line( frame, Point( 320, 240 - 10*size), Point( 320, 240 + 10*size), Scalar( 0, 0, 255 ), 2, 4);
}
imshow(" figure ",frame);
char c=(char)waitKey(25);
if(c==27)
break;
else if (c==[whatever this "small" key is])
size = 1;
else if (c==[whatever this "medium" key is])
size = 2;
else if (c==[whatever this "large" key is])
size = 4;
else if (c==[whatever this "do not draw cross" key is])
drawCross = !drawCross;
}
==EDIT==
Solution is now fixed to be working with new 'requirements' given in comments below. This is my last input on this question, as SO is not 'Can you write that for me?' type of community. Your problems described in comments required me up to two minutes of googl-ing. And you need to read up on programming basics before you go on, like conditional branching and stuff. And I do not know if this code works 100% as I don't want to install C++ compiler at this moment.
I have to perform eye detection on each of the faces in the famous Oscar selfie image.I tried using Haar Casacades on the faces since most of them are near-frontal, but the eye detection is totally random and no eyes are being recognized at all.
I have have tried the same haar cascade xml file for eye detection on images with single faces and it worked fine.
What steps could I take to correctly detect the eyes?
The image I used for eye detection can be downloaded from here:
https://drive.google.com/file/d/0B3jt6sHgpxO-d1plUjg5eU5udW8/view?usp=sharing
Below is the code I have written for face and eye detection. Basic idea is I first detect the face using viola jones algorithm and within each face, I try to detect the eyes.
#include <opencv2/highgui/highgui.hpp>
#include <cv.h>
#include <opencv2/objdetect/objdetect.hpp>
#include <vector>
using namespace cv;
using namespace std;
int x,y,w,h;
int main(int argc, const char** argv)
{
Mat image = imread("oscarSelfie.jpg",CV_LOAD_IMAGE_UNCHANGED);
Mat gray_img;
cvtColor(image, gray_img, CV_BGR2GRAY);
string faceCascade_file = "haarcascade_frontalface_alt2.xml";
string eyeCascade_file = "haarcascade_eye.xml";
CascadeClassifier faceCascade;
CascadeClassifier eyeCascade;
//Cascade classifier is a class which has a method to load the classifier from file
if( !faceCascade.load( faceCascade_file ) )
{ cout<<"--(!)Error loading\n"; return -1; };
//If it returns zero, it means an error has occured in loading the classifier
if( !eyeCascade.load( eyeCascade_file ) )
{ cout<<"--(!)Error loading\n"; return -1; };
equalizeHist(gray_img, gray_img);
//Increases contrast and make image more distingushable
/***** Detecting Faces in Image *******/
vector<Rect> faces;
vector<Rect> eyes;
//Rect is a class handling the rectangle datatypes
faceCascade.detectMultiScale(gray_img, faces, 1.1, 1, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30) );
//faces.size()-it will return number of faces detected
for( int i = 0; i < faces.size(); i++ )
{
x = faces[i].x;
y = faces[i].y;
w = faces[i].width;
h = faces[i].height;
//Point center( faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5 );
//ellipse( image, center, Size( faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );
rectangle(image, cvPoint(x,y), cvPoint(x+w,y+h), CV_RGB(0,255,0), 2, 8 );
/******** Detecting eyes ***********/
eyeCascade.detectMultiScale(gray_img, eyes, 1.1, 50, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30) );
for(int j=0; j < eyes.size(); j++)
{
Point center( faces[i].x + eyes[j].x + eyes[j].width*0.5, faces[i].y + eyes[j].y + eyes[j].height*0.5 );
int radius = cvRound( (eyes[j].width + eyes[j].height)*0.25 );
circle( image, center, radius, Scalar( 255, 0, 0 ), 4, 8, 0 );
}
}
namedWindow("oscarSelfie :)", CV_WINDOW_AUTOSIZE);
imshow("oscarSelfie :)", image);
waitKey(0);
destroyWindow("pic");
return 0;
}
`
i get the following result with facedetect.cpp (uses haarcascade_eye_tree_eyeglasses.xml)
don't expect to find all faces and eyes
i also tried dlib's face_landmark_detection_ex.cpp to compare results
dlib has an extra feature that gives you aligned faces like seen below
You may want to use CLM-framework for face landmark detection. As far as I have experience CLM-framework performance satisfactory.
Some examples of the system in action: http://youtu.be/V7rV0uy7heQ
I am currently doing real-time face evaluation and is trying to set the FPS of the Camera of my computer to 1 frame per second, followed by calling the cascade functions only once per second. (Currently using a While(true) loop) This is due to the limitation of my GPU.
I have tried to set the FPS of the camera by using
VideoCapture cap(0);
cap.set(CV_CAP_PROP_FPS, 1);
namedWindow("webcam",CV_WINDOW_AUTOSIZE);
but it is not working. The camera still process at a relative high FPS.
For the cascade function calling, I am doing it as below:
while ( true ){
cap >> frame;
vector<Rect> faces;
face_cascade.detectMultiScale( frame, faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30) );
// Draw circles on the detected faces
for( int i = 0; i < faces.size(); i++ )
{
Point center( faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5 );
cout<<"Face location: "<<faces[i].x<<","<<faces[i].x + faces[i].width<<","<<faces[i].y<<","<<faces[i].y + faces[i].height;
ellipse( frame, center, Size( faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );
}
waitKey(30);
if ( !frame.data ){
cerr << "Cannot acquire frame from the webcam " << endl;
break;
}
imshow("webcam", frame);
}
I need the camera to go for only 1 frame per second, followed by calling the cascade functions once per second.
Edit: I have tried to display the FPS of the camera by using
int FPS = cap.get(CV_CAP_PROP_FPS);
It did show that FPS is currently at 1, but it seems that the camera is still moving at a relative high frame rate.
Setting the frame rate does not always work. Sometimes the camera simply does not respond to this change. However, you can do something to solve your problem in a tricky way. Measure the time that it takes to processing a frame then subtract it from 1000 mSec (1000 - Elapsce_Time) and make it wait for this time cv::waitKey(1000-Elapsce_Time). Finally, this is not a very way to do it. You should search for the actual problem with the camera and try to solve it.
I am attempting to do a small project with C++ and OpenCV (I am fairly new to each). I studied the face detection code provided by OpenCV here to get a basic understanding. I was able to compile and run this face detection code with no issues. I then attempted to modify that code to perform full body detection as follows:
#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;
/** Function Headers */
void detectAndDisplay( Mat frame );
/** Global variables */
String body_cascade_name = "haarcascade_fullbody.xml";
CascadeClassifier body_cascade;
String window_name = "Capture - Body detection";
/** #function main */
int main( void )
{
VideoCapture capture;
Mat frame;
//-- 1. Load the cascades
if( !body_cascade.load( body_cascade_name ) ){ printf("--(!)Error loading body cascade\n"); return -1; };
//-- 2. Read the video stream
capture.open( -1 );
if ( ! capture.isOpened() ) { printf("--(!)Error opening video capture\n"); return -1; }
while ( capture.read(frame) )
{
if( frame.empty() )
{
printf(" --(!) No captured frame -- Break!");
break;
}
//-- 3. Apply the classifier to the frame
detectAndDisplay( frame );
int c = waitKey(10);
if( (char)c == 27 ) { break; } // escape
}
return 0;
}
/** #function detectAndDisplay */
void detectAndDisplay( Mat frame )
{
std::vector<Rect> bodies;
Mat frame_gray;
cvtColor( frame, frame_gray, COLOR_BGR2GRAY );
equalizeHist( frame_gray, frame_gray );
//-- Detect bodies
body_cascade.detectMultiScale( frame_gray, bodies, 1.1, 2, 0|CASCADE_SCALE_IMAGE, Size(45, 80) );
//body_cascade.detectMultiScale( frame_gray, bodies, 1.1, 3, 3, cv::Size(5,13), cv::Size(45,80));
for( size_t i = 0; i < bodies.size(); i++ )
{
Point center( bodies[i].x + bodies[i].width/2, bodies[i].y + bodies[i].height/2 );
rectangle( frame, bodies[i], Scalar( 255, 0, 255), 4, 8, 0);
//ellipse ( frame, center, Size( bodies[i].width/2, bodies[i].height/2), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );
}
//-- Show what you got
imshow( window_name, frame );
}
Mostly, I just renamed variables from "face" to "body", removed the eye cascade portions and changed the "haarcascade_xxxx".
I am able to compile this using:
g++ bodyDetect.cpp -o bodyDetect `pkg-config --cflags --libs opencv`
but when I attempt to run it I just get a "Segmentation Fault: 11"
I have been able to determine that it is due to the "haarcascade_fullbody.xml" assigned to body_cascade_name. If I change this value back to the "haarcascade_frontalface_alt.xml" it will run just fine as face detection software again. I do have the xml files copied to the same directory as the .cpp files.
Also, the upper body cascade will not work either, but the profile face cascade will work with the same code (haven't tested all cascades).
I have OpenCV 3.0.0 installed (checked using pkg-config --modversion opencv), running MacOSX 10.9.2, if this is relevant. I did have trouble compiling/running slightly different face detection code that appeared to be for OpenCV 2.4.?.
My question is, why would the "haarcascade_fullbody.xml" file cause a segmentation fault, while other cascades do not? Is there a way to correct this issue?
UPDATE: When I run the program in gdb I get the following error:
Program received signal SIGSEGV, Segmentation fault.
0x0000000100d0b707 in ?? ()
I believe that this indicates that the seg fault is not within one of the functions. I still suspect that the haarcascade_fullbody.xml to be the issue. When this is to a different filter I do not receive the seg fault.
I have this really strange issue and I think I might be doing something wrong, but I have an opencv1 implementation for Pyramidal Lucas Kanade and an opencv2 implementation. The difference is that the opencv2 takes MUCH longer to run (in particular the goodFeaturesToTrack function) vs. the opencv1. In addition, including the opencv2 libs and headers in the opencv1 implmentation results in that one becoming extremely slow as well (we're talking about 0.002 s per two images vs. 1 second per two images). Am I doing something wrong?
Windows 7, 64 bit. Here is the opencv2 code that runs really slow, at about 1 frame per second. As I said, taking the opencv1 implementation and switching library version causes the same slow down by a factor of 10 or more. I think this is very weird and google came up with no information! THANKS!!!
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
#include <cmath>
using namespace cv;
using namespace std;
int64 now, then;
double elapsed_seconds, tickspersecond=cvGetTickFrequency() * 1.0e6;
int main(int argc, char** argv)
{
// Load two images and allocate other structures
Mat imgA = imread("0000.png", CV_LOAD_IMAGE_GRAYSCALE);
Mat imgB = imread("0001.png", CV_LOAD_IMAGE_GRAYSCALE);
Size img_sz = imgA.size();
Mat imgC(img_sz,1);
int win_size = 15;
int maxCorners = 100;
double qualityLevel = 0.05;
double minDistance = 2.0;
int blockSize = 3;
double k = 0.04;
std::vector<cv::Point2f> cornersA;
cornersA.reserve(maxCorners);
std::vector<cv::Point2f> cornersB;
cornersB.reserve(maxCorners);
then = cvGetTickCount();
goodFeaturesToTrack( imgA,cornersA,maxCorners,qualityLevel,minDistance,cv::Mat(),blockSize,true);
goodFeaturesToTrack( imgB,cornersB,maxCorners,qualityLevel,minDistance,cv::Mat(),blockSize,true);
now = cvGetTickCount();
cout << (double)(now - then) / tickspersecond;
cornerSubPix( imgA, cornersA, Size( win_size, win_size ), Size( -1, -1 ),
TermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03 ) );
cornerSubPix( imgB, cornersB, Size( win_size, win_size ), Size( -1, -1 ),
TermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03 ) );
// Call Lucas Kanade algorithm
CvSize pyr_sz = Size( img_sz.width+8, img_sz.height/3 );
std::vector<uchar> features_found;
features_found.reserve(maxCorners);
std::vector<float> feature_errors;
feature_errors.reserve(maxCorners);
calcOpticalFlowPyrLK( imgA, imgB, cornersA, cornersB, features_found, feature_errors ,
Size( win_size, win_size ), 5,
cvTermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.3 ), 0 );
// Make an image of the results
for( int i=0; i < features_found.size(); i++ ){
// cout<<"Error is "<<feature_errors[i]<<endl;
//continue;
//cout<<"Got it"<<endl;
Point p0( ceil( cornersA[i].x ), ceil( cornersA[i].y ) );
Point p1( ceil( cornersB[i].x ), ceil( cornersB[i].y ) );
line( imgC, p0, p1, CV_RGB(255,255,255), 2 );
}
namedWindow( "ImageA", 0 );
namedWindow( "ImageB", 0 );
namedWindow( "LKpyr_OpticalFlow", 0 );
imshow( "ImageA", imgA );
imshow( "ImageB", imgB );
imshow( "LKpyr_OpticalFlow", imgC );
cvWaitKey(0);
return 0;
}
You're probably using the debug libraries (*d.lib) instead of the release ones. I had this same problem with ~1-2s per call for goodFeaturesToTrack() and switching to release solved it.
Why are you calling goodFeaturesToTrack twice ?
Call it once to get cornersA and then use LK to identify the same corners / features in imgB.