OpenCV&OpenCL宏观预览

版权声明:免责声明: 本人在此发文(包括但不限于汉字、拼音、拉丁字母)均为随意敲击键盘所出,用于检验本人电脑键盘录入、屏幕显示的机械、光电性能,并不代表本人局部或全部同意、支持或者反对观点。如需要详查请直接与键盘生产厂商法人代表联系 .挖井挑水无水表,不会网购无快递 https://blog.csdn.net/jscese/article/details/51745250

撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/51745250本文来自 【jscese】的博客!

概念

OpenCV :Open Source Computer Vision Library
通俗理解为 计算机视图处理的一层封装库,可服务于多个平台的开源c.c++框架
OpenCL :Open Computing Language
类似于前文提到过的OpenGL ,为同一组织维护的标准接口框架,用在调度CPU GPU协作,利用起GPU的资源进行并行计算,提高性能
不同GPU厂商根据OpenCL的标准头文件实现底层逻辑


源代码

OpenCV
关注 linux 版本 以及 android-sdk版本 ,网址:http://opencv.org/downloads.html
linux 版本含所有源码,可自己编译整个opencv ,拿需要的module
android-sdk版本是现成的so,含有头文件, 可直接拿来用

OpenCL
标准框架以及头文件可通过 https://www.khronos.org/opencl/获取
但是真正的实现,是由各GPU厂商根据框架的头文件来做,要细究开发,需要对应的驱动代码


编译

OpenCV
在ubuntu下编译linux版本,目标平台选为android
为android编译动态库,少不了编译工具链,配置NDK :
export ANDROID_NDK=tool/android-ndk-r10 ,NDK的根目录,网上有人说什么一般NDK不行,需要什么crystal版的NDK,扯淡~

因为OpenCV 跨平台,所以编译系统采用了cmake(cross platform make),通俗理解:可根据平台不同 适配成对应的编译环境
linux下自然是纯正的Makefile-gcc-g++ , 所以ubuntu下要编译opencv ,得先安装好cmake,cmake –version 查看版本
我下载的最新2.4.13,编译步骤记录如下:
1:运行~/opencv-2.4.13/platforms/scripts/cmake_android_arm.sh 脚本内容如下:

#!/bin/sh
cd `dirname $0`/..

mkdir -p build_android_arm
cd build_android_arm

cmake -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DBUILD_SHARED_LIBS=ON -DWITH_OPENCL=ON -DCMAKE_TOOLCHAIN_FILE=../android/android.toolchain.cmake $@ ../..

很明显,通过cmake 进行配置编译环境,android.toolchain.cmake 为核心逻辑

2:到build_android_arm 目录下 make ,就开始全编译,最终会在这个目录下生成对应的lib 以及 测试bin之类的,makefile 环境也同样在这个目录下
make install 会生成install/sdk 目录,含有对应so以及头文件

注意事项:
android L版本后,可执行文件需要PIE,可查看android.toolchain.cmake中有这么一段:

# pie/pic   
if( NOT (ANDROID_NATIVE_API_LEVEL LESS 16) AND (NOT DEFINED ANDROID_APP_PIE OR ANDROID_APP_PIE) AND (CMAKE_VERSION VERSION_GREATER 2.8.8))
 set( CMAKE_POSITION_INDEPENDENT_CODE TRUE )
 set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie")
else()
 set( CMAKE_POSITION_INDEPENDENT_CODE FALSE )
 set( CMAKE_CXX_FLAGS "-fpic ${CMAKE_CXX_FLAGS}" )
 set( CMAKE_C_FLAGS   "-fpic ${CMAKE_C_FLAGS}" )
endif()

没有PIE,编译出来的测试用例bin ,在android上跑不了

OpenCL
各个实现平台编译手段不同,没有参考价值,不记录了


框架实现

OpenCV: 上面有说到,实为处理封装库,主要是一些复杂的矩阵变换,以及图形处理算法的集合,模块化清晰,有需求可以针对性的研究其中的某一个模块处理流程。

