OpenCV Android SDK中提供的靜態、動態庫是不支持OpenCL加速的,若是在程序中調用OpenCL相關函數,編譯時不會報錯,但運行時logcat會輸出以下信息,提示OpenCL函數不可用linux
08-11 11:43:07.140: E/cv::error()(18198): OpenCV Error: Unknown error code -6 (OpenCL function is not available: [clGetPlatformIDs]) in void* opencl_check_fn(int), file /hdd2/buildbot/slaves/slave_ardbeg1/50-SDK/opencv/modules/ocl/src/cl_runtime/cl_runtime.cpp, line 83 08-11 11:43:07.140: E/cv::error()(18198): OpenCV Error: Unknown error code -221 (OpenCL not available) in static cv::ocl::ContextImpl* cv::ocl::ContextImpl::getContext(), file /hdd2/buildbot/slaves/slave_ardbeg1/50-SDK/opencv/modules/ocl/src/cl_context.cpp, line 678
因此,若是想使用OpenCV for Android中的ocl模塊,須要本身編譯一套支持OpenCL的OpenCV庫文件,同時還須要有一部具備OpenCL驅動的設備。下面詳細講述整個過程,整個的編譯過程都是基於windows平臺的,其餘平臺過程相似,只是編譯時的命令會有所不一樣。android
1.如何判斷設備是否支持OpenCLgit
能夠下載一款叫OpenCL-Z的軟件。安裝後,它能夠檢測當前設備的GPU是否支持OpenCL和是否具備OpenCL驅動。下面是豌豆莢上的下載地址:github
http://www.wandoujia.com/apps/com.robertwgh.opencl_z_androidexpress
不過有的時候設備的GPU支持OpenCL可是沒有安裝正確的驅動,這個時候須要以下幾個不住:windows
A.肯定設備的GPU型號,這個能夠經過google,baidu搞定。app
B.經過https://www.khronos.org/conformance/adopters/conformant-products/網站查詢你的GPU型號是否支持OpenCLide
C.若是支持,例如Adreno330,能夠去對應的網站下載驅動更新。Adreno330的驅動下載網址:https://developer.qualcomm.com/software/adreno-gpu-sdk/tools,下載連接在Drivers這一項下面。函數
D.根據驅動中的說明文檔,刷機->更新驅動。(其實這個過程挺麻煩,不過個人確讓4.4系統的nexus5支持OpenCL了)工具
2.編譯帶OpenCL模塊的OpenCV for Android
(1)首先須要從OpenCV官網下載源碼,我是基於2.4.11版本的源碼編譯的(3.x版本工程總體結構變化太大,並且單獨的ocl模塊被合入其餘模塊了,我還沒搞明白怎麼用)。
這個網址能夠下載到2.4.11版本的源碼:https://github.com/Itseez/opencv/releases
(2)接下來配置須要的工具,主要有兩個:cmake,Android ndk。下載&安裝,在環境變量中配置好cmake的bin目錄,並將ndk的根目錄添加爲ANDROID_NDK值。
(3)在opencv_path\modules\ocl\src\cl_runtime\cl_runtime.cpp文件中,作以下修改:
第48行,#if defined(__linux__) 改成 #if defined(__linux__)&&!defined(__ANDROID__)
第70行後,添加以下代碼:
#if defined(__ANDROID__) #include <dlfcn.h> #include <sys/stat.h> #if defined(__ARM_ARCH_8A__) || defined(_X64_) static const char *default_so_paths[] = { "/system/lib64/libOpenCL.so", "/system/vendor/lib64/libOpenCL.so", "/system/vendor/lib64/egl/libGLES_mali.so" }; #else static const char *default_so_paths[] = { "/system/lib/libOpenCL.so", "/system/vendor/lib/libOpenCL.so", "/system/vendor/lib/egl/libGLES_mali.so" }; #endif static int access_file(const char *filename) { struct stat buffer; return (stat(filename, &buffer) == 0); } static void* GetProcAddress (const char* name) { static void* h = NULL; unsigned int i; if (!h) { const char* name; for(i=0; i<(sizeof(default_so_paths)/sizeof(char*)); i++) { if(access_file(default_so_paths[i])) { name = (char *)default_so_paths[i]; h = dlopen(name, RTLD_LAZY); if (h) break; } } if (!h) return NULL; } return dlsym(h, name); } #define CV_CL_GET_PROC_ADDRESS(name) GetProcAddress(name) #endif
(4)編譯opencv。打開cmd,將當前目錄切換到opencv_path\platforms\下面,執行以下命令:
1 mkdir build_opencl 2 cd build_opencl 3 cmake -G "MinGW Makefiles" -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DWITH_EIGEN=off -DCMAKE_TOOLCHAIN_FILE=..\android\android.toolchain.cmake -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%\prebuilt\windows-x86_64\bin\make.exe" -DANDROID_ABI=armeabi ..\..
其中,ANDROID_NDK是在環境變量中設置好的變量值,爲android ndk的根目錄,DANDROID_ABI能夠根據須要選擇。執行完畢後,執行以下命令便可:
cmake --build .
注意build後面的那個「.」不能少。
(5)編譯完成後,把\build_opencl\lib\armeabi下面的庫覆蓋到opencv android sdk中的sdk\native\libs目錄下對應的文件夾。
(6)clean原有的工程,而後build project。在設備上運行,一切OK。
這裏是測試工程的源碼,須要用本身圖片path替換代碼中的path,而後app會在同一目錄下生成一幅灰度圖。例如:輸入圖像爲設備根目錄下a.jpg,運行後同一目錄下會生成agray.jpg灰度圖片。
更新:
編譯帶有OpenCL模塊的OpenCV3.0.0 for android
1.根目錄下的CMakeLists.txt中,設置WITH_OPENCL爲ON
2.\modules\core\src\opencl\runtime\opencl_core.cpp文件中,按照OpenCV2.4.11的方法修改並添加代碼:
/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the OpenCV Foundation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ #include "../../precomp.hpp" #if defined(HAVE_OPENCL) && !defined(HAVE_OPENCL_STATIC) #include "opencv2/core.hpp" // CV_Error #include "opencv2/core/opencl/runtime/opencl_core.hpp" #define OPENCL_FUNC_TO_CHECK_1_1 "clEnqueueReadBufferRect" #define ERROR_MSG_CANT_LOAD "Failed to load OpenCL runtime\n" #define ERROR_MSG_INVALID_VERSION "Failed to load OpenCL runtime (expected version 1.1+)\n" #if defined(__APPLE__) #include <dlfcn.h> static void* AppleCLGetProcAddress(const char* name) { static bool initialized = false; static void* handle = NULL; if (!handle) { if(!initialized) { initialized = true; const char* path = "/System/Library/Frameworks/OpenCL.framework/Versions/Current/OpenCL"; const char* envPath = getenv("OPENCV_OPENCL_RUNTIME"); if (envPath) path = envPath; handle = dlopen(oclpath, RTLD_LAZY | RTLD_GLOBAL); if (handle == NULL) { if (envPath) fprintf(stderr, ERROR_MSG_CANT_LOAD); } else if (dlsym(handle, OPENCL_FUNC_TO_CHECK_1_1) == NULL) { fprintf(stderr, ERROR_MSG_INVALID_VERSION); handle = NULL; } } if (!handle) return NULL; } return dlsym(handle, name); } #define CV_CL_GET_PROC_ADDRESS(name) AppleCLGetProcAddress(name) #endif // __APPLE__ #if defined(_WIN32) #include <windows.h> static void* WinGetProcAddress(const char* name) { static bool initialized = false; static HMODULE handle = NULL; if (!handle) { if(!initialized) { initialized = true; handle = GetModuleHandleA("OpenCL.dll"); if (!handle) { const char* path = "OpenCL.dll"; const char* envPath = getenv("OPENCV_OPENCL_RUNTIME"); if (envPath) path = envPath; handle = LoadLibraryA(path); if (!handle) { if (envPath) fprintf(stderr, ERROR_MSG_CANT_LOAD); } else if (GetProcAddress(handle, OPENCL_FUNC_TO_CHECK_1_1) == NULL) { fprintf(stderr, ERROR_MSG_INVALID_VERSION); handle = NULL; } } } if (!handle) return NULL; } return (void*)GetProcAddress(handle, name); } #define CV_CL_GET_PROC_ADDRESS(name) WinGetProcAddress(name) #endif // _WIN32 #if defined(__linux__)&&!defined(__ANDROID__) #include <dlfcn.h> #include <stdio.h> static void* GetProcAddress(const char* name) { static bool initialized = false; static void* handle = NULL; if (!handle) { if(!initialized) { initialized = true; const char* path = "libOpenCL.so"; const char* envPath = getenv("OPENCV_OPENCL_RUNTIME"); if (envPath) path = envPath; handle = dlopen(path, RTLD_LAZY | RTLD_GLOBAL); if (handle == NULL) { if (envPath) fprintf(stderr, ERROR_MSG_CANT_LOAD); } else if (dlsym(handle, OPENCL_FUNC_TO_CHECK_1_1) == NULL) { fprintf(stderr, ERROR_MSG_INVALID_VERSION); handle = NULL; } } if (!handle) return NULL; } return dlsym(handle, name); } #define CV_CL_GET_PROC_ADDRESS(name) GetProcAddress(name) #endif //linux #if defined(__ANDROID__) #include <dlfcn.h> #include <sys/stat.h> #if defined(__ARM_ARCH_8A__) || defined(_X64_) static const char *default_so_paths[] = { "/system/lib64/libOpenCL.so", "/system/vendor/lib64/libOpenCL.so", "/system/vendor/lib64/egl/libGLES_mali.so" }; #else static const char *default_so_paths[] = { "/system/lib/libOpenCL.so", "/system/vendor/lib/libOpenCL.so", "/system/vendor/lib/egl/libGLES_mali.so" }; #endif static int access_file(const char *filename) { struct stat buffer; return (stat(filename, &buffer) == 0); } static void* GetProcAddress (const char* name) { static void* h = NULL; unsigned int i; if (!h) { const char* name; for(i=0; i<(sizeof(default_so_paths)/sizeof(char*)); i++) { if(access_file(default_so_paths[i])) { name = (char *)default_so_paths[i]; h = dlopen(name, RTLD_LAZY); if (h) break; } } if (!h) return NULL; } return dlsym(h, name); } #define CV_CL_GET_PROC_ADDRESS(name) GetProcAddress(name) #endif //android #ifndef CV_CL_GET_PROC_ADDRESS #ifdef __GNUC__ #warning("OPENCV: OpenCL dynamic library loader: check configuration") #else #pragma message("WARNING: OPENCV: OpenCL dynamic library loader: check configuration") #endif #define CV_CL_GET_PROC_ADDRESS(name) NULL #endif static void* opencl_check_fn(int ID); #include "runtime_common.hpp" #include "autogenerated/opencl_core_impl.hpp" // // BEGIN OF CUSTOM FUNCTIONS // #define CUSTOM_FUNCTION_ID 1000 #ifdef HAVE_OPENCL_SVM #include "opencv2/core/opencl/runtime/opencl_svm_20.hpp" #define SVM_FUNCTION_ID_START CUSTOM_FUNCTION_ID #define SVM_FUNCTION_ID_END CUSTOM_FUNCTION_ID + 100 enum OPENCL_FN_SVM_ID { OPENCL_FN_clSVMAlloc = SVM_FUNCTION_ID_START, OPENCL_FN_clSVMFree, OPENCL_FN_clSetKernelArgSVMPointer, OPENCL_FN_clSetKernelExecInfo, OPENCL_FN_clEnqueueSVMFree, OPENCL_FN_clEnqueueSVMMemcpy, OPENCL_FN_clEnqueueSVMMemFill, OPENCL_FN_clEnqueueSVMMap, OPENCL_FN_clEnqueueSVMUnmap, }; void* (CL_API_CALL *clSVMAlloc)(cl_context context, cl_svm_mem_flags flags, size_t size, unsigned int alignment) = opencl_fn4<OPENCL_FN_clSVMAlloc, void*, cl_context, cl_svm_mem_flags, size_t, unsigned int>::switch_fn; static const struct DynamicFnEntry _clSVMAlloc_definition = { "clSVMAlloc", (void**)&clSVMAlloc}; void (CL_API_CALL *clSVMFree)(cl_context context, void* svm_pointer) = opencl_fn2<OPENCL_FN_clSVMFree, void, cl_context, void*>::switch_fn; static const struct DynamicFnEntry _clSVMFree_definition = { "clSVMFree", (void**)&clSVMFree}; cl_int (CL_API_CALL *clSetKernelArgSVMPointer)(cl_kernel kernel, cl_uint arg_index, const void* arg_value) = opencl_fn3<OPENCL_FN_clSetKernelArgSVMPointer, cl_int, cl_kernel, cl_uint, const void*>::switch_fn; static const struct DynamicFnEntry _clSetKernelArgSVMPointer_definition = { "clSetKernelArgSVMPointer", (void**)&clSetKernelArgSVMPointer}; //void* (CL_API_CALL *clSetKernelExecInfo)(cl_kernel kernel, cl_kernel_exec_info param_name, size_t param_value_size, const void* param_value) = // opencl_fn4<OPENCL_FN_clSetKernelExecInfo, void*, cl_kernel, cl_kernel_exec_info, size_t, const void*>::switch_fn; //static const struct DynamicFnEntry _clSetKernelExecInfo_definition = { "clSetKernelExecInfo", (void**)&clSetKernelExecInfo}; //cl_int (CL_API_CALL *clEnqueueSVMFree)(...) = // opencl_fn8<OPENCL_FN_clEnqueueSVMFree, cl_int, ...>::switch_fn; //static const struct DynamicFnEntry _clEnqueueSVMFree_definition = { "clEnqueueSVMFree", (void**)&clEnqueueSVMFree}; cl_int (CL_API_CALL *clEnqueueSVMMemcpy)(cl_command_queue command_queue, cl_bool blocking_copy, void* dst_ptr, const void* src_ptr, size_t size, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event) = opencl_fn8<OPENCL_FN_clEnqueueSVMMemcpy, cl_int, cl_command_queue, cl_bool, void*, const void*, size_t, cl_uint, const cl_event*, cl_event*>::switch_fn; static const struct DynamicFnEntry _clEnqueueSVMMemcpy_definition = { "clEnqueueSVMMemcpy", (void**)&clEnqueueSVMMemcpy}; cl_int (CL_API_CALL *clEnqueueSVMMemFill)(cl_command_queue command_queue, void* svm_ptr, const void* pattern, size_t pattern_size, size_t size, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event) = opencl_fn8<OPENCL_FN_clEnqueueSVMMemFill, cl_int, cl_command_queue, void*, const void*, size_t, size_t, cl_uint, const cl_event*, cl_event*>::switch_fn; static const struct DynamicFnEntry _clEnqueueSVMMemFill_definition = { "clEnqueueSVMMemFill", (void**)&clEnqueueSVMMemFill}; cl_int (CL_API_CALL *clEnqueueSVMMap)(cl_command_queue command_queue, cl_bool blocking_map, cl_map_flags map_flags, void* svm_ptr, size_t size, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event) = opencl_fn8<OPENCL_FN_clEnqueueSVMMap, cl_int, cl_command_queue, cl_bool, cl_map_flags, void*, size_t, cl_uint, const cl_event*, cl_event*>::switch_fn; static const struct DynamicFnEntry _clEnqueueSVMMap_definition = { "clEnqueueSVMMap", (void**)&clEnqueueSVMMap}; cl_int (CL_API_CALL *clEnqueueSVMUnmap)(cl_command_queue command_queue, void* svm_ptr, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event) = opencl_fn5<OPENCL_FN_clEnqueueSVMUnmap, cl_int, cl_command_queue, void*, cl_uint, const cl_event*, cl_event*>::switch_fn; static const struct DynamicFnEntry _clEnqueueSVMUnmap_definition = { "clEnqueueSVMUnmap", (void**)&clEnqueueSVMUnmap}; static const struct DynamicFnEntry* opencl_svm_fn_list[] = { &_clSVMAlloc_definition, &_clSVMFree_definition, &_clSetKernelArgSVMPointer_definition, NULL/*&_clSetKernelExecInfo_definition*/, NULL/*&_clEnqueueSVMFree_definition*/, &_clEnqueueSVMMemcpy_definition, &_clEnqueueSVMMemFill_definition, &_clEnqueueSVMMap_definition, &_clEnqueueSVMUnmap_definition, }; #endif // HAVE_OPENCL_SVM // // END OF CUSTOM FUNCTIONS HERE // static void* opencl_check_fn(int ID) { const struct DynamicFnEntry* e = NULL; if (ID < CUSTOM_FUNCTION_ID) { assert(ID >= 0 && ID < (int)(sizeof(opencl_fn_list)/sizeof(opencl_fn_list[0]))); e = opencl_fn_list[ID]; } #ifdef HAVE_OPENCL_SVM else if (ID >= SVM_FUNCTION_ID_START && ID < SVM_FUNCTION_ID_END) { ID = ID - SVM_FUNCTION_ID_START; assert(ID >= 0 && ID < (int)(sizeof(opencl_svm_fn_list)/sizeof(opencl_svm_fn_list[0]))); e = opencl_svm_fn_list[ID]; } #endif else { CV_ErrorNoReturn(cv::Error::StsBadArg, "Invalid function ID"); } void* func = CV_CL_GET_PROC_ADDRESS(e->fnName); if (!func) { throw cv::Exception(cv::Error::OpenCLApiCallError, cv::format("OpenCL function is not available: [%s]", e->fnName), CV_Func, __FILE__, __LINE__); } *(e->ppFn) = func; return func; } #endif
3.和2.4.11同樣,建立文件夾,而後順序執行以下編譯命令:
cmake -G "MinGW Makefiles" -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DWITH_OPENCL=ON -DWITH_EIGEN=off -DCMAKE_TOOLCHAIN_FILE=..\android\android.toolchain.cmake -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%\prebuilt\windows-x86_64\bin\make.exe" -DANDROID_ABI=armeabi ..\.. cmake --build .