本文主要講解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);