將OpenCL和OpenCV一些簡單的方法封裝成DLL,具體OpenCL和OpenCV的配置方法可以參考本人的部落格。
VS2015下安裝與編譯OpenCV原始碼並在VS2015下配置OpenCV環境
VS上執行CUDA,並在NVDIA顯示卡安裝的CUDA中執行OpenCL
1. DLL封裝
(1) 建立DLL工程
(2)新建標頭檔案
向動態連結庫新增類的核心程式碼:
1 2 3 4 5 |
#ifdef CREATEOPENCLDLL_EXPORTS #define CREATEOPENCLDLL_API __declspec(dllexport) #else #define CREATEOPENCLDLL_API __declspec(dllimport) #endif |
為顯式連結做準備的匯出函式:
1 2 3 4 5 |
extern "C" { CREATEOPENCLDLL_API CreateOpenCLDLL* GetCreateOpenCLDLL(void); typedef CreateOpenCLDLL* (*PFNGetCreateOpenCLDLL)(void); } |
建立類時必須加上CREATEOPENCLDLL_API,如寫成class CREATEOPENCLDLL_APICreateOpenCLDLL 要不然生成不了lib檔案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#pragma once #ifndef _CREATEOPENCLDLL_H_ #define _CREATEOPENCLDLL_H_ #ifdef CREATEOPENCLDLL_EXPORTS #define CREATEOPENCLDLL_API __declspec(dllexport) #else #define CREATEOPENCLDLL_API __declspec(dllimport) #endif #ifdef __APPLE__ #include <OpenCL/cl.h> #else #include <CL/cl.h> #endif class CREATEOPENCLDLL_API CreateOpenCLDLL { public: CreateOpenCLDLL(); ~CreateOpenCLDLL(); // 選擇平臺並建立上下文 cl_context CreateContext(); cl_command_queue CreateCommandQueue(cl_context context, cl_device_id *device); cl_program CreateProgram(cl_context context, cl_device_id device, const char* fileName); bool CreateMemObjects(cl_context context, cl_mem memObjects[3], float *a, float *b); void Cleanup(cl_context context, cl_command_queue commandQueue, cl_program program, cl_kernel kernel, cl_mem memObjects[3]); void Calcaute(); private: cl_context m_context; cl_command_queue m_commandQueue; cl_program m_program; cl_device_id m_device; cl_kernel m_kernel; cl_mem m_memObjects[3]; cl_int m_errNum; }; /* 匯出函式宣告 */ extern "C" { CREATEOPENCLDLL_API CreateOpenCLDLL* GetCreateOpenCLDLL(void); typedef CreateOpenCLDLL* (*PFNGetCreateOpenCLDLL)(void); } #endif |
(3)Cpp檔案:createopencldll.cpp
需要新增:#define CREATEOPENCLDLL_EXPORTS
完整的cpp程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
#define CREATEOPENCLDLL_EXPORTS #include <iostream> #include <fstream> #include <string> #include <vector> #include <sstream> #include "createopencldll.h" using namespace std; const int KArraySize = 1000; CreateOpenCLDLL::CreateOpenCLDLL() { m_context = 0; m_commandQueue = 0; m_program = 0; m_device = 0; m_kernel = 0; m_memObjects[0] = 0; m_memObjects[1] = 0; m_memObjects[2] = 0; m_errNum = 0; } CreateOpenCLDLL::~CreateOpenCLDLL() { } // 選擇平臺並建立上下文 cl_context CreateOpenCLDLL::CreateContext() { cl_int errNum; cl_uint numPlatforms; cl_platform_id firstPlatformId; cl_context context = NULL; //選擇第一個可用的平臺 errNum = clGetPlatformIDs(1, &firstPlatformId, &numPlatforms); if (errNum != CL_SUCCESS || numPlatforms <= 0) { std::cerr << "Failed to find any OpenCL platforms." << std::endl; return NULL; } // 建立一個opencl上下文,成功則使用GUP上下文,否則使用cpu cl_context_properties contextProperties[] = { CL_CONTEXT_PLATFORM, (cl_context_properties)firstPlatformId, 0 }; context = clCreateContextFromType(contextProperties, CL_DEVICE_TYPE_GPU, NULL, NULL, &errNum); if (errNum != CL_SUCCESS) { std::cout << "Could not create GPU context, trying CPU..." << std::endl; context = clCreateContextFromType(contextProperties, CL_DEVICE_TYPE_CPU, NULL, NULL, &errNum); if (errNum != CL_SUCCESS) { std::cerr << "Failed to create an OpenCL GPU or CPU context." << std::endl; return NULL; } } return context; } //選擇第一個可用的裝置並建立一個命令佇列 cl_command_queue CreateOpenCLDLL::CreateCommandQueue(cl_context context, cl_device_id *device) { cl_int errNum; cl_device_id *devices; cl_command_queue commandQueue = NULL; size_t deviceBufferSize = -1; //這個clGetContextInfo獲得裝置緩衝區的大小 errNum = clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &deviceBufferSize); if (errNum != CL_SUCCESS) { std::cerr << "Failed call to clGetContextInfo(...,GL_CONTEXT_DEVICES,...)"; return NULL; } if (deviceBufferSize <= 0) { std::cerr << "No devices available."; return NULL; } //為裝置緩衝區分配記憶體,這個clGetContextInfo用來獲得上下文中所有可用的裝置 devices = new cl_device_id[deviceBufferSize / sizeof(cl_device_id)]; errNum = clGetContextInfo(context, CL_CONTEXT_DEVICES, deviceBufferSize, devices, NULL); if (errNum != CL_SUCCESS) { delete[] devices; std::cerr << "Failed to get device IDs"; return NULL; } char deviceName[512]; char deviceVendor[512]; char deviceVersion[512]; errNum = clGetDeviceInfo(devices[0], CL_DEVICE_VENDOR, sizeof(deviceVendor), deviceVendor, NULL); errNum |= clGetDeviceInfo(devices[0], CL_DEVICE_NAME, sizeof(deviceName), deviceName, NULL); errNum |= clGetDeviceInfo(devices[0], CL_DEVICE_VERSION, sizeof(deviceVersion), deviceVersion, NULL); printf("OpenCL Device Vendor = %s, OpenCL Device Name = %s, OpenCL Device Version = %s\n", deviceVendor, deviceName, deviceVersion); // 在這個例子中,我們只選擇第一個可用的裝置。在實際的程式,你可能會使用所有可用的裝置或基於OpenCL裝置查詢選擇效能最高的裝置 commandQueue = clCreateCommandQueue(context, devices[0], 0, NULL); if (commandQueue == NULL) { delete[] devices; std::cerr << "Failed to create commandQueue for device 0"; return NULL; } *device = devices[0]; delete[] devices; return commandQueue; } //從磁碟載入核心原始檔並建立一個程式物件 cl_program CreateOpenCLDLL::CreateProgram(cl_context context, cl_device_id device, const char* fileName) { cl_int errNum; cl_program program; std::ifstream kernelFile(fileName, std::ios::in); if (!kernelFile.is_open()) { std::cerr << "Failed to open file for reading: " << fileName << std::endl; return NULL; } std::ostringstream oss; oss << kernelFile.rdbuf(); std::string srcStdStr = oss.str(); const char *srcStr = srcStdStr.c_str(); //建立程式物件 program = clCreateProgramWithSource(context, 1, (const char**)&srcStr, NULL, NULL); if (program == NULL) { std::cerr << "Failed to create CL program from source." << std::endl; return NULL; } //編譯核心原始碼 errNum = clBuildProgram(program, 0, NULL, NULL, NULL, NULL); if (errNum != CL_SUCCESS) { // 編譯失敗可以通過clGetProgramBuildInfo獲取日誌 char buildLog[16384]; clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG, sizeof(buildLog), buildLog, NULL); std::cerr << "Error in kernel: " << std::endl; std::cerr << buildLog; clReleaseProgram(program); return NULL; } return program; } //建立記憶體物件 bool CreateOpenCLDLL::CreateMemObjects(cl_context context, cl_mem memObjects[3],float *a, float *b) { //建立記憶體物件 memObjects[0] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(float)* KArraySize, a, NULL); memObjects[1] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(float)* KArraySize, b, NULL); memObjects[2] = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(float)* KArraySize, NULL, NULL); if (memObjects[0] == NULL || memObjects[1] == NULL || memObjects[2] == NULL) { std::cerr << "Error creating memory objects." << std::endl; return false; } return true; } //清理任何建立OpenCL的資源 void CreateOpenCLDLL::Cleanup(cl_context context, cl_command_queue commandQueue, cl_program program, cl_kernel kernel, cl_mem memObjects[3]) { for (int i = 0; i < 3; i++) { if (memObjects[i] != 0) clReleaseMemObject(memObjects[i]); } if (commandQueue != 0) clReleaseCommandQueue(commandQueue); if (kernel != 0) clReleaseKernel(kernel); if (program != 0) clReleaseProgram(program); if (context != 0) clReleaseContext(context); } void CreateOpenCLDLL::Calcaute() { // 建立opencl上下文和第一個可用平臺 m_context = CreateContext(); if (m_context == NULL) { std::cerr << "Failed to create OpenCL context." << std::endl; } // 在建立的一個上下文中選擇第一個可用的裝置並建立一個命令佇列 m_commandQueue = CreateCommandQueue(m_context, &m_device); if (m_commandQueue == NULL) { Cleanup(m_context, m_commandQueue, m_program, m_kernel, m_memObjects); } // 建立一個程式物件 HelloWorld.cl kernel source m_program = CreateProgram(m_context, m_device, "HelloWorld.cl"); if (m_program == NULL) { Cleanup(m_context, m_commandQueue, m_program, m_kernel, m_memObjects); } // 建立核心 m_kernel = clCreateKernel(m_program, "hello_kernel", NULL); if (m_kernel == NULL) { std::cerr << "Failed to create kernel" << std::endl; Cleanup(m_context, m_commandQueue, m_program, m_kernel, m_memObjects); } // 建立一個將用作引數核心記憶體中的物件。首先建立將被用來將引數儲存到核心主機儲存器陣列 float result[KArraySize]; float a[KArraySize]; float b[KArraySize]; for (int i = 0; i < KArraySize; i++) { a[i] = (float)i; b[i] = (float)(i * 2); } if (!CreateMemObjects(m_context, m_memObjects, a, b)) { Cleanup(m_context, m_commandQueue, m_program, m_kernel, m_memObjects); } // 設定核心引數、執行核心並讀回結果 m_errNum = clSetKernelArg(m_kernel, 0, sizeof(cl_mem), &m_memObjects[0]); m_errNum |= clSetKernelArg(m_kernel, 1, sizeof(cl_mem), &m_memObjects[1]); m_errNum |= clSetKernelArg(m_kernel, 2, sizeof(cl_mem), &m_memObjects[2]); if (m_errNum != CL_SUCCESS) { std::cerr << "Error setting kernel arguments." << std::endl; Cleanup(m_context, m_commandQueue, m_program, m_kernel, m_memObjects); } size_t globalWorkSize[1] = { KArraySize };//讓之等於陣列的大小 size_t localWorkSize[1] = { 1 }; //讓之等於1 // 利用命令佇列使將在裝置執行的核心排隊 m_errNum = clEnqueueNDRangeKernel(m_commandQueue, m_kernel, 1, NULL, globalWorkSize, localWorkSize, 0, NULL, NULL); if (m_errNum != CL_SUCCESS) { std::cerr << "Error queuing kernel for execution." << std::endl; Cleanup(m_context, m_commandQueue, m_program, m_kernel, m_memObjects); } std::cout << "Executed program succesfully." << std::endl; // Read the output buffer back to the Host m_errNum = clEnqueueReadBuffer(m_commandQueue, m_memObjects[2], CL_TRUE, 0, KArraySize * sizeof(float), result, 0, NULL, NULL); if (m_errNum != CL_SUCCESS) { std::cerr << "Error reading result buffer." << std::endl; Cleanup(m_context, m_commandQueue, m_program, m_kernel, m_memObjects); } //輸出結果 for (int i = 0; i < KArraySize; i++) { std::cout << result[i] << " "; } std::cout << std::endl; std::cout << "Executed program succesfully." << std::endl; Cleanup(m_context, m_commandQueue, m_program, m_kernel, m_memObjects); } /* 匯出函式定義 */ CreateOpenCLDLL* GetCreateOpenCLDLL(void) { CreateOpenCLDLL* pCreateOpenCLDLL = new CreateOpenCLDLL(); return pCreateOpenCLDLL; } |
(4)生成程式碼
封裝好一個類之後,在後面的類可以呼叫這個類生成的dll,再封裝新類的dll。具體過程可以參考第1步。
在工程中新增標頭檔案createopencldll.h到新的工程目錄。在debug或者release檔案中複製buildopencldll.lib到新的工程目錄如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#pragma once #ifdef CREATEOPENCVDLL_EXPORTS #define CREATEOPENCVDLL_API __declspec(dllexport) #else #define CREATEOPENCVDLL_API __declspec(dllimport) #endif #include "createopencldll.h" #include "opencv2\opencv_modules.hpp" #include "opencv2\highgui\highgui.hpp" #include "opencv2\core\core.hpp" using namespace cv; class CREATEOPENCVDLL_API CreateOpenCVDLL { public: CreateOpenCVDLL(); ~CreateOpenCVDLL(); void showImage(); private: Mat m_image; }; |
createopencvdll.cpp程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#define CREATEOPENCVDLL_EXPORTS #include "createopencvdll.h" CreateOpenCVDLL::CreateOpenCVDLL() { } CreateOpenCVDLL::~CreateOpenCVDLL() { } void CreateOpenCVDLL::showImage() { m_image = imread("1.jpg"); namedWindow("image", CV_WINDOW_AUTOSIZE); imshow("image", m_image); cvWaitKey(0); } |
3. DLL 的呼叫
方法1:
在工程中新增標頭檔案createopencldll.h到新的工程目錄。在debug或者release檔案中複製buildopencldll.lib到新的工程目錄如下:
1 2 3 4 5 6 7 8 |
#include "createopencldll.h" int main() { CreateOpenCLDLL createopencldll; createopencldll.Calcaute(); return 0; } |
結果圖如下:
方法2:
1 2 3 4 5 6 7 8 9 10 |
#pragma comment(lib, "buildopencldll.lib") #include "createopencldll.h" int main() { CreateOpenCLDLL createopencldll; createopencldll.Calcaute(); return 0; } |
(2)顯式連結
參考部落格:http://blog.csdn.net/chollima/article/details/5324808
這個部落格先定義虛基類,然後到基類等的實現,這樣比較好。
這種方法不需要createopencldll.lib,將createopencldll.dll複製到debug或者release目錄。
具體實現程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <windows.h> #include <cstdio> #include "createopencldll.h" int main() { HMODULE hDll = ::LoadLibrary("buildopencldll.dll"); if (NULL != hDll) { PFNGetCreateOpenCLDLL pFun = (PFNGetCreateOpenCLDLL)::GetProcAddress(hDll, "GetCreateOpenCLDLL"); if (NULL != pFun) { CreateOpenCLDLL* createOpenCLDLL = (*pFun)(); if (NULL != createOpenCLDLL) { createOpenCLDLL->Calcaute(); delete createOpenCLDLL; } } ::FreeLibrary(hDll); } return 0; } |
如果出現錯誤:
錯誤 C2664 “HMODULELoadLibraryW(LPCWSTR)”: 無法將引數 1 從“const char [19]”轉換為“LPCWSTR” bulidopecvdll f:\openclproject\bulidopecvdll\bulidopecvdll\main.cpp 18
進入屬性->常規->字符集,將使用Unicode字符集改為未設定即可。
http://blog.csdn.net/xiaowei_cqu/article/details/7737682
https://msdn.microsoft.com/zh-cn/library/ms235636
http://www.cnblogs.com/weidiao/p/5013889.html
http://blog.csdn.net/xiamentingtao/article/details/51052918
http://www.cnblogs.com/laogao/archive/2012/12/07/2806528.html