尝试视频处理
上一次就是简单的跑了下边缘检测,在上次的代码中全部都是使用的OpenCV原生API,最后的数据也说明了OpenCL是有用的。
这一次尝试人脸识别,同样有是CPU和OpenCL测试。
但是这一次无法像上次那样打印出全部数据然后做总结,只能大体上根据FPS和硬件使用率来判断。
首先放上代码:
facedetect.cpp
#include
#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
using namespace std;
using namespace cv;
void detect_cpu(cv::Mat frame);
void detect_opencl(cv::Mat frame); // Transparent API
CascadeClassifier face_cascade;
CascadeClassifier eyes_cascade;
int main( int argc, const char** argv )
{
// read xml files
string face_cascade_file = haarcascade_frontalface_alt.xml的路径;
string eye_cascade_file = haarcascade_eye_tree_eyeglasses.xml的路径;
face_cascade.load(face_cascade_file);
if( !face_cascade.load(face_cascade_file) ){
cerr << "Error loading face cascade\n";
return -1;
}
eyes_cascade.load(eye_cascade_file);
if( !eyes_cascade.load(eye_cascade_file) ){
cerr << "Error loading eyes cascade\n";
return -1;
}
VideoCapture capture;
capture.open(0); // read frame from camera 0
if (!capture.isOpened()){
cerr << "Error opening video capture\n";
return -1;
}
while (1){
Mat frame;
capture >> frame;
detect_cpu(frame);
// detect_opencl(frame);
char key = (char)waitKey(1);
if(key == 27 || key == 'q' || key == 'Q')break;
}
return 0;
}
void detect_opencl(cv::Mat frame){
double start = (double)getTickCount();
UMat Uframe;
frame.copyTo(Uframe); // copy Mat frame -> UMat Uframe
UMat Uframe_gray;
cvtColor( Uframe, Uframe_gray, COLOR_BGR2GRAY );
equalizeHist( Uframe_gray, Uframe_gray );
//-- Detect faces
std::vector faces;
face_cascade.detectMultiScale( Uframe_gray, faces );
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( Uframe, center, Size( faces[i].width/2, faces[i].height/2 ), 0, 0, 360, Scalar( 255, 0, 255 ), 4 );
UMat faceROI = Uframe_gray( faces[i] );
//-- In each face, detect eyes
std::vector eyes;
eyes_cascade.detectMultiScale( faceROI, eyes );
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( Uframe, eye_center, radius, Scalar( 255, 0, 0 ), 4 );
}
}
double time_consume = ((double)getTickCount() - start) / getTickFrequency();
double fps = 1.0 / time_consume;
char str[50];
sprintf(str,"Resolution: %dx%d and FPS:%.2f",Uframe.cols,Uframe.rows,fps);
putText(Uframe,str,Point(0,30),FONT_HERSHEY_TRIPLEX,1,Scalar(0,0,255),2,8); //show resolution and fps
imshow( "Process with OpenCL", Uframe ); //show Uframe
}
void detect_cpu(cv::Mat frame){
double start = (double)getTickCount();
Mat frame_gray;
cvtColor( frame, frame_gray, COLOR_BGR2GRAY );
equalizeHist( frame_gray, frame_gray );
//-- Detect faces
std::vector faces;
face_cascade.detectMultiScale( frame_gray, faces );
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 );
Mat faceROI = frame_gray( faces[i] );
//-- In each face, detect eyes
std::vector eyes;
eyes_cascade.detectMultiScale( faceROI, eyes );
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 );
}
}
double time_consume = ((double)getTickCount() - start) / getTickFrequency();
double fps = 1.0 / time_consume;
char str[50];
sprintf(str,"Resolution: %dx%d and FPS:%.2f",frame.cols,frame.rows,fps); //show resolution and fps
putText(frame,str,Point(0,30),FONT_HERSHEY_TRIPLEX,1,Scalar(0,0,255),2,8);
imshow("Process with CPU",frame);
}
在xml
文件出填上正确的的文件绝对路径,参考编译命令如下
clang++ -std=c++11 facedetect.cpp -o facedetect `pkg-config --cflags --libs opencv4`
运行结果:
可以看到,帧数很低,基本上就像是放映PPT,而且CPU占用飚满。
要使用OpenCL运行,请注释掉40行的detect_cpu(frame)
函数,然后取消掉下一行的detect_opencl(frame)
,重新编译运行。
这里问题就来了:OpenCL的运行结果,和CPU是一样的。(就连硬件占用率也一样,参考上图)
这就不免让人感到很疑惑了,OpenCV官方说明是绝大多数的API是可以直接用UMat
的,那么我代码里面,与OpenCV相关的函数就应该都没有问题,于是抱着试试的心态,我做了些更改:
注释掉以下代码,仅做最基本的颜色空间转换和直方图均量化:
// std::vector faces;
// face_cascade.detectMultiScale( Uframe_gray, faces );
// 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( Uframe, center, Size( faces[i].width/2, faces[i].height/2 ), 0, 0, 360, Scalar( 255, 0, 255 ), 4 );
// UMat faceROI = Uframe_gray( faces[i] );
// //-- In each face, detect eyes
// std::vector eyes;
// eyes_cascade.detectMultiScale( faceROI, eyes );
// 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( Uframe, eye_center, radius, Scalar( 255, 0, 0 ), 4 );
// }
// }
然后再重新编译,查看运行结果:
这样一看,CPU使用率下来了,然后是有GPU使用率的,虽然使用率很低。最后就是FPS,有点虚飘(摄像头每秒能捕捉的帧数都没有这么高,但是至少明显的延迟是没有的)
原因分析
忽略那虚飘的FPS,考虑实际的问题:OpenCV中人脸检测使用的是 detectMultiScale
函数。它可以检测出图片中所有的人脸,并使用std::vector
保存各个人脸的坐标、大小,函数由分类器对象调用。
首先,我们要定义保存脸部和眼部的vector
,然后使用detectMultiScale
函数将识别到的脸部和眼部信息保存至我们定义的std::vector
和std::vector
中,vector
中有了点(Point
)的信息,那么我们便可以使用循环遍历后绘制椭圆(ellipse
)和圆形(circle
)。
1.问题就出在detectMultiScale
函数上,执行这个函数的时间是最长的。
2.具体到变量就在std::vector faces
上:OpenCV官网的说明是,当有不满足T-API使用需求的函数或变量,UMat
就会变成普通的Mat
;那么很显然,我们的vector
并没有放入OpenCL设备中去计算。
所以去掉了识别处理的这部分,会发现其实GPU有占用率(虽然很小)。
后续解决办法
挖坑嘛,固然是有的,至于这个坑能不能填起来,我就不确定了。
detectMultiScale
这个函数是不支持T-API的。故没有较好的解决办法。除非是了解算法原理后,重新写一个OpenCL版本的函数。