VPI - Vision Programming Interface

0.4.4 Release

2D Image Convolution

Overview

The 2D Image Convolution application outputs an image with the edges of the input image, saving the result into edges.png. The user can define what backend will be used for processing.

Note
The output will be in grayscale as convolution is currently only supported for single-channel images.

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 where output will be written to.
  • Create a Convolve2D algorithm and call it, passing a custom kernel.
  • Simple stream synchronization.
  • Image locking to access its contents from CPU side.
  • Error handling.
  • Environment clean up.

Instructions

The usage is:

./vpi_sample_01_convolve_2d <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; it accepts png, jpeg and possibly others.

Here's one example:

./vpi_sample_01_convolve_2d cpu ../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, edges

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>
39 #include <vpi/algo/Convolution.h>
40 
41 #include <cstring> // for memset
42 #include <iostream>
43 #include <sstream>
44 
45 #define CHECK_STATUS(STMT) \
46  do \
47  { \
48  VPIStatus status = (STMT); \
49  if (status != VPI_SUCCESS) \
50  { \
51  char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
52  vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
53  std::ostringstream ss; \
54  ss << vpiStatusGetName(status) << ": " << buffer; \
55  throw std::runtime_error(ss.str()); \
56  } \
57  } while (0);
58 
59 int main(int argc, char *argv[])
60 {
61  VPIImage image = NULL;
62  VPIImage gradient = NULL;
63  VPIStream stream = NULL;
64 
65  int retval = 0;
66 
67  try
68  {
69  if (argc != 3)
70  {
71  throw std::runtime_error(std::string("Usage: ") + argv[0] + " <cpu|pva|cuda> <input image>");
72  }
73 
74  std::string strBackend = argv[1];
75  std::string strInputFileName = argv[2];
76 
77  // Load the input image
78  cv::Mat cvImage = cv::imread(strInputFileName, cv::IMREAD_GRAYSCALE);
79  if (cvImage.empty())
80  {
81  throw std::runtime_error("Can't open '" + strInputFileName + "'");
82  }
83 
84  assert(cvImage.type() == CV_8UC1);
85 
86  // Now parse the backend
87  VPIBackend backendType;
88 
89  if (strBackend == "cpu")
90  {
91  backendType = VPI_BACKEND_CPU;
92  }
93  else if (strBackend == "cuda")
94  {
95  backendType = VPI_BACKEND_CUDA;
96  }
97  else if (strBackend == "pva")
98  {
99  backendType = VPI_BACKEND_PVA;
100  }
101  else
102  {
103  throw std::runtime_error("Backend '" + strBackend +
104  "' not recognized, it must be either cpu, cuda or pva.");
105  }
106 
107  // Create the stream for the given backend.
108  CHECK_STATUS(vpiStreamCreate(backendType, &stream));
109 
110  // We now wrap the loaded image into a VPIImage object to be used by VPI.
111  {
112  // First fill VPIImageData with the, well, image data...
113  VPIImageData imgData;
114  memset(&imgData, 0, sizeof(imgData));
115  imgData.type = VPI_IMAGE_FORMAT_U8;
116  imgData.numPlanes = 1;
117  imgData.planes[0].width = cvImage.cols;
118  imgData.planes[0].height = cvImage.rows;
119  imgData.planes[0].pitchBytes = cvImage.step[0];
120  imgData.planes[0].data = cvImage.data;
121 
122  // Wrap it into a VPIImage. VPI won't make a copy of it, so the original
123  // image must be in scope at all times.
124  CHECK_STATUS(vpiImageCreateHostMemWrapper(&imgData, 0, &image));
125  }
126 
127  // Now create the output image, single unsigned 8-bit channel.
128  CHECK_STATUS(vpiImageCreate(cvImage.cols, cvImage.rows, VPI_IMAGE_FORMAT_U8, 0, &gradient));
129 
130  // Define the convolution filter, a simple edge detector.
131  float kernel[3 * 3] = {1, 0, -1, 0, 0, 0, -1, 0, 1};
132 
133  // Deal with PVA restriction where abs(weight) < 1.
134  for (int i = 0; i < 9; ++i)
135  {
136  kernel[i] /= 1 + FLT_EPSILON;
137  }
138 
139  // Submit it for processing passing the input image the result image that will store the gradient.
140  CHECK_STATUS(vpiSubmitConvolution(stream, backendType, image, gradient, kernel, 3, 3, VPI_BOUNDARY_COND_ZERO));
141 
142  // Wait until the algorithm finishes processing
143  CHECK_STATUS(vpiStreamSync(stream));
144 
145  // Now let's retrieve the output image contents and output it to disk
146  {
147  // Lock output image to retrieve its data on cpu memory
148  VPIImageData outData;
149  CHECK_STATUS(vpiImageLock(gradient, VPI_LOCK_READ, &outData));
150 
151  cv::Mat cvOut(outData.planes[0].height, outData.planes[0].width, CV_8UC1, outData.planes[0].data,
152  outData.planes[0].pitchBytes);
153  imwrite("edges_" + strBackend + ".png", cvOut);
154 
155  // Done handling output image, don't forget to unlock it.
156  CHECK_STATUS(vpiImageUnlock(gradient));
157  }
158  }
159  catch (std::exception &e)
160  {
161  std::cerr << e.what() << std::endl;
162  retval = 1;
163  }
164 
165  // Clean up
166 
167  // Make sure stream is synchronized before destroying the objects
168  // that might still be in use.
169  if (stream != NULL)
170  {
171  vpiStreamSync(stream);
172  }
173 
174  vpiImageDestroy(image);
175  vpiImageDestroy(gradient);
176 
177  vpiStreamDestroy(stream);
178 
179  return retval;
180 }
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
vpiSubmitConvolution
VPIStatus vpiSubmitConvolution(VPIStream stream, VPIBackend backend, VPIImage input, VPIImage output, const float *kernelData, uint32_t kernelWidth, uint32_t kernelHeight, VPIBoundaryCond boundary)
Runs a generic 2D convolution over an image.
vpiStreamCreate
VPIStatus vpiStreamCreate(uint32_t flags, VPIStream *stream)
Create a stream instance.
VPIBackend
VPIBackend
VPI Backend types.
Definition: Types.h:89
Convolution.h
Declares functions to perform image filtering with convolution kernels.
VPI_LOCK_READ
@ VPI_LOCK_READ
Lock memory only for reading.
Definition: Types.h:447
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
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.
VPI_IMAGE_FORMAT_U8
@ VPI_IMAGE_FORMAT_U8
Single plane with one 8-bit unsigned integer channel.
Definition: ImageFormat.h:99
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
VPI_BOUNDARY_COND_ZERO
@ VPI_BOUNDARY_COND_ZERO
All pixels outside the image are considered to be zero.
Definition: Types.h:218
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
Status.h
Declaration of VPI status codes handling functions.
VPI_BACKEND_CPU
@ VPI_BACKEND_CPU
CPU backend.
Definition: Types.h:90
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
VPI_BACKEND_PVA
@ VPI_BACKEND_PVA
PVA backend.
Definition: Types.h:92