VPI - Vision Programming Interface

3.2 版本

面向 aarch64 的交叉编译

概述

本示例展示了如何在 x86_64 主机中构建应用程序,目标设备为使用 aarch64 架构的 Jetson 设备。它使用了 cmake-3.5 中首次提供的几个特性。示例应用程序本身会创建一个输入图像,对其应用盒状滤波器,并将结果保存到磁盘。

说明

JetPack 的安装程序已经使用 gcc 设置了交叉编译工具链,但如果由于某些原因它不可用,请使用以下命令手动安装:

apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu

现在可以指示 cmake 通过在 samples 目录中调用以下命令来创建交叉编译构建树

cmake . -DCMAKE_TOOLCHAIN_FILE=Toolchain_aarch64_l4t.cmake

文件 Toolchain_aarch64_l4t.cmake 包含在 samples 目录中,并定义了将要使用的交叉编译器以及其他配置。特别是,它还允许 CUDA 应用程序的交叉编译,前提是 CUDA aarch64 交叉编译库已正确安装在主机上。

注意
此示例也可以编译为目标主机。只需在 cmake 调用期间省略 CMAKE_TOOLCHAIN_FILE 参数即可。

用法是

./vpi_sample_08_cross_aarch64_l4t <后端>

其中

  • backend:cpucudapva 之一;它定义了将执行处理的后端。

源代码

为了方便起见,这里提供了也安装在 samples 目录中的代码。