OpenCL :原理架构可参考OpenCL 原理架构
软件层面-根据OpenCL的标准接口头文件 cl.h 实现逻辑大体如下:
选择opencl框架下的platform device
platform 指接口实现平台, 也就是gpu的平台
device 指选取进行计算的设备(cpu 或者 gpu),当前准备这些的 当做 host

创建device上面的content 和 memory, 当前host与这个device属于同一content,同content才能进行数据交互
会有从当前内存 copy 数据到 devices上memory的操作,如device为gpu 即为显存

需要编写 devices上运行的程序,类似于opengl 的shader编程, 这里由opencl 往下解析运行在devices上的操作, 统称 program 程序
program 程序需要动态编译,可以得到抽象的 kernel 函数,可理解为入口函数,设置这个kernel函数的参数,host, device两端所准备的数据资源等

为device创建commandqueue,用来存放 kernel

调用clEnqueueNDRangeKernel 接口将kernel加入到commandqueue中,device会根据顺序执行当前queue中的kernel
执行之后的结果存放在 device 上,可通过clEnqueueReadBuffer 接口读回host,也就是我们当前cpu程序

host 在加入kernel之后 就与device出于异步执行状态,所以某些情况下还需要 创建event 用来同步


OpenCV调用OpenCL

这篇博客将cv和cl放一起来概述,原因就是cv和cl可以一起协作,通过cl的加速在图形的处理上达到最优表现

适配

目前OpenCV中已经兼容支持了OpenCL ,当做了一个子模块,/opencv-2.4.13/module/ocl ,需要进行一些适配如下:

可以看到上面 编译步骤的第一个执行脚本cmake_android_arm.sh 中需要传入参数:

-DBUILD_SHARED_LIBS=ON  -DWITH_OPENCL=ON

/opencv-2.4.13/module/ocl/src/cl_runtime/cl_runtime.cpp中添加动态库加载的路径 返回CV_CL_GET_PROC_ADDRESS(name)
可看到适配了有 APPLE ,WIN ,LINUX 的,需要添加ANDROID的GetProcAddress ,可参考LINUX的:

#if defined(__linux__)
    #include <dlfcn.h>
    #include <stdio.h>

    static void* GetProcAddress (const char* name)
    {
        static void* h = NULL;
        if (!h)
        {
            const char* name = "libOpenCL.so";
            const char* envOpenCLBinary = getenv("OPENCV_OPENCL_BINARY");
            if (envOpenCLBinary)
                name = envOpenCLBinary;
            h = dlopen(name, RTLD_LAZY | RTLD_GLOBAL);
            if (!h)
                return NULL;
        }

        return dlsym(h, name);
    }
    #define CV_CL_GET_PROC_ADDRESS(name) GetProcAddress(name)
#endif

对应加一段 #if defined(ANDROID) … #endif 中间的加载so路径名称 视具体平台而定

大体流程

ocl的program-kernel程序代码 目录:opencv-2.4.13\platforms\build_android_arm\modules\ocl\opencl_kernels.cpp
从编译上来看 是依赖于opencv的算法
动态编译为opencl的kernel函数:\opencv-2.4.13\modules\ocl\src\cl_operations.cpp 中的 openCLGetKernelFromSource:

kernel = clCreateKernel(program, kernelName.c_str(), &status);

编译好kernel之后,调用openCLExecuteKernel 加入到opencl的commandqueue中:

clEnqueueNDRangeKernel(getClCommandQueue(ctx), kernel, 3, NULL, globalThreads,
                                          localThreads, 0, NULL, NULL)

opencl的标准接口,其中的3代表维度,globalThreads 代表需要处理的数据资源线程数,
localThreads 代表opencl设备并行计算的线程数-受限于CL_DEVICE_MAX_WORK_ITEM_SIZES
性能主要由globalThreads 以及 localThreads 大小决定

可查看OpenCL标准cl.h中的:

/* cl_device_info */
#define CL_DEVICE_TYPE                              0x1000
#define CL_DEVICE_VENDOR_ID                         0x1001
#define CL_DEVICE_MAX_COMPUTE_UNITS                 0x1002
#define CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS          0x1003 //支持维度   
#define CL_DEVICE_MAX_WORK_GROUP_SIZE               0x1004 //一个work group上 work item的最大数量 = MAX_WORK_ITEM X*Y*Z
#define CL_DEVICE_MAX_WORK_ITEM_SIZES               0x1005 //维度上 work item最大数

