如何从OpenCL访问OpenCV UMat(gpu)缓冲区?

问题描述 投票:5回答:1

简介:

目的是使用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编译
  • 给定的缓冲区似乎无效。

DISCLAIM:请,请友好,因为这只是这个问题的一个非常小的例子。

#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;
}
c++ opencv opencl
1个回答
0
投票

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

具体地说,在您的代码中,您提供了一个空的cv :: UMat来限制cap :: read。 opencv将必须为实际帧allocate内存。但是,不能保证此内存实际上将分配在正确的设备(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));

options 2:用cv :: UMat包装一个预先分配的opencl缓冲区

cv::UMat frame;
cv::ocl::convertFromBuffer(
    my_cl_mem,
    pitch,
    rows,
    cols,
    format,
    frame
);
© www.soinside.com 2019 - 2024. All rights reserved.