日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

OpenVX

發布時間:2023/12/13 综合教程 41 生活家
生活随笔 收集整理的這篇文章主要介紹了 OpenVX 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

OpenVX

openvx

1. 編譯

嘗試編譯openvx_sample,下載相關代碼。
下載的sample code直接使用make可以生成libopenvx.so。
使用python Build.py --os linux可以編譯sample code。

2. OpenVX使用流程

主要包含7個部分:

創建openvx上下文
vx_context context = vxCreateContext();
創建輸入、輸出圖像結點
vx_image input_rgb_image = vxCreateImage( context, width, height, VX_DF_IMAGE_RGB );
vx_image output_rgb_image = vxCreateImage( context, width, height, VX_DF_IMAGE_RGB );
創建graph
vx_graph graph = vxCreateGraph(context);
構建graph
vxScaleImageNode(graph, input_rgb_image, output_rgb_image, VX_INTERPOLATION_AREA)
驗證graph
vxVerifyGraph( graph );
真正運行graph
vxProcessGraph(graph);
釋放資源
vxReleaseContext(&context);

3. OpenVX中調用OpenCL代碼解析

1. vxCreateContext

一個平臺對就一個target,一個target包含多個kernel。

./sample/framework/vx_context.c中的變量定義了幾種target支持, c_model, opencl, openmp:

vx_char targetModules[][VX_MAX_TARGET_NAME] = {
    "openvx-c_model",
#if defined(EXPERIMENTAL_USE_OPENCL)
    "openvx-opencl",
#endif
#if defined(EXPERIMENTAL_USE_OPENMP)
    "openvx-openmp"
#endif
};

以OpenCL為例,當用戶調用函數vxCreateContext(sample/framework/vx_context.c)時,其會調用函數ownLoadTarget (sample/framework/vx_target.c), 去dlopen打開libopenvx-opencl.so, 使用dlsym(mod, name)獲取vxTargetInit, vxTargetAddKernel(sample/targets/opencl/vx_interface.c)等opencl的相關函數句柄。

而在vxTargetAddKernel函數中,調用ownInitializeKernel(sample/framework/vx_kernel.c)加載了所有OpenCL實現的kernel函數。

在sample/targets/opencl目錄下的c文件定義了一些vx_cl_kernel_description_t box3x3_clkernel變量,包括box3x3_clkernel, gaussian3x3_clkernel, and_kernel等 ,這些kernel

opencl kernel結構:

包含vx_kernel_description_t還有一些其它屬性,它把function置為NULL,并提供了一個sourcepath變量用來存放opencl函數。

typedef struct _vx_cl_kernel_description_t {
    vx_kernel_description_t description;
    char             sourcepath[VX_CL_MAX_PATH];
    char             kernelname[VX_MAX_KERNEL_NAME];
    cl_program       program[VX_CL_MAX_PLATFORMS];
    cl_kernel        kernels[VX_CL_MAX_PLATFORMS];
    cl_uint          num_kernels[VX_CL_MAX_PLATFORMS];
    cl_int           returns[VX_CL_MAX_PLATFORMS][VX_CL_MAX_DEVICES];
    void            *reserved; /* for additional data */
} vx_cl_kernel_description_t;

kernel結構:

typedef struct _vx_kernel_description_t {
    /*! rief The vx_kernel_e enum */
    vx_enum                 enumeration;
    /*! rief The name that kernel will be used with ef vxGetKernelByName. */
    vx_char                 name[VX_MAX_KERNEL_NAME];
    /*! rief The pointer to the function to execute the kernel */
    vx_kernel_f             function;
    /*! rief The pointer to the array of parameter descriptors */
    vx_param_description_t *parameters;
    /*! rief The number of paraemeters in the array. */
    vx_uint32               numParams;
    /*! rief The parameters validator */
    vx_kernel_validate_f    validate;
    /*! rief The input validator (deprecated  in openvx 1.1) */
    void* input_validate;
    /*! rief The output validator (deprecated in openvx 1.1) */
    void* output_validate;
    /*! rief The initialization function */
    vx_kernel_initialize_f initialize;
    /*! rief The deinitialization function */
    vx_kernel_deinitialize_f deinitialize;
} vx_kernel_description_t;

