简介:

目的是以使用OpenCV捕获视频,并将其用作OpenCL程序的输入。两者的传输都必须尽可能地高效高效(如果不必担心,为什么要使用OpenCL,对吗?)。
我读到OpenCV在内部使用OpenCL(UMat),并且可以通过访问GPU来访问UMat::handle缓冲区。但是,到目前为止,我对此的尝试还没有成功。

目的是将UMat缓冲区重用作为OpenCL kernels的输入,并最终将结果生成为图像,返回到另一个UMat进行显示。

OpenCV框架仅用于为程序生成输入,因此,我对使用OpenCV CL包装器(cv::ocl)不感兴趣,而对使用常规OpenCL(cl::...)感兴趣。这样可以避免在完整软件中包含/链接OpenCV框架。

问题:

如何通过OpenCL访问OpenCV UMat缓冲区?

  • 使用UMat缓冲区作为OpenCL缓冲区(第一个选项)
  • 将UMat缓冲区移至GPU内的OpenCL缓冲区。 (第二个选项)


  • 我已经取得的成就:
  • OpenCL可完美独立运行
  • OpenCV可完美独立工作
  • 将UMat::handle转换为cl::Buffer编译
  • 给定的缓冲区似乎无效。


  • #include <iostream>
    #include <vector>
    #include <cassert>
    
    #define __CL_ENABLE_EXCEPTIONS // enable exceptions instead of error-codes
    #define CL_TARGET_OPENCL_VERSION 120
    #include <CL/cl.hpp>
    #include <opencv2/opencv.hpp>
    #include <opencv2/core/ocl.hpp>
    
    using namespace cv;
    using namespace std;
    
    int main()
    {
        // OPENCL STUFF
        // Very simplified/basic/stupid/naive OpenCL context creation
        std::vector<cl::Platform> platforms;
        cl::Platform::get(&platforms);
        assert(platforms.size()>0);
        std::vector<cl::Device> devices;
        platforms[0].getDevices( CL_DEVICE_TYPE_ALL, &devices);
        assert(devices.size()>0);
        cl_context_properties prop[3] =
        {
            CL_CONTEXT_PLATFORM,
            (cl_context_properties)(platforms[0])(),
            0
        };
        cl::Context context( devices[0], prop, nullptr, nullptr);
    
        std::string kernelStr = R"DELIMITER(
        kernel void replaceRB( global uchar3* content)
        {
            const size_t globalId = get_global_id(0);
    
            private uchar3 byte = content[globalId];
            char aux = byte.z;
            byte.z = byte.x;
            byte.x = aux;
            content[globalId] = byte;
        }
        )DELIMITER";
    
        cl::Program::Sources sources;
        sources.push_back(std::make_pair<const char*, size_t>(kernelStr.data(), kernelStr.size()));
        cl::Program program(context, sources);
    
        try
        {
            program.build({devices[0]}, "");
        }
        catch (...)
        {
            std::cout << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(devices[0]) << std::endl;
        }
    
        std::vector<cl::Kernel> kernels;
        program.createKernels(&kernels);
        assert(kernels.size()>0);
    
        cl::CommandQueue queue(context, devices[0]);
    
        // OPENCV STUFF
        ocl::setUseOpenCL(true);
        cv::ocl::attachContext(platforms[0].getInfo<CL_PLATFORM_NAME>(), platforms[0](), context(), devices[0]());
        assert(ocl::haveOpenCL());
    
        cout << cv::ocl::Context::getDefault().ndevices() << " GPU devices are detected." << endl;
    
        VideoCapture cap(0); //Camera
        //VideoCapture cap("SampleVideo_1280x720_1mb.mp4"); //Video example
        assert(cap.isOpened());
    
        UMat frame;
        assert(cap.read(frame));
    
        //MIX OF BOTH opencl and opencv
        //cl::Buffer buf(context,CL_MEM_READ_WRITE, 256); // This works
        cl::Buffer buf(*((cl_mem*)frame.handle(CL_MEM_READ_WRITE)));
    
        int result = kernels[0].setArg(0, buf);
        std::cout << result << " == " << CL_INVALID_MEM_OBJECT << std::endl;
    
        queue.enqueueNDRangeKernel(kernels[0], cl::NullRange, cl::NDRange(16), cl::NDRange(4));
        queue.flush();
    
        //DISPLAY RESULT?
        string window_name = "Test OpenCV and OpenCL";
        namedWindow(window_name);
        imshow(window_name, frame);
        waitKey(5000);
    
        return 0;
    }
    

    最佳答案

    cv::UMat和opencv“透明API”的使用非常不直观,主要是因为它们从客户端隐藏了实际内存管理的非常重要的任务。

    具体来说,在您的代码中,您提供了一个空的cv::UMat来限制cap::read。 opencv将必须为实际帧分配内存。但是,不能保证此内存将实际分配在正确的设备(clbuffer)内存上。如果您调试opencv源,我不会感到惊讶,您将看到在RAM上分配的实际内存。因此没有有效的cl_mem句柄。

    您基本上有2个选择:

    选项1:在设备上明确分配cv::UMat:

    UMat frame = cv::UMat(cv::Size(width, height), format, cv::USAGE_ALLOCATE_DEVICE_MEMORY);
    assert(cap.read(frame));
    

    选项2:用cv::UMat包装一个预先分配的opencl缓冲区
    cv::UMat frame;
    cv::ocl::convertFromBuffer(
        my_cl_mem,
        pitch,
        rows,
        cols,
        format,
        frame
    );
    

    另外,由于opencv与opencl的工作方式完全是效率低下的困惑,如果提供一个预先固定的主机内存来cap::read并随后异步将其传输到设备,我将不会感到惊讶,这会更有效率。请注意,您可以将任何主机内存指针包裹在cv::Mat中。

    09-05 07:21