VPI - 视觉编程接口

3.2 版本

2D 图像卷积

概述

2D 图像卷积应用程序输出一个包含输入图像边缘的图像,并将结果保存为磁盘上的图像文件。您可以定义将用于处理的后端。

注意
输出将为灰度图像,因为卷积目前仅支持单通道图像。

说明

命令行参数为

<backend> <input image>

其中

  • backend: 可以是 cpucudapva;它定义了将执行处理的后端。
  • input image: 输入图像文件名;它接受 png、jpeg 以及可能的其他格式。

这是一个示例

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

这是使用 CPU 后端和提供的示例图像之一。您可以尝试其他图像,但请遵守算法施加的约束。

结果

输入图像输出图像,边缘

源代码

为了方便起见,以下代码也安装在 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('backend', choices=['cpu','cuda','pva'],
36  help='Backend to be used for processing')
37 
38 parser.add_argument('input',
39  help='Image to be used as input')
40 
41 args = parser.parse_args();
42 
43 if args.backend == 'cpu'
44  backend = vpi.Backend.CPU
45 elif args.backend == 'cuda'
46  backend = vpi.Backend.CUDA
47 else
48  assert args.backend == 'pva'
49  backend = vpi.Backend.PVA
50 
51 # Load input into a vpi.Image
52 try
53  input = vpi.asimage(np.asarray(Image.open(args.input)))
54 except IOError
55  sys.exit("Input file not found")
56 except
57  sys.exit("Error with input file")
58 
59 # Convert it to grayscale
60 input = input.convert(vpi.Format.U8, backend=vpi.Backend.CUDA)
61 
62 # Define a simple edge detection kernel
63 kernel = [[ 1, 0, -1],
64  [ 0, 0, 0],
65  [-1, 0, 1]]
66 
67 # Using the chosen backend,
68 with backend
69  # Run input through the convolution filter
70  output = input.convolution(kernel, border=vpi.Border.ZERO)
71 
72 # Save result to disk
73 Image.fromarray(output.cpu()).save('edges_python'+str(sys.version_info[0])+'_'+args.backend+'.png')
29 #include <opencv2/core/version.hpp>
30 #include <opencv2/imgproc/imgproc.hpp>
31 #if CV_MAJOR_VERSION >= 3
32 # include <opencv2/imgcodecs.hpp>
33 #else
34 # include <opencv2/highgui/highgui.hpp>
35 #endif
36 
37 #include <vpi/OpenCVInterop.hpp>
38 
39 #include <vpi/Image.h>
40 #include <vpi/Status.h>
41 #include <vpi/Stream.h>
43 #include <vpi/algo/Convolution.h>
44 
45 #include <cstring> // for memset
46 #include <iostream>
47 #include <sstream>
48 
49 #define CHECK_STATUS(STMT) \
50  do \
51  { \
52  VPIStatus status = (STMT); \
53  if (status != VPI_SUCCESS) \
54  { \
55  char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
56  vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
57  std::ostringstream ss; \
58  ss << vpiStatusGetName(status) << ": " << buffer; \
59  throw std::runtime_error(ss.str()); \
60  } \
61  } while (0);
62 
63 int main(int argc, char *argv[])
64 {
65  // OpenCV image that will be wrapped by a VPIImage.
66  // Define it here so that it's destroyed *after* wrapper is destroyed
67  cv::Mat cvImage;
68 
69  // VPI objects that will be used
70  VPIImage image = NULL;
71  VPIImage imageBGR = NULL;
72  VPIImage gradient = NULL;
73  VPIStream stream = NULL;
74 
75  int retval = 0;
76 
77  try
78  {
79  if (argc != 3)
80  {
81  throw std::runtime_error(std::string("Usage: ") + argv[0] + " <cpu|pva|cuda> <input image>");
82  }
83 
84  std::string strBackend = argv[1];
85  std::string strInputFileName = argv[2];
86 
87  // Load the input image
88  cvImage = cv::imread(strInputFileName);
89  if (cvImage.empty())
90  {
91  throw std::runtime_error("Can't open '" + strInputFileName + "'");
92  }
93 
94  // We can't use cv::IMREAD_GRAYSCALE when opening the input file because the
95  // color to grayscale conversion used differs between OpenCV-2.4 and OpenCV>=3.0,
96  // yielding different image content.
97 
98  // Now parse the backend
99  VPIBackend backend;
100 
101  if (strBackend == "cpu")
102  {
103  backend = VPI_BACKEND_CPU;
104  }
105  else if (strBackend == "cuda")
106  {
107  backend = VPI_BACKEND_CUDA;
108  }
109  else if (strBackend == "pva")
110  {
111  backend = VPI_BACKEND_PVA;
112  }
113  else
114  {
115  throw std::runtime_error("Backend '" + strBackend +
160  "' not recognized, it must be either cpu, cuda or pva.");
117  }
118 
119  // Create the stream for any backend.
120  CHECK_STATUS(vpiStreamCreate(0, &stream));
121 
122  // We now wrap the loaded image into a VPIImage object to be used by VPI.
123  CHECK_STATUS(vpiImageCreateWrapperOpenCVMat(cvImage, 0, &imageBGR));
124 
125  // Now create the input image as a single unsigned 8-bit channel
126  CHECK_STATUS(vpiImageCreate(cvImage.cols, cvImage.rows, VPI_IMAGE_FORMAT_U8, 0, &image));
127 
128  // Convert the loaded image to grayscale
129  CHECK_STATUS(vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, imageBGR, image, NULL));
130 
131  // Now create the output image, single unsigned 8-bit channel.
132  CHECK_STATUS(vpiImageCreate(cvImage.cols, cvImage.rows, VPI_IMAGE_FORMAT_U8, 0, &gradient));
133 
134  // Define the convolution filter, a simple edge detector.
135  float kernel[3 * 3] = {1, 0, -1, 0, 0, 0, -1, 0, 1};
136 
137  // Submit it for processing passing the input image the result image that will store the gradient.
138  CHECK_STATUS(vpiSubmitConvolution(stream, backend, image, gradient, kernel, 3, 3, VPI_BORDER_ZERO));
139 
140  // Wait until the algorithm finishes processing
141  CHECK_STATUS(vpiStreamSync(stream));
142 
143  // Now let's retrieve the output image contents and output it to disk
144  {
145  // Lock output image to retrieve its data.
146  VPIImageData outData;
147  CHECK_STATUS(vpiImageLockData(gradient, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData));
148 
149  // Returned data consists of host-accessible memory buffers in pitch-linear layout.
151 
152  const VPIImageBufferPitchLinear &outPitch = outData.buffer.pitch;
153 
154  cv::Mat cvOut(outPitch.planes[0].height, outPitch.planes[0].width, CV_8UC1, outPitch.planes[0].data,
155  outPitch.planes[0].pitchBytes);
156  imwrite("edges_" + strBackend + ".png", cvOut);
157 
158  // Done handling output image, don't forget to unlock it.
159  CHECK_STATUS(vpiImageUnlock(gradient));
160  }
161  }
162  catch (std::exception &e)
163  {
164  std::cerr << e.what() << std::endl;
165  retval = 1;
166  }
167 
168  // Clean up
169 
170  // Make sure stream is synchronized before destroying the objects
171  // that might still be in use.
172  if (stream != NULL)
173  {
174  vpiStreamSync(stream);
175  }
176 
177  vpiImageDestroy(image);
178  vpiImageDestroy(imageBGR);
179  vpiImageDestroy(gradient);
180 
181  vpiStreamDestroy(stream);
182 
183  return retval;
184 }
声明处理图像格式转换的函数。
声明使用卷积核执行图像滤波的函数。
#define VPI_IMAGE_FORMAT_U8
单平面,带有一个 8 位无符号整数通道。
用于处理 VPI 图像的函数和结构。
用于处理 VPI 与 OpenCV 互操作性的函数。
VPI 状态代码处理函数的声明。
声明处理 VPI 流的函数。
VPIStatus vpiSubmitConvertImageFormat(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, const VPIConvertImageFormatParams *params)
将图像内容转换为所需的格式,带有可选的缩放和偏移。
VPIStatus vpiSubmitConvolution(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, const float *kernelData, int32_t kernelWidth, int32_t kernelHeight, VPIBorderExtension border)
对图像运行通用的 2D 卷积。
VPIImageBuffer buffer
存储图像内容。
定义: Image.h:241
VPIImagePlanePitchLinear planes[VPI_MAX_PLANE_COUNT]
以 pitch-linear 布局排列的所有图像平面的数据。
定义: Image.h:160
VPIImageBufferPitchLinear pitch
以 pitch-linear 布局存储的图像。
定义: Image.h:210
void * data
指向此平面第一行的指针。
定义: Image.h:141
VPIImageBufferType bufferType
图像缓冲区类型。
定义: Image.h:238
int32_t height
此平面的高度,以像素为单位。
定义: Image.h:123
int32_t width
此平面的宽度,以像素为单位。
定义: Image.h:119
int32_t pitchBytes
一行开头与前一行开头之间的字节差。
定义: Image.h:134
void vpiImageDestroy(VPIImage img)
销毁图像实例。
struct VPIImageImpl * VPIImage
图像的句柄。
定义: Types.h:256
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 内存布局。
定义: Image.h:172
存储图像平面内容。
定义: Image.h:150
存储有关图像特征和内容的信息。
定义: Image.h:234
VPIStatus vpiImageCreateWrapperOpenCVMat(const cv::Mat &mat, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
使用给定的图像格式将 cv::Mat 封装在 VPIImage 中。
struct VPIStreamImpl * VPIStream
流的句柄。
定义: Types.h:250
VPIStatus vpiStreamSync(VPIStream stream)
阻塞调用线程,直到此流队列中所有提交的命令都完成(队列为空)。
VPIBackend
VPI 后端类型。
定义: Types.h:91
void vpiStreamDestroy(VPIStream stream)
销毁流实例并释放所有硬件资源。
VPIStatus vpiStreamCreate(uint64_t flags, VPIStream *stream)
创建流实例。
@ VPI_BACKEND_CUDA
CUDA 后端。
定义: Types.h:93
@ VPI_BACKEND_PVA
PVA 后端。
定义: Types.h:94
@ VPI_BACKEND_CPU
CPU 后端。
定义: Types.h:92
@ VPI_BORDER_ZERO
图像外部的所有像素都被视为零。
定义: Types.h:278
@ VPI_LOCK_READ
仅锁定内存以进行读取。
定义: Types.h:617