这些可以调OpenCL 标准接口,从GPU驱动CL实现中获取,是作为编写OpenCL 的program-kernel程序的重要依据

附一个stack overflow:http://stackoverflow.com/questions/7996537/cl-invalid-work-group-size-error
其中有这么一段话,可以帮助理解参数含义关系:

Ok, let me give you a 1D example. Let's say you have 10,000 things you want to process with your CL kernel. 
That's your global size: 10,000. Perhaps you want to process these in groups of 100 things. 
That's your local size: 100. Because 10,000/100 == 100, this particular arrangement will give you 100 workgroups.
Above, you have a global size of 16, and a local size of 128. This doesn't make sense for OpenCL -- 16 / 128.
Perhaps you wanted 16*128 as your global size, and 128 as your local size? That would yield 16 workgroups of 128 items each

android上的移植

在模块Android.mk中添加如下:

LOCAL_SHARED_LIBRARIES += \
    libopencv_ocl \
    libopencv_core \
    libstlport


LOCAL_C_INCLUDES +=  \
    opencv2 \
    bionic \
    bionic/libstdc++/include \
    external/stlport/stlport    

libopencv_core . libopencv_ocl 这两个上面编译步骤lib下的库
core为OpenCV的核心,为必须的,这里用到OpenCL 就只要拿ocl这个module对应的库libopencv_ocl 即可
通过添加prebuilt编译copy到/system/lib即可

opencv2 头文件夹可又上面编译步骤说到的 make install 得到的sdk中拷贝到这里

还需要添加:

LOCAL_CFLAGS += -Wno-error=non-virtual-dtor

加上这个,虚函数的类没有析构函数 不当做error停止
android编译系统默认的Werror:

./build/core/config.mk:129:TARGET_ERROR_FLAGS := -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point

Android.mk中添加的LOCAL_CFLAGS是可以覆盖的
因为opencv2/core/core.hpp里面的_InputArray类析构函数并没有实现:

#ifdef OPENCV_CAN_BREAK_BINARY_COMPATIBILITY
    virtual ~_InputArray();
#endif

并没有定义该宏,而OpenCV中编译没有加Werror,并不会报错停止


OpenCV test 用例

最终生成的测试用例目录:
~/opencv-2.4.13/platforms/build_android_arm/bin/

比如opencv_perf_ocl 即为opencv调用opencl的测试用例
opencv_perf_ocl –help 查看command info:

  --perf_affinity_mask=[0]    set affinity mask for the main thread           
  --perf_force_samples=[100]  force set maximum number of samples for all     
                              tests                                           
  ...
  Test Selection:
  --gtest_list_tests
      List the names of all tests instead of running them. The name of
      TEST(Foo, Bar) is "Foo.Bar".
  --gtest_filter=POSTIVE_PATTERNS[-NEGATIVE_PATTERNS]
      Run only the tests whose name matches one of the positive patterns but
      none of the negative patterns. '?' matches any single character; '*'
      matches any substring; ':' separates two patterns.
  --gtest_param_filter=POSITIVE_PATTERNS[-NEGATIVE_PATTERNS]
      Like --gtest_filter, but applies to the test's parameter. If a
      test is not parameterized, its parameter is considered to be the
      empty string.
  --gtest_also_run_disabled_tests
      Run all disabled tests too.

Test Execution:
  --gtest_repeat=[COUNT]
      Run the tests repeatedly; use a negative count to repeat forever.
      ...

Test Output:
  --gtest_color=(yes|no|auto)
      ...

使用的是 googletest 框架,比较高大上,不怎么熟悉
–gtest_list_tests 查看所有的用例
–gtest_filter=XXX 跑我们想跑的用例
–gtest_repeat=[COUNT] 设置重复次数
–gtest_color=(yes|no|auto) 设置输出结果颜色
这几个参数比较实用

猜你喜欢

转载自blog.csdn.net/jscese/article/details/51745250
今日推荐