快速上手

本章节介绍了UCP示例包 ucp_tutorial 中VP模块的示例使用方法,以及如何配置开发环境、编译并运行示例应用代码,帮助快速上手UCP中的VP功能模块。 其主要架构如下:

VP2Structure

示例包使用

​示例包结构如下所示:

ucp_tutorial
├── deps_aarch64                         // aarch64 公共依赖目录   
│   ├── appsdk
│   ├── eigen
│   ├── fmt
│   ├── gflags
│   ├── glog
│   ├── hlog
│   ├── nlohmann
│   ├── opencv
│   ├── openssl
│   ├── protobuf
│   ├── rapidjson
│   ├── ucp                              // UCP 依赖库及头文件,包含dnn/vp/ucp/dsp开发
│   ├── uWS
│   └── zlib
├── deps_x86                             // x86仿真 公共依赖目录   
│   ├── eigen
│   ├── fmt
│   ├── gflags
│   ├── hlog
│   ├── opencv
│   └── ucp
├── dnn                                  // dnn示例
├── hpl                                  // hpl示例
├── tools                                // 工具
└── vp                                   // vp示例
    ├── code                             // 示例源码目录
    │   ├── 01_basic_processing          // 基础图像处理示例
    │   ├── 02_transformation            // 图像变换示例
    │   ├── 03_feature_extraction        // 图像特征提取示例
    │   ├── 04_optical_flow              // 光流处理示例
    │   ├── 05_avm                       // 环视图像拼接示例
    │   ├── 06_codec                     // 视频编解码示例
    │   ├── build_aarch64.sh             // aarch64示例编译脚本
    │   ├── build.sh                     // 编译vp示例最小可执行环境脚本
    │   ├── build_x86.sh                 // x86仿真示例编译脚本
    │   └── CMakeLists.txt               // cmake文件
    └── vp_samples                       // vp示例最小可执行环境
        ├── data                         // 图像数据目录
        ├── script                       // aarch64示例脚本目录
        │   ├── 01_basic_processing      // 基础图像处理示例脚本目录
        │   ├── 02_transformation        // 图像变换示例脚本目录
        │   ├── 03_feature_extraction    // 图像特征提取示例脚本目录
        │   ├── 04_optical_flow          // 光流处理示例脚本目录
        │   ├── 05_avm                   // 环视图像拼接示例脚本目录
        │   ├── 06_codec                 // 视频编解码示例脚本目录
        |   ├── dsp_deploy.sh            // 部署板上的dsp运行环境脚本
        │   └── README.md                // readme文件
        └── script_x86                   // x86仿真示例脚本目录
            ├── 01_basic_processing      // 基础图像处理示例脚本目录
            ├── 02_transformation        // 图像变换示例脚本目录
            ├── 03_feature_extraction    // 图像特征提取示例脚本目录
            ├── 04_optical_flow          // 光流处理示例脚本目录
            ├── 05_avm                   // 环视图像拼接示例脚本目录
            ├── 06_codec                 // 视频编解码示例脚本目录
            └── README.md                // readme文件

其中 ucp_tutorial/vp 文件夹为VP模块的示例,包括了基础图像处理示例、图像变换示例、图片特征提取示例、光流示例、环视图像拼接示例、视频编解码示例,可同时支持板端运行编译和x86仿真编译两种方式。 具体的示例原理及实现流程请查阅 示例 章节。

编译示例算子

在编译运行示例应用代码前,您需要确保环境满足要求,根据 环境部署 部分的指引,您的开发机中应该已经安装有相关环境,要求如下:

  • cmake >= 3.0。
  • 对于板端编译需要指定交叉编译工具链,对于x86仿真docker自带编译器即可。

在示例的 ucp_tutorial/vp/code 目录下有预先配置好的编译脚本 build.sh,选项 -a x86-a aarch64 分别支持两种编译方式, 直接执行 build.sh 脚本即可完成一键编译,生成的文件会被保存到 ucp_tutorial/vp/vp_samples 目录下。 此外,目录中也包含了 build_aarch64.shbuild_x86.sh 两个编译脚本,分别对应了两个编译选项, 使用这两个脚本进行编译与使用 build.sh 脚本等效。 编译板端运行的VP模块需要执行的命令如下:

