VPI - Vision Programming Interface

0.4.4 Release

Image Resampling

Overview

The Rescale application rescales the input image by first applying a low-pass filter to avoid aliasing, then doing a downsampling. The resulting image has half of input's width and one third of input's height. The result is then saved to disk.

This sample shows the following:

  • Creating and destroying a VPI stream.
  • Wrapping an image hosted on CPU (the input) to be used by VPI
  • Creating a VPI-managed 2D image.
  • Create a pipeline with two operations, GaussianFilter and Rescale.
  • Simple stream synchronization.
  • Image locking to access its contents from CPU side.
  • Error handling.
  • Environment clean up.

Instructions

The usage is:

./vpi_sample_04_rescale <backend> <input image>

where

  • backend: either cpu, cuda or pva; it defines the backend that will perform the processing.
  • input image: input image file name to be downsampled, it accepts png, jpeg and possibly others.

Here's one example:

./vpi_sample_04_rescale cuda ../assets/kodim8.png

This is using the CUDA backend and one of the provided sample images. You can try with other images, respecting the constraints imposed by the algorithm.

Results

Input imageOutput image, downsampled

Source code

For convenience, here's the code that is also installed in the samples directory.

1 /*
2 * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of NVIDIA CORPORATION nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 
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/Image.h>
37 #include <vpi/Status.h>
38 #include <vpi/Stream.h>
40 #include <vpi/algo/Rescale.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  VPIImage image = NULL;
63  VPIImage imageNV12 = NULL;
64  VPIImage outputNV12 = NULL;
65  VPIImage output = NULL;
66  VPIStream stream = NULL;
67 
68  int retval = 0;
69 
70  try
71  {
72  if (argc != 3)
73  {
74  throw std::runtime_error(std::string("Usage: ") + argv[0] + " <cpu|vic|cuda> <input image>");
75  }
76 
77  std::string strBackend = argv[1];
78  std::string strInputFileName = argv[2];
79 
80  // Load the input image
81  cv::Mat cvImage = cv::imread(strInputFileName);
82  if (cvImage.empty())
83  {
84  throw std::runtime_error("Can't open '" + strInputFileName + "'");
85  }
86 
87  assert(cvImage.type() == CV_8UC3);
88 
89  // Now parse the backend
90  VPIBackend backendType;
91 
92  if (strBackend == "cpu")
93  {
94  backendType = VPI_BACKEND_CPU;
95  }
96  else if (strBackend == "cuda")
97  {
98  backendType = VPI_BACKEND_CUDA;
99  }
100  else if (strBackend == "vic")
101  {
102  backendType = VPI_BACKEND_VIC;
103  }
104  else
105  {
106  throw std::runtime_error("Backend '" + strBackend + "' not recognized, it must be either cpu, cuda or vic");
107  }
108 
109  // 1. Initialization phase ---------------------------------------
110 
111  // Create the stream for the given backend. We'll also enable CUDA for gaussian filter.
112  CHECK_STATUS(vpiStreamCreate(backendType | VPI_BACKEND_CUDA, &stream));
113 
114  // We now wrap the loaded image into a VPIImage object to be used by VPI.
115  {
116  // First fill VPIImageData with the, well, image data...
117  VPIImageData imgData;
118  memset(&imgData, 0, sizeof(imgData));
119  imgData.type = VPI_IMAGE_FORMAT_BGR8;
120  imgData.numPlanes = 1;
121  imgData.planes[0].width = cvImage.cols;
122  imgData.planes[0].height = cvImage.rows;
123  imgData.planes[0].pitchBytes = cvImage.step[0];
124  imgData.planes[0].data = cvImage.data;
125 
126  // Wrap it into a VPIImage. VPI won't make a copy of it, so the original
127  // image must be in scope at all times.
128  CHECK_STATUS(vpiImageCreateHostMemWrapper(&imgData, 0, &image));
129  }
130 
131  // Create a temporary image to hold the input converted to NV12.
132  CHECK_STATUS(vpiImageCreate(cvImage.cols, cvImage.rows, VPI_IMAGE_FORMAT_NV12, 0, &imageNV12));
133 
134  // Now create the output image.
135  CHECK_STATUS(vpiImageCreate(cvImage.cols / 2, cvImage.rows / 3, VPI_IMAGE_FORMAT_NV12, 0, &outputNV12));
136 
137  // And the output image converted back to BGR8
138  CHECK_STATUS(vpiImageCreate(cvImage.cols / 2, cvImage.rows / 3, VPI_IMAGE_FORMAT_BGR8, 0, &output));
139 
140  // 2. Computation phase ---------------------------------------
141 
142  // Convert input from BGR8 to NV12
143  CHECK_STATUS(
144  vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, image, imageNV12, VPI_CONVERSION_CAST, 1, 0));
145 
146  // Now we downsample
147  CHECK_STATUS(
148  vpiSubmitRescale(stream, backendType, imageNV12, outputNV12, VPI_INTERP_LINEAR, VPI_BOUNDARY_COND_CLAMP));
149 
150  // Finally, convert the result back to BGR8
151  CHECK_STATUS(
152  vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, outputNV12, output, VPI_CONVERSION_CAST, 1, 0));
153 
154  // Wait until the algorithm finishes processing
155  CHECK_STATUS(vpiStreamSync(stream));
156 
157  // Now let's retrieve the output image contents and output it to disk
158  {
159  VPIImageData data;
160  CHECK_STATUS(vpiImageLock(output, VPI_LOCK_READ, &data));
161 
162  // Lock output image to retrieve its data on cpu memory
163  VPIImageData outData;
164  CHECK_STATUS(vpiImageLock(output, VPI_LOCK_READ, &outData));
165 
166  cv::Mat cvOut(outData.planes[0].height, outData.planes[0].width, CV_8UC3, outData.planes[0].data,
167  outData.planes[0].pitchBytes);
168  imwrite("scaled_" + strBackend + ".png", cvOut);
169 
170  // Done handling output image, don't forget to unlock it.
171  CHECK_STATUS(vpiImageUnlock(output));
172  }
173  }
174  catch (std::exception &e)
175  {
176  std::cerr << e.what() << std::endl;
177  retval = 1;
178  }
179 
180  // Clean up
181 
182  // Make sure stream is synchronized before destroying the objects
183  // that might still be in use.
184  if (stream != NULL)
185  {
186  vpiStreamSync(stream);
187  }
188 
189  vpiImageDestroy(image);
190  vpiImageDestroy(imageNV12);
191  vpiImageDestroy(output);
192  vpiStreamDestroy(stream);
193 
194  return retval;
195 }
VPIImagePlane::height
uint32_t height
Height of this plane in pixels.
Definition: Image.h:138
VPIImagePlane::width
uint32_t width
Width of this plane in pixels.
Definition: Image.h:137
VPI_IMAGE_FORMAT_NV12
@ VPI_IMAGE_FORMAT_NV12
YUV420sp 8-bit pitch-linear format composed of two planes:
Definition: ImageFormat.h:123
vpiStreamCreate
VPIStatus vpiStreamCreate(uint32_t flags, VPIStream *stream)
Create a stream instance.
VPIBackend
VPIBackend
VPI Backend types.
Definition: Types.h:89
VPI_LOCK_READ
@ VPI_LOCK_READ
Lock memory only for reading.
Definition: Types.h:447
VPI_IMAGE_FORMAT_BGR8
@ VPI_IMAGE_FORMAT_BGR8
Single plane with interleaved BGR 8-bit channel.
Definition: ImageFormat.h:135
vpiImageUnlock
VPIStatus vpiImageUnlock(VPIImage img)
Releases the lock on an image object.
vpiStreamSync
VPIStatus vpiStreamSync(VPIStream stream)
Blocks the calling thread until all submitted commands in this stream queue are done (queue is empty)...
VPI_BACKEND_CUDA
@ VPI_BACKEND_CUDA
CUDA backend.
Definition: Types.h:91
VPIImageData
Stores information about image characteristics and content.
Definition: Image.h:159
VPIStream
struct VPIStreamImpl * VPIStream
A handle to a stream.
Definition: Types.h:190
Rescale.h
Declares functions that implement the Rescale algorithm.
VPI_INTERP_LINEAR
@ VPI_INTERP_LINEAR
Alias to fast linear interpolation.
Definition: Types.h:282
vpiStreamDestroy
void vpiStreamDestroy(VPIStream stream)
Destroy a stream instance and deallocate all HW resources.
VPIImageData::planes
VPIImagePlane planes[VPI_MAX_PLANE_COUNT]
Data of all image planes.
Definition: Image.h:166
vpiImageCreate
VPIStatus vpiImageCreate(uint32_t width, uint32_t height, VPIImageFormat fmt, uint32_t flags, VPIImage *img)
Create an empty image instance with the specified flags.
vpiImageDestroy
void vpiImageDestroy(VPIImage img)
Destroy an image instance.
Image.h
Functions and structures for dealing with VPI images.
VPIImagePlane::pitchBytes
uint32_t pitchBytes
Difference in bytes of beginning of one row and the beginning of the previous.
Definition: Image.h:139
VPIImage
struct VPIImageImpl * VPIImage
A handle to an image.
Definition: Types.h:196
VPIImageData::numPlanes
int32_t numPlanes
Number of planes.
Definition: Image.h:161
vpiSubmitRescale
VPIStatus vpiSubmitRescale(VPIStream stream, VPIBackend backend, VPIImage input, VPIImage output, VPIInterpolationType interpolationType, VPIBoundaryCond boundary)
Changes the size and scale of a 2D image.
VPI_BACKEND_VIC
@ VPI_BACKEND_VIC
VIC backend.
Definition: Types.h:93
vpiSubmitConvertImageFormat
VPIStatus vpiSubmitConvertImageFormat(VPIStream stream, VPIBackend backend, VPIImage input, VPIImage output, VPIConversionPolicy convPolicy, float scale, float offset)
Converts the image contents to the desired format, with optional scaling and offset.
vpiImageLock
VPIStatus vpiImageLock(VPIImage img, VPILockMode mode, VPIImageData *hostData)
Acquires the lock on an image object and returns a pointer to the image planes.
VPIImageData::type
VPIImageFormat type
Image type.
Definition: Image.h:160
VPI_BOUNDARY_COND_CLAMP
@ VPI_BOUNDARY_COND_CLAMP
Border pixels are repeated indefinitely.
Definition: Types.h:219
Status.h
Declaration of VPI status codes handling functions.
ConvertImageFormat.h
Declares functions that handle image format conversion.
VPI_BACKEND_CPU
@ VPI_BACKEND_CPU
CPU backend.
Definition: Types.h:90
VPI_CONVERSION_CAST
@ VPI_CONVERSION_CAST
Casts input to the output type.
Definition: Types.h:336
vpiImageCreateHostMemWrapper
VPIStatus vpiImageCreateHostMemWrapper(const VPIImageData *hostData, uint32_t flags, VPIImage *img)
Create an image object by wrapping around an existing host memory block.
Stream.h
Declares functions dealing with VPI streams.
VPIImagePlane::data
void * data
Pointer to the first row of this plane.
Definition: Image.h:147