VPI - Vision Programming Interface

0.2.0 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.

cmake_minimum_required(VERSION 3.5)
project(vpi_blur)
# This instructs cmake to look for the most recent
# vpi instance installed on the system.
find_package(vpi REQUIRED)
find_package(OpenCV REQUIRED)
# Creates the blur executable target
add_executable(vpi_blur main.cpp)
# It uses vpi and opencv. CMake will automatically
# set up the correct header and library directories,
# and make hello_work link to these libraries.
target_link_libraries(vpi_blur vpi opencv_core)
# OpenCV < 3 uses a different library for image i/o
if(OpenCV_VERSION VERSION_LESS 3)
target_link_libraries(vpi_blur opencv_highgui)
else()
target_link_libraries(vpi_blur opencv_imgcodecs)
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.

/*
* Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// For image I/O
#include <opencv2/core/version.hpp>
#if CV_MAJOR_VERSION >= 3
# include <opencv2/imgcodecs.hpp>
#else
# include <opencv2/highgui/highgui.hpp>
#endif
#include <iostream>
// All vpi headers are under directory vpi/
#include <vpi/Image.h>
#include <vpi/Stream.h>
// Algorithm we'll need. We'll be using gaussian blur and since it's
// a separable filter, we can leverage VPI's box filter to do it
// efficiently.
int main(int argc, char *argv[])
{
if (argc != 2)
{
std::cerr << "Must pass an input image to be blurred" << std::endl;
return 1;
}
// Phase 1: Initialization ---------------------------------
// First load the input image
cv::Mat cvImage = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
if (cvImage.data == NULL)
{
std::cerr << "Can't open input image" << std::endl;
return 2;
}
// Now create the stream that uses the CUDA backend. To make it work with
// other backends, pass VPI_DEVICE_TYPE_CPU or VPI_DEVICE_TYPE_PVA. No
// other changes to the code would be needed.
VPIStream stream;
// Now wrap the loaded image into a VPIImage object to be used by VPI.
// First fill VPIImageData with all image parameters.
VPIImageData imgData;
// OpenCV guarantees that cvImage has just one plane (hence
// grayscale), with 8 bits per pixel, unsigned.
imgData.numPlanes = 1;
// Define the image dimensions, the length in bytes of each row (rowStride)
// and finally a pointer to the actual image data.
imgData.planes[0].width = cvImage.cols;
imgData.planes[0].height = cvImage.rows;
imgData.planes[0].rowStride = cvImage.step[0];
imgData.planes[0].data = cvImage.data;
// pixel type will be inferred from image type
// Wrap it into a VPIImage. VPI won't make a copy of it, so the original
// image must be in scope at all times. The parameters are:
// 1. the imgData structure we've filled above.
// 2. The image type. This represents how pixels are to be interpreted. The
// loaded image is unsigned grayscale, with 8 bits per pixel, hence the VPI
// type is U8. For signed, it would have been S8.
// 3. Flags. 0 tells vpi to make the image available to all backends. One
// could restrict the backends this image can work with so that possibly
// less memory is used in some scenarios.
// 4. The VPIImage variable that will receive the created image handle.
VPIImage image;
vpiImageWrapHostMem(&imgData, 0, &image);
// Now create the output images, single unsigned 8-bit channel.
VPIImage blurred;
vpiImageCreate(cvImage.cols, cvImage.rows, VPI_IMAGE_TYPE_U8, 0, &blurred);
// Phase 2: main processing --------------------------------------
// Submit the algorithm task for processing along with inputs and outputs
// Parameters are:
// 1. the stream on which the algorithm will run
// 2. input image
// 3. output image
// 4 and 5. box filter size, in this case, 5x5
// 6. Boundary condition for when the algorithm tries to sample pixels outside the image boundary.
// VPI_BOUNDARY_COND_ZERO will consider all such pixels as being 0.
vpiSubmitBoxImageFilter(stream, image, blurred, 5, 5, VPI_BOUNDARY_COND_ZERO);
// Wait until the algorithm finishes processing by synchronizing to the stream.
// It'll blockuntil the given stream finishes all tasks submitted to it up till now.
vpiStreamSync(stream);
// Finally retrieve the output image contents and output it to disk
// Lock output image to retrieve its data from cpu memory
VPIImageData outData;
vpiImageLock(blurred, VPI_LOCK_READ, &outData);
// Construct an cv::Mat out of the retrieved data.
cv::Mat cvOut(outData.planes[0].height, outData.planes[0].width, CV_8UC1, outData.planes[0].data,
outData.planes[0].rowStride);
// Just write it to disk
imwrite("tutorial_blurred.png", cvOut);
// Done handling output image, don't forget to unlock it.
vpiImageUnlock(blurred);
// Stage 3: clean up --------------------------------------
vpiImageDestroy(blurred);
return 0;
}

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
VPI_PIXEL_TYPE_DEFAULT
@ VPI_PIXEL_TYPE_DEFAULT
Used to signal that the pixel type must be inferred from image type.
Definition: Types.h:220
VPIImagePlane::height
uint32_t height
Height of this plane in pixels.
Definition: Image.h:131
VPIImagePlane::width
uint32_t width
Width of this plane in pixels.
Definition: Image.h:130
vpiSubmitBoxImageFilter
VPIStatus vpiSubmitBoxImageFilter(VPIStream stream, VPIImage input, VPIImage output, uint32_t kernelSizeX, uint32_t kernelSizeY, VPIBoundaryCond boundary)
Runs a 2D box filter over an image.
BoxImageFilter.h
VPIImagePlane::rowStride
uint32_t rowStride
Difference in bytes of beginning of one row and the beginning of the previous.
Definition: Image.h:132
vpiImageUnlock
VPIStatus vpiImageUnlock(VPIImage img)
Releases the lock on an image object.
vpiStreamCreate
VPIStatus vpiStreamCreate(VPIDeviceType devType, VPIStream *stream)
Create a stream instance.
vpiStreamSync
VPIStatus vpiStreamSync(VPIStream stream)
Blocks the calling thread until all submitted commands in this stream queue are done (queue is empty)...
VPIImageData
Stores information about image characteristics and content.
Definition: Image.h:149
vpiStreamDestroy
void vpiStreamDestroy(VPIStream stream)
Destroy a stream instance and deallocate all HW resources.
VPI_LOCK_READ
@ VPI_LOCK_READ
Lock memory only for reading.
Definition: Types.h:466
VPIImageData::planes
VPIImagePlane planes[VPI_MAX_PLANE_COUNT]
Data of all image planes.
Definition: Image.h:156
vpiImageDestroy
void vpiImageDestroy(VPIImage img)
Destroy an image instance as well as all resources it owns.
VPI_IMAGE_TYPE_U8
@ VPI_IMAGE_TYPE_U8
unsigned 8-bit grayscale.
Definition: Types.h:192
VPIImagePlane::pixelType
VPIPixelType pixelType
Type of each pixel within this plane.
Definition: Image.h:129
Image.h
VPIImage
struct VPIImageImpl * VPIImage
Definition: Types.h:170
VPIImageData::numPlanes
int32_t numPlanes
Number of planes.
Definition: Image.h:151
vpiImageLock
VPIStatus vpiImageLock(VPIImage img, VPILockMode mode, VPIImageData *hostData)
Acquires the lock on an image object and returns a pointer to the image planes Depending on the inter...
VPIImageData::type
VPIImageType type
Image type.
Definition: Image.h:150
vpiImageCreate
VPIStatus vpiImageCreate(uint32_t width, uint32_t height, VPIImageType type, uint32_t flags, VPIImage *img)
Create an empty image instance with the specified flags.
VPI_BOUNDARY_COND_ZERO
@ VPI_BOUNDARY_COND_ZERO
All pixels outside the image are considered to be zero.
Definition: Types.h:251
vpiImageWrapHostMem
VPIStatus vpiImageWrapHostMem(const VPIImageData *hostData, uint32_t flags, VPIImage *img)
Create an image object by wrapping around an existing host-memory block.
Stream.h
VPIImagePlane::data
void * data
Pointer to the first row of this plane.
Definition: Image.h:140
VPIStream
struct VPIStreamImpl * VPIStream
Definition: Types.h:164
VPI_DEVICE_TYPE_CUDA
@ VPI_DEVICE_TYPE_CUDA
CUDA backend.
Definition: Types.h:516