zoukankan      html  css  js  c++  java
  • 编译并使用带有OpenCL模块的OpenCV for android SDK

    OpenCV Android SDK中提供的静态、动态库是不支持OpenCL加速的,如果在程序中调用OpenCL相关函数,编译时不会报错,但运行时logcat会输出如下信息,提示OpenCL函数不可用

    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平台的,其他平台过程类似,只是编译时的命令会有所不同。

    1.如何判断设备是否支持OpenCL

    可以下载一款叫OpenCL-Z的软件。安装后,它可以检测当前设备的GPU是否支持OpenCL和是否具有OpenCL驱动。下面是豌豆荚上的下载地址:

    http://www.wandoujia.com/apps/com.robertwgh.opencl_z_android

    不过有的时候设备的GPU支持OpenCL但是没有安装正确的驱动,这个时候需要如下几个不住:

    A.确定设备的GPU型号,这个可以通过google,baidu搞定。

    B.通过https://www.khronos.org/conformance/adopters/conformant-products/网站查询你的GPU型号是否支持OpenCL

    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_pathmodulesoclsrccl_runtimecl_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_pathplatforms下面,执行如下命令:

    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=..androidandroid.toolchain.cmake -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%prebuiltwindows-x86_64inmake.exe" -DANDROID_ABI=armeabi ....

    其中,ANDROID_NDK是在环境变量中设置好的变量值,为android ndk的根目录,DANDROID_ABI可以根据需要选择。执行完毕后,执行如下命令即可:

    cmake --build .

    注意build后面的那个“.”不能少。

    (5)编译完成后,把uild_opencllibarmeabi下面的库覆盖到opencv android sdk中的sdk ativelibs目录下对应的文件夹。

    (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.modulescoresrcopencl untimeopencl_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
    "
    #define ERROR_MSG_INVALID_VERSION "Failed to load OpenCL runtime (expected version 1.1+)
    "
    
    #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=..androidandroid.toolchain.cmake -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%prebuiltwindows-x86_64inmake.exe" -DANDROID_ABI=armeabi ....
    cmake --build .
  • 相关阅读:
    ==和equals
    instanceof和相关函数
    格式化输出
    [转]使用String的intern方法节省内存
    [转]请别再拿“String s = new String("xyz");创建了多少个String实例”来面试了吧
    Go编程语言学习笔记
    [javascript]什么是闭包?
    [javascript]彻底理解 JS 中 this 的指向
    constrained属性
    Python python 数据类型的相互转换
  • 原文地址:https://www.cnblogs.com/hrlnw/p/4720977.html
Copyright © 2011-2022 走看看