VPI - Vision Programming Interface

0.4.4 Release

Building your first application

This tutorial provides step-by-step procedures for building an application that demonstrates VPI programming concepts. With this tutorial a simple grayscale image blurring command line application that runs on CUDA is created.

Requirements

Creating the application requires:

  • c++ compiler (g++ or clang++ works, testing was performed with version g++-7 and clang++-7)
  • cmake >= 3.8, to build the project
  • OpenCV >= 3.0, for image Input/Output

On Ubuntu 18.04, install these packages:

apt-get install g++ cmake libopencv-dev

Creating a CMake Project

Create the cmake project to build the application as follows.

1 cmake_minimum_required(VERSION 3.5)
2 
3 project(vpi_blur)
4 
5 # This instructs cmake to look for the most recent
6 # vpi instance installed on the system.
7 find_package(vpi REQUIRED)
8 find_package(OpenCV REQUIRED)
9 
10 # Creates the blur executable target
11 add_executable(vpi_blur main.cpp)
12 
13 # It uses vpi and opencv. CMake will automatically
14 # set up the correct header and library directories,
15 # and make hello_work link to these libraries.
16 target_link_libraries(vpi_blur vpi opencv_core)
17 
18 # OpenCV < 3 uses a different library for image i/o
19 if(OpenCV_VERSION VERSION_LESS 3)
20  target_link_libraries(vpi_blur opencv_highgui)
21 else()
22  target_link_libraries(vpi_blur opencv_imgcodecs)
23 endif()

Note that cmake automatically sets up the VPI header and library paths for the executable. It is not necessary to manually define them.

Writing an Image Blurring Application

Write the C++ code that gets one image file from command line, blurs it, and writes the results back to disk.

Copy the provided code to a file named main.cpp. For simplicity, error checking is purposely left out. Consult the 2D convolution sample for a complete application with proper error handling.

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 // For image I/O
30 #include <opencv2/core/version.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 <iostream>
38 
39 // All vpi headers are under directory vpi/
40 #include <vpi/Image.h>
41 #include <vpi/Stream.h>
42 
43 // Algorithm we'll need. Blurring will be performed by a box filter.
44 #include <vpi/algo/BoxFilter.h>
45 
46 int main(int argc, char *argv[])
47 {
48  if (argc != 2)
49  {
50  std::cerr << "Must pass an input image to be blurred" << std::endl;
51  return 1;
52  }
53 
54  // Phase 1: Initialization ---------------------------------
55 
56  // First load the input image
57  cv::Mat cvImage = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
58  if (cvImage.data == NULL)
59  {
60  std::cerr << "Can't open input image" << std::endl;
61  return 2;
62  }
63 
64  // Now create the stream. Passing 0 allows algorithms submitted to it to run
65  // in any available backend.
66  VPIStream stream;
67  vpiStreamCreate(0, &stream);
68 
69  // Now wrap the loaded image into a VPIImage object to be used by VPI.
70 
71  // First fill VPIImageData with all image parameters.
72  VPIImageData imgData;
73 
74  // OpenCV guarantees that cvImage has just one plane (hence
75  // grayscale), with 8 bits per pixel, unsigned.
76  imgData.numPlanes = 1;
77  imgData.type = VPI_IMAGE_FORMAT_U8;
78 
79  // Define the image dimensions, the length in bytes of each row (pitchBytes)
80  // and finally a pointer to the actual image data.
81  imgData.planes[0].width = cvImage.cols;
82  imgData.planes[0].height = cvImage.rows;
83  imgData.planes[0].pitchBytes = cvImage.step[0];
84  imgData.planes[0].data = cvImage.data;
85  // pixel format will be inferred from image type
87 
88  // Wrap it into a VPIImage. VPI won't make a copy of it, so the original
89  // image must be in scope at all times. The parameters are:
90  // 1. the imgData structure we've filled above.
91  // 2. Image flags. Passing 0 tells vpi to make the image available to all
92  // backends. One could restrict the backends this image can work with so
93  // that possibly less memory is used in some scenarios.
94  // 3. The variable that will receive a handle to the created image.
95  VPIImage image;
96  vpiImageCreateHostMemWrapper(&imgData, 0, &image);
97 
98  // Now create the output images, single unsigned 8-bit channel. The image lifetime is
99  // managed by VPI.
100  VPIImage blurred;
101  vpiImageCreate(cvImage.cols, cvImage.rows, VPI_IMAGE_FORMAT_U8, 0, &blurred);
102 
103  // Phase 2: main processing --------------------------------------
104 
105  // Submit the algorithm task for processing along with inputs and outputs
106  // Parameters are:
107  // 1. the stream on which the algorithm will run
108  // 2. Which hardware backend will execute it. Here CUDA is specified, but it could be CPU or
109  // PVA (on Jetson Xavier devices). No further changes to the program are needed to have it executed
110  // on a different hardware.
111  // 3. input image to be blurred.
112  // 4. output image with the result.
113  // 5 and 6. box filter size, in this case, 5x5
114  // 7. Boundary condition for when the algorithm tries to sample pixels outside the image boundary.
115  // VPI_BOUNDARY_COND_ZERO will consider all such pixels as being 0.
116  vpiSubmitBoxFilter(stream, VPI_BACKEND_CUDA, image, blurred, 5, 5, VPI_BOUNDARY_COND_ZERO);
117 
118  // Block the current thread until until the stream finishes all tasks submitted to it up till now.
119  vpiStreamSync(stream);
120 
121  // Finally retrieve the output image contents and output it to disk
122 
123  // Lock output image to retrieve its data from cpu memory
124  VPIImageData outData;
125  vpiImageLock(blurred, VPI_LOCK_READ, &outData);
126 
127  // Construct an cv::Mat out of the retrieved data.
128  cv::Mat cvOut(outData.planes[0].height, outData.planes[0].width, CV_8UC1, outData.planes[0].data,
129  outData.planes[0].pitchBytes);
130  // Just write it to disk
131  imwrite("tutorial_blurred.png", cvOut);
132 
133  // Done handling output image, don't forget to unlock it.
134  vpiImageUnlock(blurred);
135 
136  // Stage 3: clean up --------------------------------------
137 
138  // Destroy all created objects.
139  vpiStreamDestroy(stream);
140  vpiImageDestroy(image);
141  vpiImageDestroy(blurred);
142 
143  return 0;
144 }

Building and Testing the Application

With everything set in place, execute:

cmake .
make

The executable vpi_blur is created in the same directory.

To run the application, execute:

./vpi_blur <image file name>

substituting <image file name> by some image on disk.

A sample input image and the resulting blurred image is displayed. Note that although the input in in color, OpenCV converts the to grayscale prior passing it to VPI which results in grayscale output.

Input image Output image, blurred
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
vpiStreamCreate
VPIStatus vpiStreamCreate(uint32_t flags, VPIStream *stream)
Create a stream instance.
BoxFilter.h
Declares functions that implement the Box Filter algorithm.
VPIImagePlane::pixelFormat
VPIPixelFormat pixelFormat
Type of each pixel within this plane.
Definition: Image.h:136
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.
VPI_PIXEL_FORMAT_DEFAULT
@ VPI_PIXEL_FORMAT_DEFAULT
Used to signal that the pixel format must be inferred from image format.
Definition: PixelFormat.h:81
vpiSubmitBoxFilter
VPIStatus vpiSubmitBoxFilter(VPIStream stream, VPIBackend backend, VPIImage input, VPIImage output, uint32_t kernelSizeX, uint32_t kernelSizeY, VPIBoundaryCond boundary)
Runs a 2D box filter over an image.
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
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