VPI - 视觉编程接口

3.2 版本

Harris 角点检测器

概述

此应用程序从给定的输入图像中检测 Harris 角点。然后将角点绘制到输入图像上,并将结果作为图像保存在磁盘上。

指令

命令行参数为

<后端> <输入图像>

其中

  • backend: 可以是 cpucudapva;它定义了将执行处理的后端。
  • input image: 输入图像文件名,它接受 png、jpeg 以及可能的其他格式。

这是一个示例

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

这是使用 PVA 后端和提供的示例图像之一。您可以尝试其他输入图像,但需遵守算法施加的约束。如果您的流不支持 PVA,则会打印错误消息。在这种情况下,请尝试另一个后端。

结果

输入图像输出图像,Harris 关键点

源代码

为了方便起见,这里提供了也安装在 samples 目录中的代码。

语言
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("后端 '" + strBackend +
146  "' 无法识别,它必须是 cpu、cuda 或 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("无法打开 '" + strInputFileName + "'");
156  }
157 
158  // =================================
159  // Allocate all VPI resources needed
160 
161  // Create the stream where processing will happen
162  CHECK_STATUS(vpiStreamCreate(0, &stream));
163 
164  // We now wrap the loaded image into a VPIImage object to be used by VPI.
165  // VPI won't make a copy of it, so the original
166  // image must be in scope at all times.
167  CHECK_STATUS(vpiImageCreateWrapperOpenCVMat(cvImage, 0, &imgInput));
168 
169  CHECK_STATUS(vpiImageCreate(cvImage.cols, cvImage.rows, VPI_IMAGE_FORMAT_S16, 0, &imgGrayscale));
170 
171  // Create the output keypoint array. Currently for PVA backend it must have 8192 elements.
172  CHECK_STATUS(vpiArrayCreate(8192, VPI_ARRAY_TYPE_KEYPOINT_F32, 0, &keypoints));
173 
174  // Create the output scores array. It also must have 8192 elements and elements must be uint32_t.
175  CHECK_STATUS(vpiArrayCreate(8192, VPI_ARRAY_TYPE_U32, 0, &scores));
176 
177  // Create the payload for Harris Corners Detector algorithm
178  CHECK_STATUS(vpiCreateHarrisCornerDetector(backend, cvImage.cols, cvImage.rows, &harris));
179 
180  // Define the algorithm parameters. We'll use defaults, expect for sensitivity.
181  VPIHarrisCornerDetectorParams harrisParams;
182  CHECK_STATUS(vpiInitHarrisCornerDetectorParams(&harrisParams));
183  harrisParams.sensitivity = 0.01;
184 
185  // ================
186  // Processing stage
187 
188  // First convert input to grayscale
189  CHECK_STATUS(vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, imgInput, imgGrayscale, NULL));
190 
191  // Then get Harris corners
192  CHECK_STATUS(
193  vpiSubmitHarrisCornerDetector(stream, backend, harris, imgGrayscale, keypoints, scores, &harrisParams));
194 
195  // Wait until the algorithm finishes processing
196  CHECK_STATUS(vpiStreamSync(stream));
197 
198  // =======================================
199  // Output processing and saving it to disk
200 
201  // Lock output keypoints and scores to retrieve its data on cpu memory
202  VPIArrayData outKeypointsData;
203  VPIArrayData outScoresData;
204  VPIImageData imgData;
205  CHECK_STATUS(vpiArrayLockData(keypoints, VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &outKeypointsData));
206  CHECK_STATUS(vpiArrayLockData(scores, VPI_LOCK_READ, VPI_ARRAY_BUFFER_HOST_AOS, &outScoresData));
207  CHECK_STATUS(vpiImageLockData(imgGrayscale, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &imgData));
208 
209  VPIKeypointF32 *outKeypoints = (VPIKeypointF32 *)outKeypointsData.buffer.aos.data;
210  uint32_t *outScores = (uint32_t *)outScoresData.buffer.aos.data;
211 
212  printf("\n%d keypoints found\n", *outKeypointsData.buffer.aos.sizePointer);
213 
214  cv::Mat img;
215  CHECK_STATUS(vpiImageDataExportOpenCVMat(imgData, &img));
216 
217  cv::Mat outImage = DrawKeypoints(img, outKeypoints, outScores, *outKeypointsData.buffer.aos.sizePointer);
218 
219  imwrite("harris_corners_" + strBackend + ".png", outImage);
220 
221  // Done handling outputs, don't forget to unlock them.
222  CHECK_STATUS(vpiImageUnlock(imgGrayscale));
223  CHECK_STATUS(vpiArrayUnlock(scores));
224  CHECK_STATUS(vpiArrayUnlock(keypoints));
225  }
226  catch (std::exception &e)
227  {
228  std::cerr << e.what() << std::endl;
229  retval = 1;
230  }
231 
232  // ========
233  // Clean up
234 
235  // Make sure stream is synchronized before destroying the objects
236  // that might still be in use.
237  if (stream != NULL)
238  {
239  vpiStreamSync(stream);
240  }
241 
242  vpiImageDestroy(imgInput);
243  vpiImageDestroy(imgGrayscale);
244  vpiArrayDestroy(keypoints);
245  vpiArrayDestroy(scores);
246  vpiPayloadDestroy(harris);
247  vpiStreamDestroy(stream);
248 
249  return retval;
250 }
用于处理 VPI 数组的函数和结构。
声明处理图像格式转换的函数。
声明实现 Harris 角点检测器算法的函数。
#define VPI_IMAGE_FORMAT_S16
单平面,带有一个 16 位有符号整数通道。
用于处理 VPI 图像的函数和结构。
用于处理 OpenCV 与 VPI 互操作性的函数。
VPI 状态码处理函数的声明。
声明处理 VPI 流的函数。
void * data
指向数组的第一个元素。
定义: Array.h:135
VPIArrayBuffer buffer
存储数组内容。
定义: Array.h:175
int32_t * sizePointer
指向数组中元素的数量。
定义: Array.h:122
VPIArrayBufferAOS aos
以结构数组布局存储的数组。
定义: Array.h:162
VPIStatus vpiArrayUnlock(VPIArray array)
释放数组对象的锁定。
VPIStatus vpiArrayLockData(VPIArray array, VPILockMode mode, VPIArrayBufferType bufType, VPIArrayData *data)
获取数组对象的锁定并返回数组内容。
void vpiArrayDestroy(VPIArray array)
销毁数组实例。
VPIStatus vpiArrayCreate(int32_t capacity, VPIArrayType type, uint64_t flags, VPIArray *array)
创建空数组实例。
struct VPIArrayImpl * VPIArray
数组的句柄。
定义: Types.h:232
@ VPI_ARRAY_TYPE_U32
无符号 32 位。
定义: ArrayType.h:76
@ VPI_ARRAY_TYPE_KEYPOINT_F32
VPIKeypointF32 元素。
定义: ArrayType.h:77
@ VPI_ARRAY_BUFFER_HOST_AOS
主机可访问的结构体数组。
定义: Array.h:146
存储关于数组特性和内容的信息。
定义: Array.h:168
VPIStatus vpiSubmitConvertImageFormat(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, const VPIConvertImageFormatParams *params)
将图像内容转换为所需的格式,可选择缩放和偏移。
float sensitivity
指定 Harris-Stephens 方程的灵敏度阈值。
VPIStatus vpiSubmitHarrisCornerDetector(VPIStream stream, uint64_t backend, VPIPayload payload, VPIImage input, VPIArray outFeatures, VPIArray outScores, const VPIHarrisCornerDetectorParams *params)
向流提交 Harris 角点检测器操作。
VPIStatus vpiInitHarrisCornerDetectorParams(VPIHarrisCornerDetectorParams *params)
使用默认值初始化 VPIHarrisCornerDetectorParams。
VPIStatus vpiCreateHarrisCornerDetector(uint64_t backends, int32_t inputWidth, int32_t inputHeight, VPIPayload *payload)
创建 Harris 角点检测器负载。
定义 vpiSubmitHarrisCornerDetector 参数的结构体。
void vpiImageDestroy(VPIImage img)
销毁图像实例。
struct VPIImageImpl * VPIImage
图像的句柄。
定义: Types.h:256
VPIStatus vpiImageLockData(VPIImage img, VPILockMode mode, VPIImageBufferType bufType, VPIImageData *data)
获取图像对象的锁并返回图像内容。
VPIStatus vpiImageCreate(int32_t width, int32_t height, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
使用指定的标志创建空图像实例。
VPIStatus vpiImageUnlock(VPIImage img)
释放图像对象的锁。
@ VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR
主机可访问,平面采用 pitch-linear 内存布局。
定义: Image.h:172
存储关于图像特性和内容的信息。
定义: Image.h:234
VPIStatus vpiImageCreateWrapperOpenCVMat(const cv::Mat &mat, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
使用给定的图像格式将 cv::Mat 封装到 VPIImage 中。
VPIStatus vpiImageDataExportOpenCVMat(const VPIImageData &imgData, cv::Mat *mat)
使用来自锁定 VPIImage 的 VPIImageData 数据填充现有的 cv::Mat。
struct VPIPayloadImpl * VPIPayload
算法负载的句柄。
定义: Types.h:268
void vpiPayloadDestroy(VPIPayload payload)
释放负载对象和所有相关的资源。
struct VPIStreamImpl * VPIStream
流的句柄。
定义: Types.h:250
VPIStatus vpiStreamSync(VPIStream stream)
阻塞调用线程,直到此流队列中所有提交的命令都完成(队列为空)。。。
VPIBackend
VPI 后端类型。
定义: Types.h:91
void vpiStreamDestroy(VPIStream stream)
销毁流实例并释放所有硬件资源。
VPIStatus vpiStreamCreate(uint64_t flags, VPIStream *stream)
创建流实例。
@ VPI_BACKEND_CUDA
CUDA 后端。
定义: Types.h:93
@ VPI_BACKEND_PVA
PVA 后端。
定义: Types.h:94
@ VPI_BACKEND_CPU
CPU 后端。
定义: Types.h:92
@ VPI_LOCK_READ
锁定内存仅用于读取。
定义: Types.h:617
存储 float32 关键点坐标。坐标相对于图像的左上角。
定义: Types.h:334