cd ucp_tutorial/vp/code
sh build_aarch64.sh

执行编译脚本后,会生成运行示例所需要的执行程序和依赖文件,保存在 ucp_tutorial/vp/vp_samples 目录下。

以VP为例,其生成物如下所示,包含图片数据、示例运行脚本、运行依赖库、可执行文件以及运行示例的脚本目录,共同组成了完整的运行环境和运行依赖。

vp_samples
├── data                                 // 图片数据目录
└── script                               // aarch64示例脚本目录
    ├── 01_basic_processing              // 基础图像处理示例目录
    │   ├── README.md
    │   └── run_image_process.sh         // 基础图像处理示例脚本
    ├── 02_transformation                // 图像变换示例目录
    │   ├── README.md
    │   └── run_transformation.sh        // 图像变换示例脚本
    ├── 03_feature_extraction            // 图像特征提取示例目录
    │   ├── README.md
    │   └── run_feature_extraction.sh    // 图像特征提取示例脚本
    ├── 04_optical_flow                  // 光流处理示例目录
    │   ├── README.md
    │   └── run_optical_flow.sh          // 光流处理示例脚本
    ├── 05_avm                           // 环视图像拼接示例目录
    │   ├── README.md
    │   └── run_avm.sh                   // 环视图像拼接示例脚本
    ├── 06_codec                         // 视频编解码示例目录
    │   ├── README.md
    │   └── run_codec.sh                 // 视频编解码示例脚本
    ├── aarch64                          // 示例可执行文件、依赖库目录
    ├── README.md                        // readme文件
    └── dsp_deploy.sh                    // 部署板上的dsp运行环境脚本

运行示例

正确地完成编译的全部步骤后,可执行示例会被配置完毕并保存在 vp_samples 文件夹内。下面根据执行环境不同,介绍上板与仿真两种运行示例的方式。

上板运行

vp_samples 文件夹整个拷贝到开发板上,进入 vp_samples/script 文件夹,直接执行示例文件夹中提供的运行脚本即可看到示例的运行结果。 开发板上执行脚本的参考指令如下:

cd vp_samples

cd script

# 由于部分算子依赖dsp后端,因此需要刷新dsp镜像,此操作可选,对于不需要dsp执行或者x86仿真可跳过
sh dsp_deploy.sh

# 运行基础图像处理示例
cd 01_basic_processing
sh run_image_process.sh 

x86仿真运行

进入 vp_samples/script_x86 文件夹,直接执行示例文件夹中提供的运行脚本即可看到示例的运行结果。 执行脚本的参考指令如下:

cd vp_samples

cd script_x86

# 运行基础图像处理示例
cd 01_basic_processing
sh run_image_process.sh 
注解

地平线J6 SOC使用的是Cadence公司的Tensilica Vision Q8 DSP,因此x86仿真实例中dsp算子运行依赖Cadence提供的一套工具链,环境配置可参考 DSP工具链安装 的指引, 需要正确配置 License 以及环境变量 XTENSA_ROOT 即可。

输出物说明

以板端运行为例,示例运行时会在控制台内打印流程日志并生成对应输出文件。日志中会包含全部算子的调用流程,输出结果会被保存在 script/01_basic_processing 文件夹中。示例部分输出如下:

root@j6dvb:/userdata/user/ucp_sample/vp_samples/script/01_basic_processing# sh run_basic_processing.sh
[I][3037][08-24][15:56:35:392][basic_image_process.cpp:527][basic_process_sample][BASIC_VP_PROCESS] Basic image process begin
[I][3037][08-24][15:56:35:396][basic_image_process.cpp:537][basic_process_sample][BASIC_VP_PROCESS] Read image ../../data/images/input_noise_image.jpg success
[I][3037][08-24][15:56:35:396][basic_image_process.cpp:204][basic_process_sample][BASIC_VP_PROCESS] sep filter2D operate start.
[UCP]: log level = 3
[UCP]: UCP version = 1.0.0
[VP]: log level = 3
[DNN]: log level = 3
[DSP] DSP version = 0.3.9
[DSP]: log level = 3
DSP direct client mode
[I][3037][08-24][15:56:35:408][basic_image_process.cpp:299][basic_process_sample][BASIC_VP_PROCESS] dilate operate start.
[I][3037][08-24][15:56:35:409][basic_image_process.cpp:345][basic_process_sample][BASIC_VP_PROCESS] erode operate start.
[I][3037][08-24][15:56:35:414][basic_image_process.cpp:487][basic_process_sample][BASIC_VP_PROCESS] equalizeHist operate start.
[I][3037][08-24][15:56:35:420][basic_image_process.cpp:643][basic_process_sample][BASIC_VP_PROCESS] Basic image process finish

