opencv3UMat介绍,以及测试比较opencl,cuda性能

在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极其类似。而且两者之间是可以相互转换的。

从UMat中获取Mat对象使用UMat的get方法UMat::getMat(int access_flags)支持的FLAG如下:

  • ACCESS_READ
  • ACCESS_WRITE
  • ACCESS_RW
  • ACCESS_MASK
  • ACCESS_FAST 
    最常用的就是读写,注意当使用这种方式的时候UMat对象将会被LOCK直到CPU使用获取Mat对象完成操作,销毁临时Mat对象之后,UMat才可以再被使用。
反过来也是一样,但是有的时候我用会出问题

所以我会直接使用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加速效果很明显,不知道原因在哪里






你可能感兴趣的:(opencv)