通过上一次的优化:见[2014.5.27]TQ210开发板人脸检测代码优化(1)_vimer-hz的博客-CSDN博客
注意每次paintEvent()的时间:
主要花费时间在Qimage图像转换为Iplimage图像的这个函数。这里注意一点,当时在Ubuntu上测试时主要的花费时间在getframe上,大约有20ms左右,而qimage—iplimage这个函数耗时只有8ms左右,跑到TQ210开发板后就不一样了,应该是和电路结构和浮点运算能力有关,TQ210取一帧的时间只有0.02ms,而qimage—iplimage这个函数消耗大约有65ms,所以这次优化的目的是避开这个转换函数。
Qimage图像之所以要转换为Iplimag图像是因为后面压缩图像用的函数如cvresize,cvrectangle等等需要的是Iplimage函数,优化思路是摄像头取来的帧是Qimage图像,就在QT直接显示出来,然后每5帧选1帧进行Qimage转换为Iplimage图像并进行人脸检测,还需要注意的是画人脸检测方框不能再用cvrecttangle函数,要用Qpainter直接在取回来的帧图像上画矩形框,再在QT上显示出来。
优化后的测试时间如下:
这样一来,平均每帧时间=(39 + 40 + 51 +42 + 143)/5 = 63ms,相当于每秒15.8帧,这看起来比较流畅了,但是矩形框就不是“即时”的,人脸移动不是高速移动的物体,勉强可以。
具体代码解释如下:
#include "widget.h"
#include "ui_widget.h"
#include <QtGui>
#ifndef PROPERTY
#define PROPERTY
#define image_width 320 //图片显示宽度
#define image_height 240 //图片显示高度
#define image_Format QImage::Format_RGB888 //图片显示格式
#define cameraDevice"/dev/video3" //摄像头设备
#define haarXML "./data/haarcascade_frontalface_alt2.xml" //人脸正脸模型级联分类器文件
#define haarXML_profile"./data/haarcascade_profileface.xml" //人脸侧脸模型级联分类器文件
#define imgSizeScaleSmall 0.5 //图像放缩比例
#define imgSizeScaleBig 3 //图像放缩比例
#endif //PROPERTY
long frame_count = 0; //统计第几帧
#define N 4
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
imgBuf = (unsigned char *)malloc(image_width * image_height* 3 *sizeof(char));
frame = new QImage(imgBuf,image_width,image_height,image_Format);
m_camera = new VideoDevice(tr(cameraDevice));
connect(m_camera, SIGNAL(display_error(QString)),this,SLOT(display_error(QString)));
camReturn = m_camera->open_device();
{
QMessageBox::warning(this,tr("error"),tr("open /dev/dsperror"),QMessageBox::Yes);
m_camera->close_device();
}
camReturn = m_camera->init_device();
if(-1==camReturn)
{
QMessageBox::warning(this,tr("error"),tr("initfailed"),QMessageBox::Yes);
m_camera->close_device();
}
camReturn = m_camera->start_capturing();
if(-1==camReturn)
{
QMessageBox::warning(this,tr("error"),tr("start capturefailed"),QMessageBox::Yes);
m_camera->close_device();
}
if(-1==camReturn)
{
QMessageBox::warning(this,tr("error"),tr("get framefailed"),QMessageBox::Yes);
m_camera->stop_capturing();
}
cascadeFile = haarXML;
cascade = (CvHaarClassifierCascade *) cvLoad(cascadeFile.toUtf8());
cascadeFile_profile = haarXML_profile;
cascade_profile = (CvHaarClassifierCascade *)cvLoad(cascadeFile_profile.toUtf8());
storage = cvCreateMemStorage(0);
}
Widget::~Widget()
{
delete ui;
}
void Widget::changeEvent(QEvent *e)
{
QWidget::changeEvent(e);
switch (e->type())
{
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
void Widget::paintEvent(QPaintEvent *)
{
frame_count++;
double t_0 = (double)cvGetTickCount();
uchar * pImgBuf;
unsigned int len;
camReturn = m_camera->get_frame((void **)&pImgBuf,&len);
double t_1 = (double)cvGetTickCount()-t_0; //t_get_frame
printf( "run t_get_frame = %gms\n",t_1/((double)cvGetTickFrequency()*1000.) );
convert_yuv_to_rgb_buffer(pImgBuf,imgBuf,image_width,image_height);
frame->loadFromData((uchar *)imgBuf,image_width * image_height * 3 *sizeof(char));
static CvScalar colors[] =
{
{{0,0,255}},
{{0,255,0}},
};
static CvSeq* face1=NULL; //记录正脸检测矩形序列
static CvSeq* face2=NULL; //记录侧脸检测矩形序列
static IplImage* dst1=NULL; //
double scale = 2.0;
if(frame_count % N == 1) //N帧只Qimage转换Iplimage1帧,人脸检测1帧
{
IplImage* src = QImageToIplImage(frame);
if (!src)
{
printf("imgerror!");
return;
}
double t_4 =(double)cvGetTickCount()-t_0; //t_qimage_iplimage
printf( "run t_qimage_iplimage =%gms\n", t_4/((double)cvGetTickFrequency()*1000.) );
//压缩图像
double sizeScale = imgSizeScaleSmall;
CvSize img_cvsize;
img_cvsize.width = src->width *sizeScale;
img_cvsize.height = src->height *sizeScale;
IplImage* dst = cvCreateImage(img_cvsize,src->depth, src->nChannels);
dst1 = dst;
cvResize(src,dst, CV_INTER_LINEAR);
//RGB转为灰度图
IplImage*gray = cvCreateImage( cvSize(dst->width,dst->height), 8, 1 );
IplImage* small_img = cvCreateImage(cvSize( cvRound (dst->width/scale),
cvRound(dst->height/scale)), 8, 1 );
cvCvtColor( dst, gray, CV_BGR2GRAY );
cvResize( gray, small_img,CV_INTER_LINEAR );
cvEqualizeHist( small_img, small_img );
cvClearMemStorage( storage );
double t = (double)cvGetTickCount();
CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage,
1.3,1, 0, cvSize(40,40) );
face1 = faces; //记录正脸检测序列
t = (double)cvGetTickCount() - t;
printf( "detection t_face_frontal = %gms\n",t/((double)cvGetTickFrequency()*1000.) ); //t_face_frontal
double t_profile =(double)cvGetTickCount();
CvSeq* faces_profile = cvHaarDetectObjects(small_img, cascade_profile, storage,
1.3,2, 0, cvSize(40,40) );
face2 = faces_profile; //记录侧脸检测序列
t_profile = (double)cvGetTickCount() - t_profile;
printf( "detection t_face_profile = %gms\n", t_profile/((double)cvGetTickFrequency()*1000.) ); //t_face_profile
cvReleaseImage(&src);
cvReleaseImage( &gray );
cvReleaseImage( &small_img );
}
/*
for( int i = 0; i < (face1 ? face1->total : 0); i++ )
{
CvRect* r = (CvRect*)cvGetSeqElem( face1, i );
CvPoint rectP1, rectP2;
rectP1.x = cvRound(r->x * (scale));
rectP1.y = cvRound(r->y * (scale));
rectP2.x = cvRound((r->x + r->width) * (scale));
rectP2.y = cvRound((r->y + r->height) * (scale));
cvRectangle(dst1, rectP1, rectP2, colors[0], 3, 8, 0);
}
for( int i = 0; i < (face2 ? face2->total : 0); i++ )
{
CvRect* r_pro = (CvRect*)cvGetSeqElem( face2, i );
CvPoint rectP1, rectP2;
rectP1.x = cvRound(r_pro->x * (scale));
rectP1.y = cvRound(r_pro->y * (scale));
rectP2.x = cvRound((r_pro->x + r_pro->width) * (scale));
rectP2.y = cvRound((r_pro->y + r_pro->height) * (scale));
cvRectangle(dst1, rectP1, rectP2, colors[1], 3, 8, 0);
}
*/
double t_5 = (double)cvGetTickCount()-t_0;
printf( "run t_detect_draw = %gms\n",t_5/((double)cvGetTickFrequency()*1000.) ); //t_detect_draw
long x1,x2;
QImage frame_dst =(*frame).scaled(image_width*1*1.5,image_height*1*1.5); //放大图像后显示Qiamge图像
//每帧图像不再用原来的cvRectangle花矩形,而采用QPainter直接画矩形
for( int i = 0; i < (face1 ? face1->total : 0); i++ )
{
CvRect* r = (CvRect*)cvGetSeqElem( face1, i );
CvPoint rectP1, rectP2;
rectP1.x = cvRound(r->x * (scale));
rectP1.y = cvRound(r->y * (scale));
rectP2.x = cvRound((r->x + r->width) * (scale));
rectP2.y = cvRound((r->y + r->height) * (scale));
x1 = rectP1.x;
x2 = rectP2.x;
printf( "x1_frontal = %ld \n", x1);
printf( "x2_frontal = %ld \n", x2);
QPainter paint(&frame_dst);
paint.setPen(QPen(Qt::red,14,Qt::SolidLine)); //QPainter红色矩形框
if(x1>=0 && x1<=20) //以下根据横坐标调整矩形框大小
paint.drawRect(rectP1.x*2.8,rectP1.y*2,rectP2.x*2.7,rectP2.y*2.7);
if(x1>20 && x1<=40)
paint.drawRect(rectP1.x*2.8,rectP1.y*2,rectP2.x*2.2,rectP2.y*2.7);
if(x1>40 && x1<=60)
paint.drawRect(rectP1.x*2.8,rectP1.y*2,rectP2.x*1.9,rectP2.y*2.7);
if(x1>60 && x1<=80)
paint.drawRect(rectP1.x*2.8,rectP1.y*2,rectP2.x*1.7,rectP2.y*2.7);
if(x1>80)
paint.drawRect(rectP1.x*2.8,rectP1.y*2,rectP2.x*1.5,rectP2.y*2.7);
paint.end();
}
for( int i = 0; i < (face2 ?face2->total : 0); i++ )
{
CvRect* r_pro = (CvRect*)cvGetSeqElem( face2, i );
CvPoint rectP1, rectP2;
rectP1.x = cvRound(r_pro->x * (scale));
rectP1.y = cvRound(r_pro->y * (scale));
rectP2.x = cvRound((r_pro->x + r_pro->width) * (scale));
rectP2.y = cvRound((r_pro->y + r_pro->height) * (scale));
x1 = rectP1.x;
printf( "x1_profile = %ld \n", x1);
QPainter paint(&frame_dst);
paint.setPen(QPen(Qt::green,14,Qt::SolidLine));
if(x1>=0 && x1<=20)
paint.drawRect(rectP1.x*2.8,rectP1.y*2,rectP2.x*2.6,rectP2.y*2.7);
if(x1>20 && x1<=40)
paint.drawRect(rectP1.x*2.8,rectP1.y*2,rectP2.x*2.2,rectP2.y*2.7);
if(x1>40 && x1<=60)
paint.drawRect(rectP1.x*2.8,rectP1.y*2,rectP2.x*2,rectP2.y*2.7);
if(x1>60 && x1<=80)
paint.drawRect(rectP1.x*2.9,rectP1.y*2,rectP2.x*1.8,rectP2.y*2.7);
//if(x1>80)
//paint.drawRect(rectP1.x*2.8,rectP1.y*2,rectP2.x*1.5,rectP2.y*2.7);
paint.end();
}
ui->m_imgLabel->setPixmap(QPixmap::fromImage(frame_dst)); //显示
camReturn = m_camera->unget_frame();
double t_7 = (double)cvGetTickCount()-t_0;
printf( "run t_all = %gms\n\n",t_7/((double)cvGetTickFrequency()*1000.) ); //t_all
}
void Widget::display_error(QString err)
{
QMessageBox::warning(this,tr("error"), err,QMessageBox::Yes);
}
intWidget::convert_yuv_to_rgb_buffer(unsigned char *yuv, unsigned char *rgb,unsigned int width, unsigned int height)
{
unsigned int in, out = 0;
unsigned int pixel_16;
unsigned char pixel_24[3];
unsigned int pixel32;
inty0, u, y1, v;
for(in = 0; in < width * height * 2; in +=4) {
pixel_16 =
yuv[in + 3] << 24 |
yuv[in + 2] << 16 |
yuv[in + 1] << 8 |
yuv[in + 0];
y0= (pixel_16 & 0x000000ff);
u = (pixel_16 & 0x0000ff00)>> 8;
y1= (pixel_16 & 0x00ff0000) >> 16;
v = (pixel_16 & 0xff000000)>> 24;
pixel32 = convert_yuv_to_rgb_pixel(y0, u, v);
pixel_24[0] = (pixel32 & 0x000000ff);
pixel_24[1] = (pixel32 & 0x0000ff00) >> 8;
pixel_24[2] = (pixel32 & 0x00ff0000) >> 16;
rgb[out++] = pixel_24[0];
rgb[out++] = pixel_24[1];
rgb[out++] = pixel_24[2];
pixel32 = convert_yuv_to_rgb_pixel(y1, u, v);
pixel_24[0] = (pixel32 & 0x000000ff);
pixel_24[1] = (pixel32 & 0x0000ff00) >> 8;
pixel_24[2] = (pixel32 & 0x00ff0000) >> 16;
rgb[out++] = pixel_24[0];
rgb[out++] = pixel_24[1];
rgb[out++] = pixel_24[2];
}
return 0;
}
int Widget::convert_yuv_to_rgb_pixel(int y,int u, int v)
{
unsigned int pixel32 = 0;
unsigned char *pixel = (unsigned char*)&pixel32;
intr, g, b;
r =y + (1.370705 * (v-128));
g =y - (0.698001 * (v-128)) - (0.337633 * (u-128));
b =y + (1.732446 * (u-128));
if(r> 255) r = 255;
if(g> 255) g = 255;
if(b> 255) b = 255;
if(r< 0) r = 0;
if(g< 0) g = 0;
if(b< 0) b = 0;
pixel[0] = r * 220 / 256;
pixel[1] = g * 220 / 256;
pixel[2] = b * 220 / 256;
return pixel32;
}
IplImage* Widget::QImageToIplImage(constQImage * qImage)
{
int width = qImage->width();
intheight = qImage->height();
CvSize Size;
Size.height = height;
Size.width = width;
IplImage *IplImageBuffer = cvCreateImage(Size, IPL_DEPTH_8U, 3);
for (int y = 0; y < (int)(height); y++)
{
for (int x = 0; x < (int)(width); x++)
{
QRgb rgb = qImage->pixel(x, y);
cvSet2D(IplImageBuffer, y, x,CV_RGB(qRed(rgb), qGreen(rgb), qBlue(rgb)));
}
}
return IplImageBuffer;
}