生成物会被保存到 vp_samples/script/01_basic_processing 目录下,内容如下:

root@j6dvb:/userdata/user/ucp_sample/vp_samples/script/01_basic_processing# ls
README.md   equalizeHist_image.jpg    filtered_image.jpg      morphology_image.jpg    run_basic_processing.sh

VP算子使用示例

本节通过一个简单的算子调用展示了如何使用VP封装的算子实现图片处理的功能。主要步骤包含了图片载入、任务创建、任务提交、任务完成、销毁任务、保存输出等。 您可以阅读相应源码和注释进行学习。

该示例的作用为使用hbVPRotate算子将图片旋转90度,具体实现如下:

#include <cstring>

#include "img_util.h"
#include "log_util.h"
#include "opencv2/opencv.hpp"
#include "hobot/vp/hb_vp_rotate.h"

// Fill the operator parameter
int8_t rotate_code = HB_VP_ROTATE_90_CLOCKWISE;

// Read the input image
std::string src_img = "../../data/images/input.jpg";
cv::Mat src_mat = cv::imread(src_img.c_str(), cv::IMREAD_GRAYSCALE);
LOGE_AND_RETURN_IF(src_mat.empty(), HB_UCP_INVALID_ARGUMENT,
                   "Read image {} failed", src_img.c_str());
LOGI("Read image {} success", src_img);

const int32_t src_width = src_mat.cols;
const int32_t src_height = src_mat.rows;
const int32_t src_stride = src_width;
const int32_t dst_width = src_height;
const int32_t dst_height = src_width;
const int32_t dst_stride = dst_width;

// Initialize the input and output memory and fill the input memory with images
hbUCPSysMem src_mem, dst_mem;
hbUCPMallocCached(&src_mem, src_stride * src_height, 0);
hbUCPMallocCached(&dst_mem, dst_stride * dst_height, 0);
memcpy(src_mem.virAddr, src_mat.data, src_stride * src_height);
hbUCPMemFlush(&src_mem, HB_SYS_MEM_CACHE_CLEAN);

// Fill the input image information
hbVPImage src{HB_VP_IMAGE_FORMAT_Y,
              HB_VP_IMAGE_TYPE_U8C1,
              src_width,
              src_height,
              src_stride,
              src_mem.virAddr,
              src_mem.phyAddr,
              0,
              0,
              0};

// Fill the output image information
hbVPImage dst{HB_VP_IMAGE_FORMAT_Y,
              HB_VP_IMAGE_TYPE_U8C1,
              dst_height,
              dst_width,
              dst_stride,
              dst_mem.virAddr,
              dst_mem.phyAddr,
              0,
              0,
              0};

// Create a task through the operator interface provided by VP, the task handle can be set to nullptr, 
// and this task will be executed in synchronized mode
hbUCPTaskHandle_t rotate_task{nullptr}; // UCP task handle
hbVPRotate(&rotate_task/*task handle*/,
           &dst/*output image*/, 
           &src/*input image*/, 
           rotate_code/*operator parameter*/);

// Set scheduling parameters to adjust task priority, select execution terminals etc
hbUCPSchedParam sched_param;
HB_UCP_INITIALIZE_SCHED_PARAM(&sched_param);
sched_param.backend = HB_UCP_DSP_CORE_0; /*Specify the execution device ID*/

// Submit the task
hbUCPSubmitTask(rotate_task, &sched_param);

// Wait for the task to complete, set the timeout parameter, a value of 0 means to wait all the time
hbUCPWaitTaskDone(rotate_task, 500);

// Release the task handle
hbUCPReleaseTask(rotate_task);

// Release the memory resource
hbUCPFree(&src_mem);
hbUCPFree(&dst_mem);