可以看到目前雖然配置了一些參數,但OpenCL分為主機端代碼和device端代碼,device端代碼在kernel/opencl中,而host端代碼在哪呢?如何根據設置的參數去執行Host端代碼,從而執行device端代碼:
可以看到在vxTargetInit函數中,調用ownInitializeKernel初始化kernel時,判斷了kfunc是否為NULL,(kfunc == NULL ? vxclCallOpenCLKernel : kfunc)如果為NULL則使用vxclCallOpenCLKernel函數。

我們再看vxclCallOpenCLKernel函數,我們發現這個函數里有clSetKernelArg,clEnqueueNDRangeKernel等OpenCL的API函數,這個便是host-side的OpenCL代碼。

2. vxScaleImageNode

在sample/framework/vx_node_api.c中定義了所有提供的可用的OpenVX結點,包括vxScaleImageNode結點,通過如下方法創建Node:

vx_kernel kernel   = vxGetKernelByEnum( context, VX_KERNEL_SCALE_IMAGE );

如果函數有兩種實現,那么按照優先級使用: opencl > openmp > c_model。(不對,感覺優先使用的是c_model的函數;實際是先找到opencl kernel,但找到之后并沒有停止查找,找到后面的c_model就會覆蓋掉前面的opencl kernel。不知道這兒是寫錯了,還是就是要優先使用c_model,代碼見sample/framework/vx_kernel.c中的vxGetKernelByEnum函數)

node的參數如何傳遞給kernel: 在vxCreateNodeByStructure中調用vxSetParameterByIndex將Node的參數傳遞kernel。

3. vxVerifyGraph

vx_graph.c會調用每一個結點的validator函數,包括inputValidator,outputValidator,確保構建的Graph可以跑通。

4. vxProcessGraph

vxProcessGraph函數調用vxExecuteGraph函數,在其中調用action = target->funcs.process(target, &node, 0, 1);,其中的funcs.process就是各個target的vxTargetProcess函數。

在vxTargetProcess中會調用nodes[n]->kernel->function,即我們事先定義的host-side端代碼,傳遞結點,參數,以及參數個數:

status = nodes[n]->kernel->function((vx_node)nodes[n], 
                                            (vx_reference *)nodes[n]->parameters,
                                             nodes[n]->kernel->signature.num_parameters);

而我們的function,則主要負責內存管理,以及調用device端代碼。

幾種參數類型:
memory:
CL_MEM_OBJECT_BUFFER
CL_MEM_OBJECT_IMAGE2D
scalar:
VX_TYPE_SCALAR
threashold:
VX_TYPE_THRESHOLD

4. OpenVX中使用OpenCL的編譯問題

使用Makefile編譯出來的so默認是沒有opencl。

使用Build.py出來的so可以有opencl,但結點報錯:
Target[1] is not valid!
Target[2] is not valid!
LOG: [ status = -17 ] Node: org.khronos.openvx.color_convert: parameter[1] is not a valid type 1280!

在target.mak中對SYSDEFS添加EXPERIMENTAL_USE_OPENCL,可以編譯Opencl,但在運行時build opencl 代碼時報錯,可以將錯誤信息打印出來,發現找不到頭文件。

查看代碼,發現在sample/targets/opencl/vx_interface.c中需要如下兩個參數,VX_CL_INCLUDE_DIR是VX頭文件位置,VX_CL_SOURCE_DIR是CL源碼位置,在環境中可以配置這兩個參數:
char *vx_incs = getenv("VX_CL_INCLUDE_DIR");
char *cl_dirs = getenv("VX_CL_SOURCE_DIR");

