VPI - Vision Programming Interface

4.0 Release

Harris Corners Detector

Overview

This application detects Harris corners from a given input image. The corners are then drawn onto the input image and the result is saved as an image on disk.

Instructions

The command line parameters are:

<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:

  • C++
    ./vpi_sample_03_harris_corners pva ../assets/kodim08.png
  • Python
    python3 main.py pva ../assets/kodim08.png

This is using the PVA backend and one of the provided sample images. You can try with other input images, respecting the constraints imposed by the algorithm. If your stream doesn't support PVA, an error is printed. In this case, just try another backend.

Results

Input imageOutput image, Harris keypoints

Source Code

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

Language:
27 import sys
28 import vpi
29 import numpy as np
30 from argparse import ArgumentParser
31 import cv2
32 
33 
34 # ----------------------------
35 # Parse command line arguments
36 
37 parser = ArgumentParser()
38 parser.add_argument('backend', choices=['cpu','cuda','pva'],
39  help='Backend to be used for processing')
40 
41 parser.add_argument('input',
42  help='Input image on which harris corners will be detected')
43 
44 args = parser.parse_args();
45 
46 if args.backend == 'cpu':
47  backend = vpi.Backend.CPU
48 elif args.backend == 'cuda':
49  backend = vpi.Backend.CUDA
50 else:
51  assert args.backend == 'pva'
52  backend = vpi.Backend.PVA
53 
54 # --------------------------------------------------------------
55 # Load input into a vpi.Image and convert it to grayscale, signed 16bpp
56 with vpi.Backend.CUDA:
57  input = vpi.asimage(cv2.imread(args.input), vpi.Format.BGR8).convert(vpi.Format.S16)
58 
59 with backend:
60  corners, scores = input.harriscorners(sensitivity=0.01)
61 
62 # ---------------------------------------
63 # Render the keypoints in the output image
64 
65 out = input.convert(vpi.Format.BGR8, backend=vpi.Backend.CUDA)
66 
67 if corners.size > 0:
68  with out.lock_cpu() as out_data, scores.lock_cpu() as scores_data, corners.lock_cpu() as corners_data:
69  cmap = cv2.applyColorMap(np.arange(0, 256, dtype=np.uint8), cv2.COLORMAP_HOT)
70 
71  maxscore = scores_data.max()
72 
73  for i in range(corners.size):
74  color = tuple([int(x) for x in cmap[255*scores_data[i]//maxscore,0]])
75  kpt = tuple(corners_data[i].astype(np.int16))
76  cv2.circle(out_data, kpt, 3, color, -1)
77 
78 # -------------------
79 # Save result to disk
80 cv2.imwrite('harris_corners_python'+str(sys.version_info[0])+'_'+args.backend+'.png', out.cpu())
29 #include <opencv2/core/version.hpp>
30 #if CV_MAJOR_VERSION >= 3
31 # include <opencv2/imgcodecs.hpp>
32 #else
33 # include <opencv2/contrib/contrib.hpp> // for applyColorMap
34 # include <opencv2/highgui/highgui.hpp>
35 #endif
36 
37 #include <opencv2/imgproc/imgproc.hpp>
38 #include <vpi/OpenCVInterop.hpp>
39 
40 #include <vpi/Array.h>
41 #include <vpi/Image.h>
42 #include <vpi/Status.h>
43 #include <vpi/Stream.h>
45 #include <vpi/algo/HarrisCorners.h>
46 
47 #include <cstdio>
48 #include <cstring> // for memset
49 #include <iostream>
50 #include <sstream>
51 
52 #define CHECK_STATUS(STMT) \
53  do \
54  { \
55  VPIStatus status = (STMT); \
56  if (status != VPI_SUCCESS) \
57  { \
58  char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
59  vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
60  std::ostringstream ss; \
61  ss << vpiStatusGetName(status) << ": " << buffer; \
62  throw std::runtime_error(ss.str()); \
63  } \
64  } while (0);
65 
66 static cv::Mat DrawKeypoints(cv::Mat img, VPIKeypointF32 *kpts, uint32_t *scores, int numKeypoints)
67 {
68  cv::Mat out;
69  img.convertTo(out, CV_8UC1);
70  cvtColor(out, out, cv::COLOR_GRAY2BGR);
71 
72  if (numKeypoints == 0)
73  {
74  return out;
75  }
76 
77  // prepare our colormap
78  cv::Mat cmap(1, 256, CV_8UC3);
79  {
80  cv::Mat gray(1, 256, CV_8UC1);
81  for (int i = 0; i < 256; ++i)
82  {
83  gray.at<unsigned char>(0, i) = i;
84  }
85  applyColorMap(gray, cmap, cv::COLORMAP_HOT);
86  }
87 
88  float maxScore = *std::max_element(scores, scores + numKeypoints);
89 
90  for (int i = 0; i < numKeypoints; ++i)
91  {
92  cv::Vec3b color = cmap.at<cv::Vec3b>(scores[i] / maxScore * 255);
93  circle(out, cv::Point(kpts[i].x, kpts[i].y), 3, cv::Scalar(color[0], color[1], color[2]), -1);
94  }
95 
96  return out;
97 }
98 
99 int main(int argc, char *argv[])
100 {
101  // OpenCV image that will be wrapped by a VPIImage.
102  // Define it here so that it's destroyed *after* wrapper is destroyed
103  cv::Mat cvImage;
104 
105  // VPI objects that will be used
106  VPIImage imgInput = NULL;
107  VPIImage imgGrayscale = NULL;
108  VPIArray keypoints = NULL;
109  VPIArray scores = NULL;
110  VPIStream stream = NULL;
111  VPIPayload harris = NULL;
112 
113  int retval = 0;
114 
115  try
116  {
117  // =============================
118  // Parse command line parameters
119 
120  if (argc != 3)
121  {
122  throw std::runtime_error(std::string("Usage: ") + argv[0] + " <cpu|pva|cuda> <input image>");
123  }
124 
125  std::string strBackend = argv[1];
126  std::string strInputFileName = argv[2];
127 
128  // Now parse the backend
129  VPIBackend backend;
130 
131  if (strBackend == "cpu")
132  {
133  backend = VPI_BACKEND_CPU;
134  }
135  else if (strBackend == "cuda")
136  {
137  backend = VPI_BACKEND_CUDA;
138  }
139  else if (strBackend == "pva")
140  {
141  backend = VPI_BACKEND_PVA;
142  }
143  else
144  {
145  throw std::runtime_error("Backend '" + strBackend +
146  "' not recognized, it must be either cpu, cuda or pva.");
147  }
148 
149  // =====================
150  // Load the input image
151 
152  cvImage = cv::imread(strInputFileName);
153  if (cvImage.empty())
154  {
155  throw std::runtime_error("Can't open '" + strInputFileName + "'");
156  }
157 
158  // =================================
159  // Allocate all VPI resources needed
160 
161  // Create adjusted backend type that includes CPU and CUDA
162  VPIBackend adjBackend = static_cast<VPIBackend>(backend | VPI_BACKEND_CUDA | VPI_BACKEND_CPU);
163 
164  // Create the stream where processing will happen
165  CHECK_STATUS(vpiStreamCreate(adjBackend, &stream));
166 
167  // We now wrap the loaded image into a VPIImage object to be used by VPI.
168  // VPI won't make a copy of it, so the original
169  // image must be in scope at all times.
170  CHECK_STATUS(vpiImageCreateWrapperOpenCVMat(cvImage, adjBackend, &imgInput));
171 
172  CHECK_STATUS(vpiImageCreate(cvImage.cols, cvImage.rows, VPI_IMAGE_FORMAT_S16, adjBackend, &imgGrayscale));
173 
174  // Create the output keypoint array. Currently for PVA backend it must have 8192 elements.
175  CHECK_STATUS(vpiArrayCreate(8192, VPI_ARRAY_TYPE_KEYPOINT_F32, adjBackend, &keypoints));
176 
177  // Create the output scores array. It also must have 8192 elements and elements must be uint32_t.
178  CHECK_STATUS(vpiArrayCreate(8192, VPI_ARRAY_TYPE_U32, adjBackend, &scores));
179 
180  // Create the payload for Harris Corners Detector algorithm
181  CHECK_STATUS(vpiCreateHarrisCornerDetector(backend, cvImage.cols, cvImage.rows, &harris));
182 
183  // Define the algorithm parameters. We'll use defaults, expect for sensitivity.
184  VPIHarrisCornerDetectorParams harrisParams;
185  CHECK_STATUS(vpiInitHarrisCornerDetectorParams(&harrisParams));
186  harrisParams.sensitivity = 0.01;
187 
188  // ================
189  // Processing stage
190 
191  // First convert input to grayscale
192  CHECK_STATUS(vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, imgInput, imgGrayscale, NULL));
193 
194  // Then get Harris corners
195  CHECK_STATUS(
196  vpiSubmitHarrisCornerDetector(stream, backend, harris, imgGrayscale, keypoints, scores, &harrisParams));
197 
198  // Wait until the algorithm finishes processing
199  CHECK_STATUS(vpiStreamSync(stream));
200 
201  // =======================================
202  // Output processing and saving it to disk
203 
204  // Lock output keypoints and scores to retrieve its data on cpu memory
205  VPIArrayData outKeypointsData;
206  VPIArrayData outScoresData;
207  VPIImageData imgData;
208  CHECK_STATUS(vpiArrayLockData(keypoints, VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &outKeypointsData));
209  CHECK_STATUS(vpiArrayLockData(scores, VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &outScoresData));
210  CHECK_STATUS(vpiImageLockData(imgGrayscale, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &imgData));
211 
212  VPIKeypointF32 *outKeypoints = (VPIKeypointF32 *)outKeypointsData.buffer.aos.data;
213  uint32_t *outScores = (uint32_t *)outScoresData.buffer.aos.data;
214 
215  printf("\n%d keypoints found\n", *outKeypointsData.buffer.aos.sizePointer);
216 
217  cv::Mat img;
218  CHECK_STATUS(vpiImageDataExportOpenCVMat(imgData, &img));
219 
220  cv::Mat outImage = DrawKeypoints(img, outKeypoints, outScores, *outKeypointsData.buffer.aos.sizePointer);
221 
222  imwrite("harris_corners_" + strBackend + ".png", outImage);
223 
224  // Done handling outputs, don't forget to unlock them.
225  CHECK_STATUS(vpiImageUnlock(imgGrayscale));
226  CHECK_STATUS(vpiArrayUnlock(scores));
227  CHECK_STATUS(vpiArrayUnlock(keypoints));
228  }
229  catch (std::exception &e)
230  {
231  std::cerr << e.what() << std::endl;
232  retval = 1;
233  }
234 
235  // ========
236  // Clean up
237 
238  // Make sure stream is synchronized before destroying the objects
239  // that might still be in use.
240  if (stream != NULL)
241  {
242  vpiStreamSync(stream);
243  }
244 
245  vpiImageDestroy(imgInput);
246  vpiImageDestroy(imgGrayscale);
247  vpiArrayDestroy(keypoints);
248  vpiArrayDestroy(scores);
249  vpiPayloadDestroy(harris);
250  vpiStreamDestroy(stream);
251 
252  return retval;
253 }
Functions and structures for dealing with VPI arrays.
Declares functions that handle image format conversion.
Declares functions that implement the Harris Corner Detector algorithm.
#define VPI_IMAGE_FORMAT_S16
Single plane with one 16-bit signed integer channel.
Definition: ImageFormat.h:120
Functions and structures for dealing with VPI images.
Functions for handling OpenCV interoperability with VPI.
Declaration of VPI status codes handling functions.
Declares functions dealing with VPI streams.
void * data
Points to the first element of the array.
Definition: Array.h:135
VPIArrayBuffer buffer
Stores the array contents.
Definition: Array.h:175
int32_t * sizePointer
Points to the number of elements in the array.
Definition: Array.h:122
VPIArrayBufferAOS aos
Array stored in array-of-structures layout.
Definition: Array.h:162
VPIStatus vpiArrayUnlock(VPIArray array)
Releases the lock on array object.
VPIStatus vpiArrayLockData(VPIArray array, VPILockMode mode, VPIArrayBufferType bufType, VPIArrayData *data)
Acquires the lock on an array object and returns the array contents.
void vpiArrayDestroy(VPIArray array)
Destroy an array instance.
VPIStatus vpiArrayCreate(int32_t capacity, VPIArrayType type, uint64_t flags, VPIArray *array)
Create an empty array instance.
struct VPIArrayImpl * VPIArray
A handle to an array.
Definition: Types.h:230
@ VPI_ARRAY_TYPE_U32
Unsigned 32-bit.
Definition: ArrayType.h:76
@ VPI_ARRAY_TYPE_KEYPOINT_F32
VPIKeypointF32 element.
Definition: ArrayType.h:77
@ VPI_ARRAY_BUFFER_HOST_AOS
Host-accessible array-of-structures.
Definition: Array.h:146
Stores information about array characteristics and contents.
Definition: Array.h:168
VPIStatus vpiSubmitConvertImageFormat(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, const VPIConvertImageFormatParams *params)
Converts the image contents to the desired format, with optional scaling and offset.
float sensitivity
Specifies sensitivity threshold from the Harris-Stephens equation.
Definition: HarrisCorners.h:97
VPIStatus vpiSubmitHarrisCornerDetector(VPIStream stream, uint64_t backend, VPIPayload payload, VPIImage input, VPIArray outFeatures, VPIArray outScores, const VPIHarrisCornerDetectorParams *params)
Submits a Harris Corner Detector operation to the stream.
VPIStatus vpiInitHarrisCornerDetectorParams(VPIHarrisCornerDetectorParams *params)
Initializes VPIHarrisCornerDetectorParams with default values.
VPIStatus vpiCreateHarrisCornerDetector(uint64_t backends, int32_t inputWidth, int32_t inputHeight, VPIPayload *payload)
Creates a Harris Corner Detector payload.
Structure that defines the parameters for vpiSubmitHarrisCornerDetector.
Definition: HarrisCorners.h:82
void vpiImageDestroy(VPIImage img)
Destroy an image instance.
struct VPIImageImpl * VPIImage
A handle to an image.
Definition: Types.h:254
VPIStatus vpiImageLockData(VPIImage img, VPILockMode mode, VPIImageBufferType bufType, VPIImageData *data)
Acquires the lock on an image object and returns the image contents.
VPIStatus vpiImageCreate(int32_t width, int32_t height, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
Create an empty image instance with the specified flags.
VPIStatus vpiImageUnlock(VPIImage img)
Releases the lock on an image object.
@ VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR
Host-accessible with planes in pitch-linear memory layout.
Definition: Image.h:176
Stores information about image characteristics and content.
Definition: Image.h:238
VPIStatus vpiImageCreateWrapperOpenCVMat(const cv::Mat &mat, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
Wraps a cv::Mat in an VPIImage with the given image format.
VPIStatus vpiImageDataExportOpenCVMat(const VPIImageData &imgData, cv::Mat *mat)
Fills an existing cv::Mat with data from VPIImageData coming from a locked VPIImage.
struct VPIPayloadImpl * VPIPayload
A handle to an algorithm payload.
Definition: Types.h:266
void vpiPayloadDestroy(VPIPayload payload)
Deallocates the payload object and all associated resources.
struct VPIStreamImpl * VPIStream
A handle to a stream.
Definition: Types.h:248
VPIStatus vpiStreamSync(VPIStream stream)
Blocks the calling thread until all submitted commands in this stream queue are done (queue is empty)...
VPIBackend
VPI Backend types.
Definition: Types.h:91
void vpiStreamDestroy(VPIStream stream)
Destroy a stream instance and deallocate all HW resources.
VPIStatus vpiStreamCreate(uint64_t flags, VPIStream *stream)
Create a stream instance.
@ VPI_BACKEND_CUDA
CUDA backend.
Definition: Types.h:93
@ VPI_BACKEND_PVA
PVA backend.
Definition: Types.h:94
@ VPI_BACKEND_CPU
CPU backend.
Definition: Types.h:92
@ VPI_LOCK_READ
Lock memory only for reading.
Definition: Types.h:621
Stores a float32 keypoint coordinate The coordinate is relative to the top-left corner of an image.
Definition: Types.h:338