本文主要讲解opencl在windows下,使用cpp编程的基本过程,使用的IDE是Visual Studio 2017。
下面的代码连在一起就可以运行
step1:新建工程,导入头文件
在VS上新建一个工程,然后配置相关的头文件:视图->其它窗口->属性管理器,然后配置一些项,目的是将opencl的头文件导入这个cpp工程里面。(这里不介绍了,有很多现成的文章)
step2:导入头文件
#include <opencv2/opencv.hpp> //我的核函数是用来处理图片的,如果不是可以去掉
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <CL/cl2.hpp> //cpp版本的opencl的头文件
using namespace cv;
using namespace std;
#pragma warning( disable : 4996 ) //去掉告警信息
step3:获取平台和上下文
cl::Platform platforms = cl::Platform::getDefault();
//我们获取的是gpu设备
cl::Context context = cl::Context(CL_DEVICE_TYPE_GPU,NULL);
step4:获取设备和命令队列
这里的errNum帮助我们判断每一步是否出错,选中CL_SUCCESS,按F12,就可以看到这些错误码的具体定义
cl_int errNum = CL_SUCCESS;
std::vector<cl::Device> devices = context.getInfo<CL_CONTEXT_DEVICES>();
cl::CommandQueue cq = cl::CommandQueue(context, devices[0], CL_QUEUE_PROFILING_ENABLE, &errNum);
if(errNum != CL_SUCCESS){
printf("创建命令队列失败,错误码:%d\n, errNum);
}
step5:创建并且编译Program
main.cl是一个文件,放的是所有的核函数
//读取main.cl中核函数的内容
std::ifstream kernelFile("main.cl", std::ios::in);
std::ostringstream oss;
oss << kernelFile.rdbuf();
std::string srcStdStr = oss.str();
const char *srcStr = srcStdStr.c_str();
//创建Program
//这里要注意,传入的是核函数的内容,而不是核函数所在的文件的名字
program = cl::Program(context, srcStr, false, &errNum);
errNum = program.build(devices); //编译Program
step6:编写核函数
这段代码放在main.cl文件中,我们这里的main.cl中只有一个核函数,当然,可以有多个
__kernel void kernel_func(__global unsigned char * rgbImage, __global * result)
{
int x = get_global_id(0);
int y = get_global_id(1);
int index = x * height + y;
result[index] = rgbImage[index]; //这里是输出
}
step7:为核函数设置参数
Mat image = imread("D://b.jpg"); //存放自己图像的路径
Mat dst;
cvtColor(image, dst, CV_BGR2GRAY);//图片转灰度图
int imgSize = dst.rows * dst.cols;
//输入的参数
cl::Buffer srcImg(context, CL_MEM_USE_HOST_PTR, sizeof(uchar) * 3 * imgSize, dst.data, &errNum);
//输出的参数
cl::Buffer memResult = cl::Buffer(context, CL_MEM_WRITE_ONLY, sizeof(int)*imgSize,NULL,NULL);
//设置参数
errNum = kernel.setArg(0, sizeof(cl_mem), &srcImg);
errNum = kernel.setArg(1, sizeof(cl_mem), &memResult);
step8:执行核函数
size_t globalThreads[2];
globalThreads[0] = dst.cols;
globalThreads[1] = dst.rows;
errNum = cq.enqueueNDRangeKernel(kernel, cl::NDRange(0), cl::NDRange(globalThreads[0], globalThreads[1]), cl::NullRange, NULL, NULL);
step9:获取执行结果
float * result = new float[imgSize];
errNum = cq.enqueueReadBuffer(memResult, true, 0, sizeof(float)*imgSize, result, NULL, NULL);