/usr/include/features.h:367:12: fatal error: 'sys/cdefs.h' file not found
在cl編譯命令里(sample/targets/opencl/vx_interface.c)添加-I /usr/include/x86_64-linux-gnu/:
snprintf(cl_args, sizeof(cl_args), "-D VX_CL_KERNEL -I %s -I /usr/include/x86_64-linux-gnu/ -I %s %s %s", vx_incs, cl_dirs...

Linux gnu/stubs-32.h: No such file or directory
這是缺少32位的嵌入式C庫。在嵌入式開發環境配置時,也常遇到這個問題。sudo apt-get install libc6-dev-i386

fatal error: 'stddef.h' file not found
定位stddef.h, 在cl編譯命令里cl_args里添加-I /usr/include/linux/

vx_khr_opencl.h和vx_api.h里有些類型進行了重定義:
不要在vx_khr_opencl.h里include vx_api.h。

histogram.cl仍然報錯,將histogram的kernel去掉,就可以成功編譯。

5. 使用OpenCL vx_not

使用c_model的VX_KERNEL_NOT可以正常運行,使用opencl的就會報如下錯誤:

clSetKernelArg: OpenCL error CL_INVALID_ARG_INDEX at vxclCallOpenCLKernel in /home/guru_ge/openvx/openvx_sample/sample/targets/opencl/vx_interface.c:639
clSetKernelArg: OpenCL error CL_INVALID_ARG_INDEX at vxclCallOpenCLKernel in /home/guru_ge/openvx/openvx_sample/sample/targets/opencl/vx_interface.c:639
clEnqueueNDRangeKernel: OpenCL error CL_INVALID_KERNEL_ARGS at vxclCallOpenCLKernel in /home/guru_ge/openvx/openvx_sample/sample/targets/opencl/vx_interface.c:724

嘗試自己寫host side的code。

寫完發現并在Load時就不通過,檢查原因,打開log信息,發現在自己實現的代碼中有個CL_ERROR_MSG找不到,直接注釋該行代碼,程序可以正常運行。但是得到的結果還是不對,全是黑色,好像是沒有將處理后的結果拷貝回來,導致結果全是0。

這是因為cl中提供兩種形式的表達,一個是image2d_t,一個是簡單的buffer,在vx_interface.c中編譯cl時,加上了CL_USE_LUMINANCE,使用的是image2d_t;而在編譯整個OpenVX時,沒有加上CL_USE_LUMINANCE,導致外面使用的是簡單的buffer,而一個image2d_t的參數如果使用buffer需要傳遞5個參數,所以導致最后設置參數時兩邊不一致出錯。修改concerto/target.mak在31行SYSDEFS里加上CL_USE_LUMINANCE就可以了。

雖然不報錯了,但是出來的結果居然是一條直線,而不是取反后的效果,很奇怪:


一條直線

難道是傳給opencl的圖像就不對?嘗試手動拷貝圖像數據。

嘗試學習opencl c 語法,修改代碼查看結果,發現openvx在實現opencl的時候not kernel時存在一些不規范的地方,可能這些問題在其它平臺可以運行,但到現在這個平臺上就不行了。

原來的kernel實現:

__kernel void vx_not(read_only image2d_t a, write_only image2d_t b) {
    int2 coord = (get_global_id(0), get_global_id(1));
    write_imageui(b, coord, ~read_imageui(a, nearest_clamp, coord));
}

首先我嘗試打印其像素坐標時,發現得到的x, y坐標總是相同的,這很奇怪,這也解釋了為什么結果只有一條直線,因為它只寫了x, y坐標相同的那些像素點的值。查看 API發現get_global_id返回的是size_t,所以要用(int)去顯示轉換一下,再打印時,發現坐標在不停的變換,變成正常的了。

再運行,得到的圖居然是一幅全白的圖,說明像素值還有問題。嘗試打印原像素值,與取反后的像素值,發現相加不是255,說明這里的取反操作也有問題。read_imageui返回的類型是uint4向量,我們取反時,得到的結果并不對,這里使用255直接相減,最后代碼如下所示:

__kernel void vx_not(read_only image2d_t a, write_only image2d_t b) {
    int2 coord = (int2)(get_global_id(0), get_global_id(1));
    write_imageui(b, coord, 255-read_imageui(a, nearest_clamp, coord));
}

得到的效果正確了,如下:


right_result

6. 實現OpenCL vx_scale

實現opencl scale報錯:
parameter[1] is an invalid dimension 640x240

傳遞的參數是(inputImg, outputImg, type),parameter[1]應該是輸出圖像,大小確實應該是640x240。
使用c_model中的outputvalidator就不報這個錯了,說明不能直接return VX_SUCCESS,可能validator中還需要做些其它的事情。
在validator中會記錄一些信息,以供后面verify時與實際傳入參數比對,所以不能直接返回SUCCESS:

    ptr->type = VX_TYPE_IMAGE;
    ptr->dim.image.format = VX_DF_IMAGE_U8;                         
    ptr->dim.image.width = width;                                   
    ptr->dim.image.height = height;

然而現在又報如下錯誤:
clEnqueueNDRangeKernel: OpenCL error CL_INVALID_EVENT at vxclCallOpenCLKernel in /home/guru_ge/openvx/openvx_sample/sample/targets/opencl/vx_interface.c:725
clEnqueueReadImage: OpenCL error CL_INVALID_EVENT at vxclCallOpenCLKernel in /home/guru_ge/openvx/openvx_sample/sample/targets/opencl/vx_interface.c:793

為什么event會invalid呢?嘗試自己寫host-side代碼,不使用默認的。

自己的代碼報如下錯誤:
VX_ZONE_ERROR:[vxcl_platform_notifier:59] CL_OUT_OF_RESOURCES error executing CL_COMMAND_READ_IMAGE on GeForce GTX 1080 Ti (Device 0)

spec里解釋CL_OUT_OF_RESOURCES: if there is a failure to allocate resources required by the OpenCL implementation on the device.

這估計是使用c_model的validator導致沒有初始化cl_mem,嘗試使用cl validator。

在check scale node parameter時報如下錯誤:

LOG: [ status = -10 ] Node[3] org.khronos.openvx.image_scaling: parameter[2] failed input/bi validation!

這估計是Input validator里只允許Image類型,沒有判斷scalar類型。

所以validator要對每個參數逐一判斷,對于input參數,直接返回SUCCESS就可以了;而對output參數,還需要寫一些信息。

結果還是全黑的,在kernel中打印坐標發現也不對,查看代碼發現輸入的維度是輸入圖片的大小,這兒應該是輸出圖像的大小才對。

再運行還是黑色,發現在取坐標轉換時,沒有將float轉為int,導致有問題(所以類型要確保完全一致,不會替你做轉換)。修改后,可以正常運行。

__kernel void image_scaling(read_only image2d_t in,
            write_only image2d_t out)
{
    //從glob_id中獲取目標像素坐標
    int2 coordinate = (int2)(get_global_id(0), get_global_id(1));
    //計算歸一化浮點坐標    
    float2 normalizedCoordinate = convert_float2(coordinate) * (float2)(2, 2);
    //根據歸一化坐標從原圖中讀取像素數據
    uint4 colour = read_imageui(in, sampler, convert_int2(normalizedCoordinate));
    //將像素數據寫入目標圖像    
    write_imageui(out, coordinate, colour);
}

實際比較,vx_not, vx_scale使用opencl, c_model實現時間對比:
opencl:
average time: 44099.857143 us

c_model:
average time: 68343.380952 us

7. vx debug print信息

程序中通過獲取VX_ZONE_MASK環境變量的值來設置Log級別,可以通過如下將所有級別信息都打開:
export VX_ZONE_MASK=fffff

一共有如下幾個級別,每個級別占int的一個bit位:

enum vx_debug_zone_e {
    VX_ZONE_ERROR       = 0,    /*!< Used for most errors */
    VX_ZONE_WARNING     = 1,    /*!< Used to warning developers of possible issues */
    VX_ZONE_API         = 2,    /*!< Used to trace API calls and return values */
    VX_ZONE_INFO        = 3,    /*!< Used to show run-time processing debug */

    VX_ZONE_PERF        = 4,    /*!< Used to show performance information */
    VX_ZONE_CONTEXT     = 5,
    VX_ZONE_OSAL        = 6,
    VX_ZONE_REFERENCE   = 7,

    VX_ZONE_ARRAY       = 8,
    VX_ZONE_IMAGE       = 9,
    VX_ZONE_SCALAR      = 10,
    VX_ZONE_KERNEL      = 11,

    VX_ZONE_GRAPH       = 12,
    VX_ZONE_NODE        = 13,
    VX_ZONE_PARAMETER   = 14,
    VX_ZONE_DELAY       = 15,

    VX_ZONE_TARGET      = 16,
    VX_ZONE_LOG         = 17,

    VX_ZONE_MAX         = 32
};

Ref

AMD openvx實現:

https://github.com/GPUOpen-ProfessionalCompute-Libraries/amdovx-core
https://github.com/GPUOpen-ProfessionalCompute-Libraries/amdovx-modules

總結

以上是生活随笔為你收集整理的OpenVX的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。