在opencv中,已经嵌入了opencl运行的方式,通过使用UMat对象,opencv会自动在支持OpenCL的设备上使用GPU运算,在不支持OpenCL的设备仍然使用CPU运算,这样就避免了程序运行失败,而且统一了接口。
一般正常基于CPU的读写视频一帧图像代码如下:
cv::Mat inMat, outMat;
vidInput >> inMat;
cv::cvtColor(inMat, outMat, cv::COLOR_RGB2GRAY);
vidOutput << outMat;
基于OpenCL的GPU方式读写视频一帧图像代码如下:
cv::Mat inMat, outMat;
vidInput >> inMat;
cv::ocl::oclMat inOclMat(inMat);
cv::ocl::oclMat outOclMat;
cv::ocl::cvtColor(inOclMat, outOclMat, cv::COLOR_RGB2GRAY);
outMat = outOclMat;
vidOutput << outMat;
上述代码通过添加ocl前缀空间实现OpenCL支持设备的GPU运算能力提高。但是上述代码在不支持OpenCL的平台上还会运行失败,使用起来及其不方便。对开发者来说不是统一API和底层透明。
而如果在OpenCV3中使用UMat,则如下:
cv::UMat inMat, outMat;
vidInput >> inMat;
cv::cvtColor(inMat, outMat, cv::COLOR_RGB2GRAY);
vidOutput << outMat;
这样就无需像OpenCV2中那样通过显式声明的调用方式。很明显UMat与Mat极其类似。而且两者之间是可以相互转换的。
所以我会直接使用cv::CopyTo(),可能这样做会减少错误,但不知道会有什么影响
cv::UMat aMat;
cv::Mat bMat;
bMat.CopyTo(aMat);
aMat.CopyTo(bMat);
UMat不能提取其中某个特定元素的值,毕竟已经存到gpu中了,提取方法是先转换成Mat,再提取,下面提供一种提取方法
UMat test_umat;
test_umat.getMat(ACCESS_READ).at(row, column);
注意,不要过多地进行Mat和UMat之间地转化,因为内存显存之间传递信息耗时很大,这是像我这种初学者,很容易犯的错误。
之后我打算用一段程序测试OpenCL和CUDA的性能,程序如下:
#include
#include
#include
#include
#include
#include
int main(int argc, char**argv) {
std::cout << "OpenCV version=" << std::hex << CV_VERSION << std::dec << std::endl;
cv::Mat frame;
cv::UMat uframe, uFrameGray;
cv::cuda::GpuMat image_gpu, image_gpu_gray;
cv::VideoCapture capture("path_to_the_video");
bool useOpenCL = (argc == 2);
std::cout << "Use OpenCL=" << useOpenCL << std::endl;
cv::ocl::setUseOpenCL(useOpenCL);
bool useCuda = (argc == 3);
std::cout << "Use CUDA=" << useCuda << std::endl;
cv::Ptr cascade = cv::makePtr("data/lbpcascades/lbpcascade_frontalface_at.xml");
cv::Ptr cascade_gpu = cv::cuda::CascadeClassifier::create("data/lbpcascades/lbpcascade_frontalface_at.xml");
double time = 0.0;
int nb = 0;
if(capture.isOpened()) {
for(;;) {
capture >> frame;
if(frame.empty() || nb >= 1000) {
break;
}
std::vector faces;
double t = 0.0;
if(!useCuda) {
t = (double) cv::getTickCount();
frame.copyTo(uframe);
cv::cvtColor(uframe, uFrameGray, CV_BGR2GRAY);
cascade->detectMultiScale(uFrameGray, faces);
t = ((double) cv::getTickCount() - t) / cv::getTickFrequency();
} else {
t = (double) cv::getTickCount();
image_gpu.upload(frame);
cv::cuda::cvtColor(image_gpu, image_gpu_gray, CV_BGR2GRAY);
cv::cuda::GpuMat objbuf;
cascade_gpu->detectMultiScale(image_gpu_gray, objbuf);
cascade_gpu->convert(objbuf, faces);
t = ((double) cv::getTickCount() - t) / cv::getTickFrequency();
}
time += t;
nb++;
for(std::vector::const_iterator it = faces.begin(); it != faces.end(); ++it) {
cv::rectangle(frame, *it, cv::Scalar(0,0,255));
}
std::stringstream ss;
ss << "FPS=" << (nb / time);
cv::putText(frame, ss.str(), cv::Point(30, 30), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0,0,255));
cv::imshow("Frame", frame);
char c = cv::waitKey(30);
if(c == 27) {
break;
}
}
}
std::cout << "Mean time=" << (time / nb) << " s" << " ; Mean FPS=" << (nb / time) << " ; nb=" << nb << std::endl;
system("pause");
return 0;
}
lbpcascade_frontalface_at.xml
文件可以百度搜索就能下载到,是一个级联分类器训练好的模型,用于识别人脸正面,很常用。具体资料可以参考http://blog.csdn.net/yang_xian521/article/details/6973667
只需要将
cv::VideoCapture capture("path_to_the_video");
这里填入你想检测的视频的地址,时长最好在十几秒,情况视你的处理器情况所定。
再将
cv::Ptr cascade = cv::makePtr("data/lbpcascades/lbpcascade_frontalface_at.xml");
cv::Ptr cascade_gpu = cv::cuda::CascadeClassifier::create("data/lbpcascades/lbpcascade_frontalface_at.xml");
两处填入下载好的xml文件的地址即可运行
运行方式为
g++ -o 文件名 文件名.cpp `pkg-config opencv --cflags --libs`
此处要链接上opencv的库,注意不要少了`这个符号,它在键盘数字1旁边。
如果额外输入一个参数,程序将利用opencl进行加速,如果额外输入两个,程序将利用cuda进行加速,没有额外参数,将正常运行
我的结果很意外,opencl基本没有加速效果,而cuda加速效果很明显,不知道原因在哪里