VPI - 视觉编程接口

3.2 版本

图像视图

概述

图像视图应用程序将输入图像分割成 4 个视图,以 2x2 网格排列,对每个图像视图进行不同的处理,可能并行处理并使用不同的后端。处理过程消耗相同的输入父图像,并生成单个输出,将结果作为图像文件保存在磁盘上。您可以定义要处理的输入图像。每个处理使用的后端,以及每个算法及其参数,都固定如下:

  1. CPU 双边滤波
  2. CPU 水平方向图像翻转
  3. CUDA 垂直方向图像翻转
  4. CUDA 双方向图像翻转

说明

命令行参数为:

<输入图像>

其中

  • 输入图像:输入图像文件名;它接受 png、jpeg 以及可能的其他格式。

这是一个示例

  • C++
    ./vpi_sample_15_image_view ../assets/kodim08.png
  • Python
    python3 main.py ../assets/kodim08.png

这使用了提供的示例图像之一。您可以尝试其他图像,但需遵守在每个图像视图上运行的每种算法施加的约束。

结果

输入图像输出图像

源代码

为方便起见,以下是在 samples 目录中也安装的代码。

语言
27 import sys
28 import vpi
29 import numpy as np
30 from PIL import Image
31 from argparse import ArgumentParser
32 
33 # Parse command line arguments
34 parser = ArgumentParser()
35 parser.add_argument('input', help='Image to be used as input')
36 
37 args = parser.parse_args();
38 
39 # Load input parent image into a vpi.Image
40 try
41  input = vpi.asimage(np.asarray(Image.open(args.input)))
42 except IOError
43  sys.exit("Input file not found")
44 except
45  sys.exit("Error with input file")
46 
47 # Create 4 streams for independent processing
48 streams = []
49 for _ in range(4)
50  streams.append(vpi.Stream())
51 
52 # Create input parent image and output as grayscale images in the CPU using numpy
53 np_parent = np.zeros((input.height, input.width), np.uint8)
54 np_output = np.zeros((input.height, input.width), np.uint8)
55 
56 # Wrap the input parent and output using VPI, this is important
57 # because views on Tegra devices can only be created on wrapped CPU or
58 # CUDA buffers. If an image is created with vpi.Image, and there is a
59 # different backend available, the view cannot be created.
60 parent = vpi.asimage(np_parent)
61 output = vpi.asimage(np_output)
62 
63 # Convert input parent image to grayscale
64 with vpi.Backend.CPU, streams[0]
65  input.convert(out=parent)
66 
67 # Calculate views size: width x height
68 viewSize = (input.width // 2, input.height // 2)
69 
70 # Define clip bounds for each view
71 clipBounds = vpi.RectangleI(0, 0, viewSize[0], viewSize[1])
72 
73 # Create 4 input and output child image views
74 parViews = []
75 outViews = []
76 for i in range(4)
77  clipBounds.x = (i % 2) * viewSize[0]
78  clipBounds.y = (i // 2) * viewSize[1]
79  parViews.append(parent.view(clipBounds))
80  outViews.append(output.view(clipBounds))
81 
82 # Run one algorithm on each view:
83 # Each algorithm runs on a different stream, potentially in parallel, all streams working together
84 # consuming the same input to produce a single output, using CPU and CUDA backends
85 
86 with vpi.Backend.CPU
87  parViews[0].bilateral_filter(3, 45, 35, out=outViews[0], border=vpi.Border.ZERO, stream=streams[0])
88  parViews[1].image_flip(vpi.Flip.HORIZ, out=outViews[1], stream=streams[1])
89 
90 with vpi.Backend.CUDA
91  parViews[2].image_flip(vpi.Flip.VERT, out=outViews[2], stream=streams[2])
92  parViews[3].image_flip(vpi.Flip.BOTH, out=outViews[3], stream=streams[3])
93 
94 # Sync all streams to guarantee they finish before reading the output image
95 for i in range(4)
96  streams[i].sync()
97 
98 # Save result to disk
99 Image.fromarray(output.cpu()).save('output_views_python'+str(sys.version_info[0])+'.png')
29 #include <opencv2/core/version.hpp>
30 #include <opencv2/imgcodecs.hpp>
31 #include <opencv2/imgproc/imgproc.hpp>
32 #include <vpi/OpenCVInterop.hpp>
33 
34 #include <vpi/Event.h>
35 #include <vpi/Image.h>
36 #include <vpi/Status.h>
37 #include <vpi/Stream.h>
40 #include <vpi/algo/ImageFlip.h>
41 
42 #include <cstring> // for memset
43 #include <iostream>
44 #include <sstream>
45 
46 #define CHECK_STATUS(STMT) \
47  do \
48  { \
49  VPIStatus status = (STMT); \
50  if (status != VPI_SUCCESS) \
51  { \
52  char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
53  vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
54  std::ostringstream ss; \
55  ss << vpiStatusGetName(status) << ": " << buffer; \
56  throw std::runtime_error(ss.str()); \
57  } \
58  } while (0);
59 
60 int main(int argc, char *argv[])
61 {
62  // OpenCV image that will be wrapped by a VPIImage.
63  // Define it here so that it's destroyed *after* wrapper is destroyed
64  cv::Mat cvImage;
65 
66  // VPI objects that will be used
67  VPIImage imageBGR = NULL;
68  VPIImage inParent = NULL;
69  VPIImage outParent = NULL;
70  VPIEvent parentEvent = NULL;
71  VPIImage inViews[4] = {};
72  VPIImage outViews[4] = {};
73  VPIStream streams[4] = {};
74 
75  int retval = 0;
76 
77  // Views on Tegra devices need parent images to have only CPU or CUDA backends enabled.
78  int imgFlags = VPI_BACKEND_CPU | VPI_BACKEND_CUDA;
79 
80  try
81  {
82  if (argc != 2)
83  {
84  throw std::runtime_error(std::string("Usage: ") + argv[0] + " <input image>");
85  }
86 
87  std::string strInputFileName = argv[1];
88 
89  // Load the input image
90  cvImage = cv::imread(strInputFileName);
91  if (cvImage.empty())
92  {
93  throw std::runtime_error("Can't open '" + strInputFileName + "'");
94  }
95 
96  // Wrap the loaded image into a VPIImage object to be used by VPI
97  CHECK_STATUS(vpiImageCreateWrapperOpenCVMat(cvImage, 0, &imageBGR));
98 
99  // Retrieve the width and height of the loaded parent image
100  const int parentWidth = cvImage.cols, parentHeight = cvImage.rows;
101 
102  // Calculate the width and height of the child image views
103  const int viewWidth = parentWidth / 2, viewHeight = parentHeight / 2;
104 
105  if ((viewWidth == 0) || (viewHeight == 0))
106  {
107  throw std::runtime_error(std::string("Input image too small, it must be at least 2x2"));
108  }
109 
110  // Create the input parent image as a single unsigned 8-bit channel
111  CHECK_STATUS(vpiImageCreate(parentWidth, parentHeight, VPI_IMAGE_FORMAT_U8, imgFlags, &inParent));
112 
113  // Create the output parent image, single unsigned 8-bit channel
114  CHECK_STATUS(vpiImageCreate(parentWidth, parentHeight, VPI_IMAGE_FORMAT_U8, imgFlags, &outParent));
115 
116  // Create 1 event for the parent image
117  CHECK_STATUS(vpiEventCreate(0, &parentEvent));
118 
119  for (int i = 0; i < 4; ++i)
120  {
121  // Create 4 streams to execute algorithms on
122  CHECK_STATUS(vpiStreamCreate(0, &streams[i]));
123 
124  // Define the clip bounds to be the rectangular region of each view inside the parent image
125  VPIRectangleI clipBounds;
126 
127  clipBounds.x = static_cast<int>(i % 2) * viewWidth;
128  clipBounds.y = static_cast<int>(i / 2) * viewHeight;
129  clipBounds.width = viewWidth;
130  clipBounds.height = viewHeight;
131 
132  // Create each input child image view
133  CHECK_STATUS(vpiImageCreateView(inParent, &clipBounds, imgFlags, &inViews[i]));
134 
135  // Create each output child image view
136  CHECK_STATUS(vpiImageCreateView(outParent, &clipBounds, imgFlags, &outViews[i]));
137  }
138 
139  // Run one algorithm on each view:
140  // Each algorithm runs on a different stream, potentially in parallel, all streams working together
141  // consuming the same input to produce a single output, using CPU and CUDA backends
142 
143  // Convert the loaded parent image to grayscale
144  CHECK_STATUS(vpiSubmitConvertImageFormat(streams[0], VPI_BACKEND_CPU, imageBGR, inParent, NULL));
145 
146  // Record the parent event announcing the conversion above has finished
147  CHECK_STATUS(vpiEventRecord(parentEvent, streams[0]));
148 
149  // Wait the conversion above to finish, then release all streams for view processing
150  CHECK_STATUS(vpiStreamWaitEvent(streams[1], parentEvent));
151  CHECK_STATUS(vpiStreamWaitEvent(streams[2], parentEvent));
152  CHECK_STATUS(vpiStreamWaitEvent(streams[3], parentEvent));
153 
154  // Submit an algorithm on 1st view
155  CHECK_STATUS(
156  vpiSubmitBilateralFilter(streams[0], VPI_BACKEND_CPU, inViews[0], outViews[0], 3, 45, 35, VPI_BORDER_ZERO));
157 
158  // Submit an algorithm on 2nd view
159  CHECK_STATUS(vpiSubmitImageFlip(streams[1], VPI_BACKEND_CPU, inViews[1], outViews[1], VPI_FLIP_HORIZ));
160 
161  // Submit an algorithm on 3rd view
162  CHECK_STATUS(vpiSubmitImageFlip(streams[2], VPI_BACKEND_CUDA, inViews[2], outViews[2], VPI_FLIP_VERT));
163 
164  // Submit an algorithm on 4th view
165  CHECK_STATUS(vpiSubmitImageFlip(streams[3], VPI_BACKEND_CUDA, inViews[3], outViews[3], VPI_FLIP_BOTH));
166 
167  // Synchronize all streams to make sure all views finished processing
168  CHECK_STATUS(vpiStreamSync(streams[0]));
169  CHECK_STATUS(vpiStreamSync(streams[1]));
170  CHECK_STATUS(vpiStreamSync(streams[2]));
171  CHECK_STATUS(vpiStreamSync(streams[3]));
172 
173  // Retrieve the output parent image contents and write it to disk
174 
175  // Lock output parent image to retrieve its data, checking it consists of
176  // host-accessible memory buffers in pitch-linear layout
177  VPIImageData outData;
178  CHECK_STATUS(vpiImageLockData(outParent, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData));
180 
181  cv::Mat cvOut;
182  vpiImageDataExportOpenCVMat(outData, &cvOut);
183  imwrite("output_views.png", cvOut);
184 
185  // Done handling output parent image, don't forget to unlock it
186  CHECK_STATUS(vpiImageUnlock(outParent));
187  }
188  catch (std::exception &e)
189  {
190  std::cerr << e.what() << std::endl;
191  retval = 1;
192  }
193 
194  // Clean up
195 
196  // Make sure all streams are synchronized before destroying the objects possibly in use
197  for (int i = 0; i < 4; ++i)
198  {
199  if (streams[i] != NULL)
200  {
201  vpiStreamSync(streams[i]);
202  }
203  }
204 
205  // Make sure to destroy all views before destroying their parents
206  for (int i = 0; i < 4; ++i)
207  {
208  vpiStreamDestroy(streams[i]);
209 
210  vpiImageDestroy(inViews[i]);
211  vpiImageDestroy(outViews[i]);
212  }
213 
214  vpiEventDestroy(parentEvent);
215 
216  vpiImageDestroy(imageBGR);
217  vpiImageDestroy(inParent);
218  vpiImageDestroy(outParent);
219 
220  return retval;
221 }
声明实现双边滤波算法的函数。
声明处理图像格式转换的函数。
用于处理 VPI 事件的函数和结构。
声明实现图像翻转算法的函数。
#define VPI_IMAGE_FORMAT_U8
具有一个 8 位无符号整数通道的单平面。
Definition: ImageFormat.h:100
用于处理 VPI 图像的函数和结构。
用于处理 VPI 的 OpenCV 互操作性的函数。
VPI 状态代码处理函数的声明。
声明处理 VPI 流的函数。
@ VPI_FLIP_HORIZ
水平翻转。
Definition: Types.h:711
@ VPI_FLIP_VERT
垂直翻转。
Definition: Types.h:714
@ VPI_FLIP_BOTH
水平和垂直翻转。
Definition: Types.h:719
VPIStatus vpiSubmitBilateralFilter(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, int32_t kernelSize, float sigmaRange, float sigmaSpace, VPIBorderExtension border)
对图像运行 2D 双边滤波器。
VPIStatus vpiSubmitConvertImageFormat(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, const VPIConvertImageFormatParams *params)
将图像内容转换为所需的格式,可选择缩放和偏移。
struct VPIEventImpl * VPIEvent
事件的句柄。
Definition: Types.h:244
VPIStatus vpiEventRecord(VPIEvent event, VPIStream stream)
捕获事件中调用此函数时流命令队列的内容。
VPIStatus vpiEventCreate(uint64_t flags, VPIEvent *event)
创建事件实例。
void vpiEventDestroy(VPIEvent event)
销毁事件实例以及它拥有的所有资源。
VPIStatus vpiSubmitImageFlip(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, VPIFlipMode flipMode)
水平、垂直或同时翻转 2D 图像。
VPIImageBufferType bufferType
图像缓冲区类型。
Definition: Image.h:238
void vpiImageDestroy(VPIImage img)
销毁图像实例。
struct VPIImageImpl * VPIImage
图像的句柄。
Definition: Types.h:256
VPIStatus vpiImageCreateView(VPIImage imgParent, const VPIRectangleI *clipBounds, uint64_t flags, VPIImage *imgView)
创建图像,该图像包装现有图像的轴对齐矩形子区域。
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:234
VPIStatus vpiImageCreateWrapperOpenCVMat(const cv::Mat &mat, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
使用给定的图像格式将 cv::Mat 包装在 VPIImage 中。
VPIStatus vpiImageDataExportOpenCVMat(const VPIImageData &imgData, cv::Mat *mat)
使用来自锁定的 VPIImage 的 VPIImageData 数据填充现有的 cv::Mat。
struct VPIStreamImpl * VPIStream
流的句柄。
Definition: Types.h:250
VPIStatus vpiStreamWaitEvent(VPIStream stream, VPIEvent event)
推送一个命令,该命令会阻止处理提交到流的所有未来命令,直到 ...
VPIStatus vpiStreamSync(VPIStream stream)
阻止调用线程,直到此流队列中的所有已提交命令都完成(队列为空)...
void vpiStreamDestroy(VPIStream stream)
销毁流实例并释放所有硬件资源。
VPIStatus vpiStreamCreate(uint64_t flags, VPIStream *stream)
创建流实例。
@ VPI_BACKEND_CUDA
CUDA 后端。
定义: Types.h:93
@ VPI_BACKEND_CPU
CPU 后端。
定义: Types.h:92
@ VPI_BORDER_ZERO
图像外部的所有像素均被视为零。
定义: Types.h:278
@ VPI_LOCK_READ
仅锁定内存以进行读取。
定义: Types.h:617
存储矩形的几何信息。
定义: Types.h:588