语言
29 #include <vpi/Image.h>
30 #include <vpi/Status.h>
31 #include <vpi/Stream.h>
32 #include <vpi/algo/BoxFilter.h>
33 
34 #include <cstring> // for memset
35 #include <fstream>
36 #include <iostream>
37 #include <sstream>
38 
39 #define CHECK_STATUS(STMT) \
40  do \
41  { \
42  VPIStatus status = (STMT); \
43  if (status != VPI_SUCCESS) \
44  { \
45  char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
46  vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
47  std::ostringstream ss; \
48  ss << vpiStatusGetName(status) << ": " << buffer; \
49  throw std::runtime_error(ss.str()); \
50  } \
51  } while (0);
52 
53 int main(int argc, char *argv[])
54 {
55  // VPI objects that will be used
56  VPIImage image = NULL;
57  VPIImage blurred = NULL;
58  VPIStream stream = NULL;
59 
60  int retval = 0;
61 
62  try
63  {
64  if (argc != 2)
65  {
66  throw std::runtime_error(std::string("Usage: ") + argv[0] + " <cpu|pva|cuda>");
67  }
68 
69  std::string strBackend = argv[1];
70 
71  // Now parse the backend
72  VPIBackend backend;
73 
74  if (strBackend == "cpu")
75  {
76  backend = VPI_BACKEND_CPU;
77  }
78  else if (strBackend == "cuda")
79  {
80  backend = VPI_BACKEND_CUDA;
81  }
82  else if (strBackend == "pva")
83  {
84  backend = VPI_BACKEND_PVA;
85  }
86  else
87  {
88  throw std::runtime_error("Backend '" + strBackend +
89  "' not recognized, it must be either cpu, cuda or pva.");
90  }
91 
92  // Create the stream for the given backend.
93  CHECK_STATUS(vpiStreamCreate(backend, &stream));
94 
95  char imgContents[512][512];
96  for (int i = 0; i < 512; ++i)
97  {
98  for (int j = 0; j < 512; ++j)
99  {
100  imgContents[i][j] = i * 512 + j + i;
101  }
102  }
103 
104  // We now wrap the loaded image into a VPIImage object to be used by VPI.
105  {
106  // First fill VPIImageBufferPitchLinear with the, well, image data...
107  VPIImageData imgData;
108  memset(&imgData, 0, sizeof(imgData));
111  imgData.buffer.pitch.numPlanes = 1;
112  imgData.buffer.pitch.planes[0].width = 512;
113  imgData.buffer.pitch.planes[0].height = 512;
114  imgData.buffer.pitch.planes[0].pitchBytes = 512;
115  imgData.buffer.pitch.planes[0].data = imgContents[0];
116 
117  // Wrap it into a VPIImage. VPI won't make a copy of it, so the original
118  // image must be in scope at all times.
119  CHECK_STATUS(vpiImageCreateWrapper(&imgData, nullptr, 0, &image));
120  }
121 
122  // Now create the output image, single unsigned 8-bit channel.
123  CHECK_STATUS(vpiImageCreate(512, 512, VPI_IMAGE_FORMAT_U8, 0, &blurred));
124 
125  // Submit it for processing passing the image to be blurred and the result image
126  CHECK_STATUS(vpiSubmitBoxFilter(stream, backend, image, blurred, 3, 3, VPI_BORDER_ZERO));
127 
128  // Wait until the algorithm finishes processing
129  CHECK_STATUS(vpiStreamSync(stream));
130 
131  // Now let's retrieve the output image contents and output it to disk
132  {
133  // Lock output image to retrieve its data on cpu memory
134  VPIImageData outData;
135  CHECK_STATUS(vpiImageLockData(blurred, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData));
136 
138  VPIImageBufferPitchLinear &outPitch = outData.buffer.pitch;
139 
140  std::ofstream fd(("boxfiltered_" + strBackend + ".pgm").c_str());
141 
142  fd << "P5\n512 512 255\n";
143  for (int i = 0; i < 512; ++i)
144  {
145  fd.write(reinterpret_cast<const char *>(outPitch.planes[0].data) + outPitch.planes[0].pitchBytes * i,
146  512);
147  }
148  fd.close();
149 
150  // Done handling output image, don't forget to unlock it.
151  CHECK_STATUS(vpiImageUnlock(blurred));
152  }
153  }
154  catch (std::exception &e)
155  {
156  std::cerr << e.what() << std::endl;
157  retval = 1;
158  }
159 
160  // Clean up
161 
162  // Make sure stream is synchronized before destroying the objects
163  // that might still be in use.
164  if (stream != NULL)
165  {
166  vpiStreamSync(stream);
167  }
168 
169  vpiImageDestroy(image);
170  vpiImageDestroy(blurred);
171  vpiStreamDestroy(stream);
172 
173  return retval;
174 }
声明实现盒状滤波器算法的函数。
#define VPI_IMAGE_FORMAT_U8
单平面,带有一个 8 位无符号整数通道。
Definition: ImageFormat.h:100
用于处理 VPI 图像的函数和结构。
VPI 状态代码处理函数的声明。
声明处理 VPI 流的函数。
VPIStatus vpiSubmitBoxFilter(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, int32_t kernelWidth, int32_t kernelHeight, VPIBorderExtension border)
在图像上运行 2D 盒状滤波器。
VPIImageBuffer buffer
存储图像内容。
Definition: Image.h:241
VPIImagePlanePitchLinear planes[VPI_MAX_PLANE_COUNT]
所有图像平面在 pitch-linear 布局中的数据。
Definition: Image.h:160
VPIImageBufferPitchLinear pitch
以 pitch-linear 布局存储的图像。
Definition: Image.h:210
void * data
指向此平面的第一行的指针。
Definition: Image.h:141
int32_t numPlanes
平面数量。
Definition: Image.h:156
VPIImageFormat format
图像格式。
Definition: Image.h:152
VPIImageBufferType bufferType
图像缓冲区类型。
Definition: Image.h:238
int32_t height
此平面的高度(像素)。
Definition: Image.h:123
int32_t width
此平面的宽度(像素)。
Definition: Image.h:119
int32_t pitchBytes
一行的开头与前一行的开头之间的字节差。
Definition: Image.h:134
void vpiImageDestroy(VPIImage img)
销毁图像实例。
struct VPIImageImpl * VPIImage
图像的句柄。
Definition: Types.h:256
VPIStatus vpiImageCreateWrapper(const VPIImageData *data, const VPIImageWrapperParams *params, uint64_t flags, VPIImage *img)
通过包装现有内存块创建图像对象。
VPIStatus vpiImageLockData(VPIImage img, VPILockMode mode, VPIImageBufferType bufType, VPIImageData *data)
获取图像对象的锁并返回图像内容。
VPIStatus vpiImageCreate(int32_t width, int32_t height, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
使用指定的标志创建空的图像实例。
VPIStatus vpiImageUnlock(VPIImage img)
释放图像对象上的锁。
@ VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR
主机可访问,平面采用 pitch-linear 内存布局。
Definition: Image.h:172
存储图像平面内容。
Definition: Image.h:150
存储有关图像特征和内容的信息。
Definition: Image.h:234
struct VPIStreamImpl * VPIStream
流的句柄。
Definition: Types.h:250
VPIStatus vpiStreamSync(VPIStream stream)
阻塞调用线程,直到此流队列中所有提交的命令都完成(队列为空)...
VPIBackend
VPI 后端类型。
Definition: Types.h:91
void vpiStreamDestroy(VPIStream stream)
销毁流实例并释放所有硬件资源。
VPIStatus vpiStreamCreate(uint64_t flags, VPIStream *stream)
创建流实例。
@ VPI_BACKEND_CUDA
CUDA 后端。
Definition: Types.h:93
@ VPI_BACKEND_PVA
PVA 后端。
Definition: Types.h:94
@ VPI_BACKEND_CPU
CPU 后端。
Definition: Types.h:92
@ VPI_BORDER_ZERO
图像外部的所有像素均被视为零。
Definition: Types.h:278
@ VPI_LOCK_READ
仅锁定内存以进行读取。
Definition: Types.h:617

这是正在使用的 cmake 工具链文件。

27 set(CMAKE_SYSTEM_NAME Linux)
28 set(CMAKE_SYSTEM_PROCESSOR aarch64)
29 
30 set(target_arch aarch64-linux-gnu)
31 set(CMAKE_LIBRARY_ARCHITECTURE ${target_arch} CACHE STRING "" FORCE)
32 
33 # 配置 cmake 以查找库、包含目录和
34 # 目标根前缀内的软件包。
35 set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
36 set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
37 set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
38 set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
39 set(CMAKE_FIND_ROOT_PATH "/usr/${target_arch}")
40 
41 # 需要避免执行一些更严格的编译器检查,这些检查在
42 # 交叉编译时会失败
43 set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
44 
45 # 指定工具链程序
46 find_program(CMAKE_C_COMPILER ${target_arch}-gcc)
47 find_program(CMAKE_CXX_COMPILER ${target_arch}-g++)
48 if(NOT CMAKE_C_COMPILER OR NOT CMAKE_CXX_COMPILER)
49  message(FATAL_ERROR "找不到适用于 ${target_arch} 的合适的 C/C++ 交叉编译器")
50 endif()
51 
52 set(CMAKE_AR ${target_arch}-ar CACHE FILEPATH "" FORCE)
53 set(CMAKE_RANLIB ${target_arch}-ranlib)
54 set(CMAKE_LINKER ${target_arch}-ld)
55 
56 # 并非所有共享库依赖项都安装在主机上。
57 # 确保链接器不会报错。
58 set(CMAKE_EXE_LINKER_FLAGS_INIT -Wl,--allow-shlib-undefined)
59 
60 # 指示 nvcc 使用我们的交叉编译器
61 set(CMAKE_CUDA_FLAGS "-ccbin ${CMAKE_CXX_COMPILER} -Xcompiler -fPIC" CACHE STRING "" FORCE)

最后是随附的 CMakeLists.txt。请注意,它只是一个简单的 CMakeLists.txt。与交叉编译相关的所有内容都在上面的工具链文件中定义。

27 cmake_minimum_required(VERSION 3.5)
28 
29 # 要从 x86 为 aarch64-l4t 目标进行交叉编译,
30 # 请将 -DCMAKE_TOOLCHAIN_FILE=Toolchain_aarch64_l4t.cmake 传递给
31 # cmake 以创建构建树。
32 
33 project(vpi_sample_08_cross_aarch64_l4t)
34 
35 find_package(vpi ${vpi_API_VERSION} REQUIRED)
36 
37 add_executable(${PROJECT_NAME} main.cpp)
38 target_link_libraries(${PROJECT_NAME} vpi)