最近对VP的数据协议进行了反思,昨天进行了重大改进,去掉了智能指针的使用,在模块内部使用了缓存区概念,既方便了内存管理又极大地提高了效率,性能检测结果对比如下:
改进之前:
/*---------------------------------------------------*/ Providers Execute: 0 WebCam Provider Execute: 1 CvContourExtractor Execute: 6 Processors Execute: 145 VPImageConsumer Execute: 1 Consumers Execute: 2 Total: 147 /*---------------------------------------------------*/
改进之后:
/*---------------------------------------------------*/ Providers Execute: 0 CvContourExtractor Execute: 2 Processors Execute: 52 VPImageConsumer Execute: 1 VPImageConsumer Execute: 0 Consumers Execute: 1 Total: 53 /*---------------------------------------------------*/
上述所有的数字都以cpu时钟周期为单位。
运行结果还是不太满意,继续追踪各个模块的运行时间,结果大大出乎意料,从IplImage到QImage的转化函数居然这么耗时,性能检测结果如下:
/*---------------------------------------------------*/ Providers Execute: 0 Topo Sort : 1 IntProvider Exec: 0 FileLoader Exec: 0 VideoLoader Exec: 0 CvContourExtractor Execute: 1 CvContourExtractor Exec: 1 CvImage2QImage Exec: 22 ImageDisplayer Exec: 0 CvImage2QImage Exec: 23 ImageDisplayer Exec: 0 Processors Execute: 52 VPImageConsumer Execute: 0 VPImageConsumer Execute: 1 Consumers Execute: 1 Total: 53 /*---------------------------------------------------*/
注意其中的CvImage2QImage的执行时间。 跟踪那段函数代码,使用的两重循环执行图片的转化:
// 23 clock. void vpCvImageToQImage(const IplImage* pImage, QImage &qImage) { const IplImage* pIplImage = pImage; if(!pIplImage) return; if(qImage.isNull()) { int w = pIplImage->width; int h = pIplImage->height; qImage = QImage(w, h, QImage::Format_RGB32); } // 复制像素 int x, y; for(x = 0; x < pIplImage->width; ++x) { for(y = 0; y < pIplImage->height; ++y) { CvScalar color = cvGet2D(pIplImage, y, x); int r = color.val[2]; int g = color.val[1]; int b = color.val[0]; qImage.setPixel(x, y, qRgb(r,g,b)); } } } // vpCvImageToQImage
下面是改进的代码:
// 4 clock void vpCvImageToQImage(const IplImage* cvimage, QImage &qimage) { if (!cvimage) return; if ( qimage.isNull() ) qimage = QImage(cvimage->width, cvimage->height, QImage::Format_RGB32); int cvIndex = 0; int cvLineStart = 0; for (int y = 0; y < cvimage->height; y++) { unsigned char red,green,blue; cvIndex = cvLineStart; for (int x = 0; x < cvimage->width; x++) { // DO it red = cvimage->imageData[cvIndex+2]; green = cvimage->imageData[cvIndex+1]; blue = cvimage->imageData[cvIndex+0]; qimage.setPixel(x,y,qRgb(red, green, blue)); cvIndex += 3; } cvLineStart += cvimage->widthStep; } } // vpCvImageToQImage
改进后的性能检测结果如下:
/*---------------------------------------------------*/ Providers Execute: 1 Topo Sort : 0 IntProvider Exec: 0 FileLoader Exec: 0 VideoLoader Exec: 1 CvContourExtractor Execute: 1 CvContourExtractor Exec: 1 CvImage2QImage : 3 CvImage2QImage Exec: 4 ImageDisplayer Exec: 0 CvImage2QImage : 4 CvImage2QImage Exec: 5 ImageDisplayer Exec: 0 Processors Execute: 15 VPImageConsumer Execute: 0 VPImageConsumer Execute: 0 Consumers Execute: 0 Total: 16 /*---------------------------------------------------*/
原因分析:
第一种情况要差不多23个时钟周期,然后第二个却只需要4个时钟周期,效率差距这么大。原因在于opencv中图片存储是按行存取,所以行优先处理可以更加符合程序的局部性原理。
尾声
对于一个实时性强的系统每秒处理几十帧,从一帧处理147个时钟周期到16个时钟周期。。。
/*---------------------------------------------------*/ Providers Execute: 1 Topo Sort : 0 IntProvider Exec: 0 FileLoader Exec: 0 VideoLoader Exec: 1 CvContourExtractor Execute: 1 CvContourExtractor Exec: 1 CvImage2QImage : 3 CvImage2QImage Exec: 4 ImageDisplayer Exec: 0 CvImage2QImage : 4 CvImage2QImage Exec: 5 ImageDisplayer Exec: 0 Processors Execute: 15 VPImageConsumer Execute: 0 VPImageConsumer Execute: 0 Consumers Execute: 0 Total: 16 /*---------------------------------------------------*/
通过QImage和IplImage共享内存,需要注意的是,QT中的的图片是RGB格式,而OpenCV摄像头获得图片是BGR格式。具体实现代码如下:
void vpCvImageToQImage(const IplImage* frame, QImage &qimage) { if (!frame) return; if ( qimage.isNull() ) { // 使用24位 // QImage to draw on paint event qimage = QImage( QSize(frame->width, frame->height), QImage::Format_RGB888 ); // IplImage * to work with OpenCV functions _cvimage = cvCreateImageHeader( cvSize(frame->width, frame->height), 8, 3 ); // Share the buffer between QImage and IplImage * _cvimage->imageData = (char *)qimage.bits(); } cvCopy(frame, _cvimage, 0); // Convert it from BGR to RGB. QImage works with RGB and cvQueryFrame returns a BGR IplImage cvCvtColor(_cvimage, _cvimage, CV_BGR2RGB); } // vpCvImageToQImage
经测试,对于640*480的帧图片,转化为qimage只需要五个时钟周期左右。
add: 2011/1/14 by RYF