VPI - Vision Programming Interface

3.2 版本

图像重采样

概述

Rescale 应用程序通过首先应用低通滤波器以避免混叠,然后进行降采样来重新调整输入图像的大小。生成的图像具有输入图像宽度的一半和高度的三分之一。然后将结果保存到磁盘。

说明

命令行参数为

<backend> <input image>

其中

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

这是一个示例

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

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

结果

输入图像输出图像,已降采样

源代码

为了方便起见,以下代码也安装在示例目录中。

语言
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','vic'],
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 == 'vic'
49  backend = vpi.Backend.VIC
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 # Using the chosen backend,
60 with backend
61  # First convert input to NV12_ER.
62  # We're overriding the default backend with CUDA.
63  temp = input.convert(vpi.Format.NV12_ER, backend=vpi.Backend.CUDA)
64 
65  # Rescale the image using the chosen backend
66  temp = temp.rescale((input.width//2, input.height//3))
67 
68  # Convert result back to input's format
69  output = temp.convert(input.format, backend=vpi.Backend.CUDA)
70 
71 # Save result to disk
72 Image.fromarray(output.cpu()).save('scaled_python'+str(sys.version_info[0])+'_'+args.backend+'.png')
29 #include <opencv2/core/version.hpp>
30 #if CV_MAJOR_VERSION >= 3
31 # include <opencv2/imgcodecs.hpp>
32 #else
33 # include <opencv2/highgui/highgui.hpp>
34 #endif
35 
36 #include <vpi/OpenCVInterop.hpp>
37 
38 #include <vpi/Image.h>
39 #include <vpi/Status.h>
40 #include <vpi/Stream.h>
42 #include <vpi/algo/Rescale.h>
43 
44 #include <cassert>
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 imageNV12 = NULL;
72  VPIImage outputNV12 = NULL;
73  VPIImage output = NULL;
74  VPIStream stream = NULL;
75 
76  int retval = 0;
77 
78  try
79  {
80  if (argc != 3)
81  {
82  throw std::runtime_error(std::string("Usage: ") + argv[0] + " <cpu|vic|cuda> <input image>");
83  }
84 
85  std::string strBackend = argv[1];
86  std::string strInputFileName = argv[2];
87 
88  // Load the input image
89  cvImage = cv::imread(strInputFileName);
90  if (cvImage.empty())
91  {
92  throw std::runtime_error("Can't open '" + strInputFileName + "'");
93  }
94 
95  assert(cvImage.type() == CV_8UC3);
96 
97  // Now parse the backend
98  VPIBackend backend;
99 
100  if (strBackend == "cpu")
101  {
102  backend = VPI_BACKEND_CPU;
103  }
104  else if (strBackend == "cuda")
105  {
106  backend = VPI_BACKEND_CUDA;
107  }
108  else if (strBackend == "vic")
109  {
110  backend = VPI_BACKEND_VIC;
111  }
112  else
113  {
114  throw std::runtime_error("Backend '" + strBackend + "' not recognized, it must be either cpu, cuda or vic");
115  }
116 
117  // 1. Initialization phase ---------------------------------------
118 
119  // Create the stream for the given backend. We'll also enable CUDA for gaussian filter.
120  CHECK_STATUS(vpiStreamCreate(backend | VPI_BACKEND_CUDA, &stream));
121 
122  // We now wrap the loaded image into a VPIImage object to be used by VPI.
123  // VPI won't make a copy of it, so the original
124  // image must be in scope at all times.
125  CHECK_STATUS(vpiImageCreateWrapperOpenCVMat(cvImage, 0, &image));
126 
127  // Create a temporary image to hold the input converted to NV12.
128  CHECK_STATUS(vpiImageCreate(cvImage.cols, cvImage.rows, VPI_IMAGE_FORMAT_NV12_ER, 0, &imageNV12));
129 
130  // Now create the output image.
131  CHECK_STATUS(vpiImageCreate(cvImage.cols / 2, cvImage.rows / 3, VPI_IMAGE_FORMAT_NV12_ER, 0, &outputNV12));
132 
133  // And the output image converted back to BGR8
134  CHECK_STATUS(vpiImageCreate(cvImage.cols / 2, cvImage.rows / 3, VPI_IMAGE_FORMAT_BGR8, 0, &output));
135 
136  // 2. Computation phase ---------------------------------------
137 
138  // Convert input from BGR8 to NV12
139  CHECK_STATUS(vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, image, imageNV12, NULL));
140 
141  // Now we downsample
142  CHECK_STATUS(vpiSubmitRescale(stream, backend, imageNV12, outputNV12, VPI_INTERP_LINEAR, VPI_BORDER_CLAMP, 0));
143 
144  // Finally, convert the result back to BGR8
145  CHECK_STATUS(vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, outputNV12, output, NULL));
146 
147  // Wait until the algorithm finishes processing
148  CHECK_STATUS(vpiStreamSync(stream));
149 
150  // Now let's retrieve the output image contents and output it to disk
151  {
152  // Lock output image to retrieve its data on cpu memory
153  VPIImageData outData;
154  CHECK_STATUS(vpiImageLockData(output, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &outData));
155 
156  // Returned data consists of host-accessible memory buffers in pitch-linear layout.
158 
159  VPIImageBufferPitchLinear &outDataPitch = outData.buffer.pitch;
160 
161  cv::Mat cvOut(outDataPitch.planes[0].height, outDataPitch.planes[0].width, CV_8UC3,
162  outDataPitch.planes[0].data, outDataPitch.planes[0].pitchBytes);
163  imwrite("scaled_" + strBackend + ".png", cvOut);
164 
165  // Done handling output image, don't forget to unlock it.
166  CHECK_STATUS(vpiImageUnlock(output));
167  }
168  }
169  catch (std::exception &e)
170  {
171  std::cerr << e.what() << std::endl;
172  retval = 1;
173  }
174 
175  // Clean up
176 
177  // Make sure stream is synchronized before destroying the objects
178  // that might still be in use.
179  vpiStreamSync(stream);
180 
181  vpiImageDestroy(image);
182  vpiImageDestroy(imageNV12);
183  vpiImageDestroy(output);
184  vpiStreamDestroy(stream);
185 
186  return retval;
187 }
声明处理图像格式转换的函数。
#define VPI_IMAGE_FORMAT_BGR8
具有交错 BGR 8 位通道的单平面。
#define VPI_IMAGE_FORMAT_NV12_ER
具有全范围的 YUV420sp 8 位 pitch-linear 格式。
用于处理 VPI 图像的函数和结构。
用于处理 OpenCV 与 VPI 互操作性的函数。
声明实现 Rescale 算法的函数。
VPI 状态代码处理函数的声明。
声明处理 VPI 流的函数。
VPIStatus vpiSubmitConvertImageFormat(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, const VPIConvertImageFormatParams *params)
将图像内容转换为所需的格式,具有可选的缩放和偏移。
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 中。
VPIStatus vpiSubmitRescale(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, VPIInterpolationType interpolationType, VPIBorderExtension border, uint64_t flags)
更改 2D 图像的大小和比例。
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_VIC
VIC 后端。
定义: Types.h:95
@ VPI_BACKEND_CPU
CPU 后端。
定义: Types.h:92
@ VPI_BORDER_CLAMP
无限重复边界像素。
定义: Types.h:279
@ VPI_INTERP_LINEAR
线性插值。
@ VPI_LOCK_READ
仅锁定内存以进行读取。
定义: Types.h:617