VPI - Vision Programming Interface

3.2 版本

鱼眼畸变校正

概述

此示例应用程序使用同一相机/镜头拍摄的输入图像执行鱼眼镜头校准。然后,它使用 Remap 和校准数据来校正这些图像的鱼眼镜头畸变,并将结果保存到磁盘。用于畸变校正的映射是 VPI_FISHEYE_EQUIDISTANT,它将场景中的直线映射到校正图像中的直线。

镜头校准

镜头校准使用同一相机/镜头拍摄的一组图像,每个图像都显示一个位于不同位置的棋盘格图案,以便从整体上看,棋盘格几乎出现在整个视野中。图像越多,校准就越准确,但通常 10 到 15 张图像就足够了。

VPI 示例包含一组可以使用的输入图像。它们位于 /opt/nvidia/vpi3/samples/assets/fisheye 目录中。

注意
鱼眼 Python 示例仅适用于 OpenCV 版本 <= 4.9

要为给定的镜头创建一组校准图像,请执行以下操作

  1. 在纸上打印棋盘格图案。VPI 在示例的 assets 目录中提供了一个 10x7 棋盘格文件,可以使用,名为 checkerboard_10x7.pdf
  2. 将鱼眼镜头安装在相机上。
  3. 在相机位置固定的情况下,拍摄几张照片,显示位于不同位置的棋盘格,覆盖视野的大部分区域。

说明

命令行参数如下

-c W,H [-s win] <image1> [image2] [image3] ...

其中

  • -c W,H:指定棋盘格图案水平 (W) 和垂直 (H) 方向上的方格数。
  • -s win:(可选)在棋盘格的每个内部顶点(4 个方格相交的点)周围的窗口宽度,用于顶点位置细化阶段。实际的顶点位置将在此窗口内搜索。如果省略此参数,将跳过细化阶段。
  • imageN:校准图像集

这是一个调用示例

  • C++
    ./vpi_sample_11_fisheye -c 10,7 -s 22 ../assets/fisheye/*.jpg
  • Python
    python3 main.py -c 10,7 -s 22 ../assets/fisheye/*.jpg

这将校正包含的校准图像集,所有图像均使用也 包含 的棋盘格图案捕获。它在每个棋盘格内部顶点周围使用 22x22 窗口来细化顶点位置。

结果

以下是示例应用程序生成的一些输入和输出图像

输入已校正

源代码

为了方便起见,以下是在 samples 目录中也安装的代码。

语言
27 import sys
28 import vpi
29 import numpy as np
30 from argparse import ArgumentParser
31 import cv2
32 
33 # ============================
34 # Parse command line arguments
35 
36 # Changes in fisheye camera calibration function in OpenCV 4.10, leads to corrupted output.
37 # Get the major and minor version numbers
38 version = cv2.__version__.split('.')
39 major = int(version[0])
40 minor = int(version[1])
41 
42 # Check if the version is 4.10 or higher
43 if major * 100 + minor >= 410
44  raise Exception("OpenCV >= 4.10 isn't supported")
45 
46 parser = ArgumentParser()
47 parser.add_argument('-c', metavar='W,H', required=True,
48  help='Checkerboard with WxH squares')
49 
50 parser.add_argument('-s', metavar='win', type=int,
51  help='Search window width around checkerboard verted used in refinement, default is 0 (disable refinement)')
52 
53 parser.add_argument('images', nargs='+',
54  help='Input images taken with a fisheye lens camera')
55 
56 args = parser.parse_args();
57 
58 # Parse checkerboard size
59 try
60  cbSize = np.array([int(x) for x in args.c.split(',')])
61 except ValueError
62  exit("Error parsing checkerboard information")
63 
64 # =========================================
65 # Calculate fisheye calibration from images
66 
67 # OpenCV expects number of interior vertices in the checkerboard,
68 # not number of squares. Let's adjust for that.
69 vtxCount = cbSize-1
70 
71 # -------------------------------------------------
72 # Determine checkerboard coordinates in image space
73 
74 imgSize = None
75 corners2D = []
76 
77 for imgName in args.images
78  # Load input image and do some sanity check
79  img = cv2.imread(imgName)
80  curImgSize = (img.shape[1], img.shape[0])
81 
82  if imgSize == None
83  imgSize = curImgSize
84  elif imgSize != curImgSize
85  exit("All images must have the same size")
86 
87  # Find the checkerboard pattern on the image, saving the 2D
88  # coordinates of checkerboard vertices in cbVertices.
89  # Vertex is the point where 4 squares (2 white and 2 black) meet.
90  found, corners = cv2.findChessboardCorners(img, tuple(vtxCount), flags=cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE)
91  if found
92  # Needs to perform further corner refinement?
93  if args.s != None and args.s >= 2
94  criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 30, 0.0001)
95  imgGray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
96  corners = cv2.cornerSubPix(imgGray, corners, (args.s//2, args.s//2), (-1,-1), criteria)
97  corners2D.append(corners)
98  else
99  exit("Warning: checkerboard pattern not found in image {}".format(input))
100 
101 # Create the vector that stores 3D coordinates for each checkerboard pattern on a space
102 # where X and Y are orthogonal and run along the checkerboard sides, and Z==0 in all points on
103 # checkerboard.
104 cbCorners = np.zeros((1, vtxCount[0]*vtxCount[1], 3))
105 cbCorners[0,:,:2] = np.mgrid[0:vtxCount[0], 0:vtxCount[1]].T.reshape(-1,2)
106 corners3D = [cbCorners.reshape(-1,1,3) for i in range(len(corners2D))]
107 
108 # ---------------------------------------------
109 # Calculate fisheye lens calibration parameters
110 camMatrix = np.eye(3)
111 coeffs = np.zeros((4,))
112 rms, camMatrix, coeffs, rvecs, tvecs = cv2.fisheye.calibrate(corners3D, corners2D, imgSize, camMatrix, coeffs, flags=cv2.fisheye.CALIB_FIX_SKEW)
113 
114 # Print out calibration results
115 print("rms error: {}".format(rms))
116 print("Fisheye coefficients: {}".format(coeffs))
117 print("Camera matrix:")
118 print(camMatrix)
119 
120 # ======================
121 # Undistort input images
122 
123 # Create an uniform grid
124 grid = vpi.WarpGrid(imgSize)
125 
126 # Create undistort warp map from the calibration parameters and the grid
127 undist_map = vpi.WarpMap.fisheye_correction(grid,
128  K=camMatrix[0:2,:], X=np.eye(3,4), coeffs=coeffs,
129  mapping=vpi.FisheyeMapping.EQUIDISTANT)
130 
131 # Go through all input images,
132 idx=0
133 for imgName in args.images
134  # Load input image and do some sanity check
135  img = cv2.imread(imgName)
136 
137  # Using the CUDA backend,
138  with vpi.Backend.CUDA
139  # Convert image to NV12_ER, apply the undistortion map and convert image back to RGB8
140  imgCorrected = vpi.asimage(img).convert(vpi.Format.NV12_ER).remap(undist_map, interp=vpi.Interp.CATMULL_ROM).convert(vpi.Format.RGB8)
141 
142  # Write undistorted image to disk
143  cv2.imwrite("undistort_python{}_{:03d}.jpg".format(sys.version_info[0],idx), imgCorrected.cpu())
144  idx += 1
29 #include <opencv2/core/version.hpp>
30 
31 #if CV_MAJOR_VERSION >= 3
32 # include <opencv2/imgcodecs.hpp>
33 #else
34 # include <opencv2/highgui/highgui.hpp>
35 #endif
36 
37 // Changes in fisheye camera calibration function in OpenCV 4.10, leads to corrupted output.
38 #if CV_VERSION_MAJOR * 100 + CV_VERSION_MINOR >= 410
39 # error "OpenCV >= 4.10 isn't supported"
40 #endif
41 
42 #include <opencv2/calib3d/calib3d.hpp>
43 #include <opencv2/imgproc/imgproc.hpp>
44 #include <vpi/OpenCVInterop.hpp>
45 
46 #include <vpi/Image.h>
48 #include <vpi/Status.h>
49 #include <vpi/Stream.h>
51 #include <vpi/algo/Remap.h>
52 
53 #include <iostream>
54 #include <sstream>
55 
56 #define CHECK_STATUS(STMT) \
57  do \
58  { \
59  VPIStatus status = (STMT); \
60  if (status != VPI_SUCCESS) \
61  { \
62  char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
63  vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
64  std::ostringstream ss; \
65  ss << vpiStatusGetName(status) << ": " << buffer; \
66  throw std::runtime_error(ss.str()); \
67  } \
68  } while (0);
69 
70 static void PrintUsage(const char *progname, std::ostream &out)
71 {
72  out << "Usage: " << progname << " <-c W,H> [-s win] <image1> [image2] [image3] ...\n"
73  << " where,\n"
74  << " W,H\tcheckerboard with WxH squares\n"
75  << " win\tsearch window width around checkerboard vertex used\n"
76  << "\tin refinement, default is 0 (disable refinement)\n"
77  << " imageN\tinput images taken with a fisheye lens camera" << std::endl;
78 }
79 
80 static char *my_basename(char *path)
81 {
82 #ifdef WIN32
83  char *name = strrchr(path, '\\');
84 #else
85  char *name = strrchr(path, '/');
86 #endif
87  if (name != NULL)
88  {
89  return name;
90  }
91  else
92  {
93  return path;
94  }
95 }
96 
97 struct Params
98 {
99  cv::Size vtxCount; // Number of internal vertices the checkerboard has
100  int searchWinSize; // search window size around the checkerboard vertex for refinement.
101  std::vector<const char *> images; // input image names.
102 };
103 
104 static Params ParseParameters(int argc, char *argv[])
105 {
106  Params params = {};
107 
108  cv::Size cbSize;
109 
110  for (int i = 1; i < argc; ++i)
111  {
112  if (argv[i][0] == '-')
113  {
114  if (strlen(argv[i] + 1) == 1)
115  {
116  switch (argv[i][1])
117  {
118  case 'h'
119  PrintUsage(my_basename(argv[0]), std::cout);
120  return {};
121 
122  case 'c'
123  if (i == argc - 1)
124  {
125  throw std::invalid_argument("Option -c must be followed by checkerboard width and height");
126  }
127 
128  if (sscanf(argv[++i], "%d,%d", &cbSize.width, &cbSize.height) != 2)
129  {
130  throw std::invalid_argument("Error parsing checkerboard information");
131  }
132 
133  // OpenCV expects number of interior vertices in the checkerboard,
134  // not number of squares. Let's adjust for that.
135  params.vtxCount.width = cbSize.width - 1;
136  params.vtxCount.height = cbSize.height - 1;
137  break;
138 
139  case 's'
140  if (i == argc - 1)
141  {
142  throw std::invalid_argument("Option -s must be followed by search window size");
143  }
144  if (sscanf(argv[++i], "%d", &params.searchWinSize) != 1)
145  {
146  throw std::invalid_argument("Error parsing search window size");
147  }
148  if (params.searchWinSize < 0)
149  {
150  throw std::invalid_argument("Search window size must be >= 0");
151  }
152  break;
153 
154  default
155  throw std::invalid_argument(std::string("Option -") + (argv[i] + 1) + " not recognized");
156  }
157  }
158  else
159  {
160  throw std::invalid_argument(std::string("Option -") + (argv[i] + 1) + " not recognized");
161  }
162  }
163  else
164  {
165  params.images.push_back(argv[i]);
166  }
167  }
168 
169  if (params.images.empty())
170  {
171  throw std::invalid_argument("At least one image must be defined");
172  }
173 
174  if (cbSize.width <= 3 || cbSize.height <= 3)
175  {
176  throw std::invalid_argument("Checkerboard size must have at least 3x3 squares");
177  }
178 
179  if (params.searchWinSize == 1)
180  {
181  throw std::invalid_argument("Search window size must be 0 (default) or >= 2");
182  }
183 
184  return params;
185 }
186 
187 int main(int argc, char *argv[])
188 {
189  // OpenCV image that will be wrapped by a VPIImage.
190  // Define it here so that it's destroyed *after* wrapper is destroyed
191  cv::Mat cvImage;
192 
193  # VPI objects that will be used
194  VPIStream stream = NULL;
195  VPIPayload remap = NULL;
196  VPIImage tmpIn = NULL, tmpOut = NULL;
197  VPIImage vimg = nullptr;
198 
199  int retval = 0;
200 
201  try
202  {
203  // First parse command line paramers
204  Params params = ParseParameters(argc, argv);
205  if (params.images.empty()) // user just wanted the help message?
206  {
207  return 0;
208  }
209 
210  // Where to store checkerboard 2D corners of each input image.
211  std::vector<std::vector<cv::Point2f>> corners2D;
212 
213  // 存储图像尺寸。所有输入图像必须具有相同尺寸。
214  cv::Size imgSize = {};
215 
216  for (unsigned i = 0; i < params.images.size(); ++i)
217  {
218  // 加载输入图像并进行一些健全性检查
219  cv::Mat img = cv::imread(params.images[i]);
220  if (img.empty())
221  {
222  throw std::runtime_error("Can't read " + std::string(params.images[i]));
223  }
224 
225  if (imgSize == cv::Size{})
226  {
227  imgSize = img.size();
228  }
229  else if (imgSize != img.size())
230  {
231  throw std::runtime_error("All images must have same size");
232  }
233 
234  // 在图像上查找棋盘格图案,将棋盘格顶点的 2D
235  // 坐标保存在 cbVertices 中。
236  // 顶点是 4 个正方形(2 个白色和 2 个黑色)相遇的点。
237  std::vector<cv::Point2f> cbVertices;
238 
239  if (findChessboardCorners(img, params.vtxCount, cbVertices,
240  cv::CALIB_CB_ADAPTIVE_THRESH + cv::CALIB_CB_NORMALIZE_IMAGE))
241  {
242  // 需要执行进一步的角点细化吗?
243  if (params.searchWinSize >= 2)
244  {
245  cv::Mat gray;
246  cvtColor(img, gray, cv::COLOR_BGR2GRAY);
247 
248  cornerSubPix(gray, cbVertices, cv::Size(params.searchWinSize / 2, params.searchWinSize / 2),
249  cv::Size(-1, -1),
250  cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT, 30, 0.0001));
251  }
252 
253  // 将此图像的 2D 顶点保存在向量中
254  corners2D.push_back(std::move(cbVertices));
255  }
256  else
257  {
258  std::cerr << "Warning: checkerboard pattern not found in image " << params.images[i] << std::endl;
259  }
260  }
261 
262  // 创建向量,用于存储空间中每个棋盘格图案的 3D 坐标
263  // 其中 X 和 Y 是正交的,并沿着棋盘格边运行,并且在棋盘格上的所有点中 Z==0。
264  // 棋盘格。
265  std::vector<cv::Point3f> initialCheckerboard3DVertices;
266  for (int i = 0; i < params.vtxCount.height; ++i)
267  {
268  for (int j = 0; j < params.vtxCount.width; ++j)
269  {
270  // 由于我们对外部相机参数不感兴趣,
271  // 我们可以假设棋盘格正方形大小为 1x1。
272  initialCheckerboard3DVertices.emplace_back(static_cast<float>(j), static_cast<float>(i), 0.0f);
273  }
274  }
275 
276  // 使用所有图像的初始棋盘格位置初始化向量
277  std::vector<std::vector<cv::Point3f>> corners3D(corners2D.size(), initialCheckerboard3DVertices);
278 
279  // 相机内部参数,最初为单位矩阵(将通过校准过程估计)。
280  using Mat3 = cv::Matx<double, 3, 3>;
281  Mat3 camMatrix = Mat3::eye();
282 
283  // 存储鱼眼模型系数。
284  std::vector<double> coeffs(4);
285 
286  // VPI 当前不支持相机矩阵上的倾斜参数,请确保
287  // 校准过程将其固定为 0。
288  int flags = cv::fisheye::CALIB_FIX_SKEW;
289 
290  // 运行校准
291  {
292  cv::Mat rvecs, tvecs; // 存储每个相机的旋转和平移,现在不需要。
293  double rms = cv::fisheye::calibrate(corners3D, corners2D, imgSize, camMatrix, coeffs, rvecs, tvecs, flags);
294  printf("rms 误差: %lf\n", rms);
295  }
296 
297  // 输出校准结果。
298  printf("鱼眼系数: %lf %lf %lf %lf\n", coeffs[0], coeffs[1], coeffs[2], coeffs[3]);
299 
300  printf("相机矩阵:\n");
301  printf("[%lf %lf %lf; %lf %lf %lf; %lf %lf %lf]\n", camMatrix(0, 0), camMatrix(0, 1), camMatrix(0, 2),
302  camMatrix(1, 0), camMatrix(1, 1), camMatrix(1, 2), camMatrix(2, 0), camMatrix(2, 1), camMatrix(2, 2));
303 
304  // 现在使用 VPI 来 undistort 输入图像:
305 
306  // 分配密集映射。
307  VPIWarpMap map = {};
308  map.grid.numHorizRegions = 1;
309  map.grid.numVertRegions = 1;
310  map.grid.regionWidth[0] = imgSize.width;
311  map.grid.regionHeight[0] = imgSize.height;
312  map.grid.horizInterval[0] = 1;
313  map.grid.vertInterval[0] = 1;
314  CHECK_STATUS(vpiWarpMapAllocData(&map));
315 
316  // 使用校准程序给出的系数初始化鱼眼镜头模型。
317  VPIFisheyeLensDistortionModel distModel = {};
318  distModel.mapping = VPI_FISHEYE_EQUIDISTANT;
319  distModel.k1 = coeffs[0];
320  distModel.k2 = coeffs[1];
321  distModel.k3 = coeffs[2];
322  distModel.k4 = coeffs[3];
323 
324  // 填充相机校准程序给出的相机内部参数。
326  for (int i = 0; i < 2; ++i)
327  {
328  for (int j = 0; j < 3; ++j)
329  {
330  K[i][j] = camMatrix(i, j);
331  }
332  }
333 
334  // 相机外参应为单位矩阵。
335  VPICameraExtrinsic X = {};
336  X[0][0] = X[1][1] = X[2][2] = 1;
337 
338  // 生成扭曲映射,以校正由鱼眼镜头拍摄的图像的失真
339  // 使用上面计算的给定参数。
340  vpiWarpMapGenerateFromFisheyeLensDistortionModel(K, X, K, &distModel, &map);
341 
342  // 创建 Remap 有效负载以根据上面生成的映射进行 undistortion。
343  CHECK_STATUS(vpiCreateRemap(VPI_BACKEND_CUDA, &map, &remap));
344 
345  // 现在 remap 有效负载已创建,我们可以销毁扭曲映射。
346  vpiWarpMapFreeData(&map);
347 
348  // 创建将要执行操作的流。我们正在使用 CUDA
349  // 处理。
350  CHECK_STATUS(vpiStreamCreate(VPI_BACKEND_CUDA, &stream));
351 
352  // NV12 格式的临时输入和输出图像。
353  CHECK_STATUS(vpiImageCreate(imgSize.width, imgSize.height, VPI_IMAGE_FORMAT_NV12_ER, 0, &tmpIn));
354  CHECK_STATUS(vpiImageCreate(imgSize.width, imgSize.height, VPI_IMAGE_FORMAT_NV12_ER, 0, &tmpOut));
355 
356  // 对于每个输入图像,
357  for (unsigned i = 0; i < params.images.size(); ++i)
358  {
359  // 从磁盘读取它。
360  cvImage = cv::imread(params.images[i]);
361  assert(!cvImage.empty());
362 
363  // 将其包装到 VPIImage 中
364  if (vimg == nullptr)
365  {
366  // 现在创建一个包装它的 VPIImage。
367  CHECK_STATUS(vpiImageCreateWrapperOpenCVMat(cvImage, 0, &vimg));
368  }
369  else
370  {
371  CHECK_STATUS(vpiImageSetWrappedOpenCVMat(vimg, cvImage));
372  }
373 
374  // 转换 BGR -> NV12
375  CHECK_STATUS(vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, vimg, tmpIn, NULL));
376 
377  // Undistorts 输入图像。
378  CHECK_STATUS(vpiSubmitRemap(stream, VPI_BACKEND_CUDA, remap, tmpIn, tmpOut, VPI_INTERP_CATMULL_ROM,
379  VPI_BORDER_ZERO, 0));
380 
381  // 将结果 NV12 转换回 BGR,写回输入图像。
382  CHECK_STATUS(vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, tmpOut, vimg, NULL));
383 
384  // 等待转换完成。
385  CHECK_STATUS(vpiStreamSync(stream));
386 
387  // 由于 vimg 正在包装 OpenCV 图像,因此结果已在那里。
388  // 我们只需要将其保存到磁盘。
389  char buf[64];
390  snprintf(buf, sizeof(buf), "undistort_%03d.jpg", i);
391  imwrite(buf, cvImage);
392  }
393  }
394  catch (std::exception &e)
395  {
396  std::cerr << "Error: " << e.what() << std::endl;
397  PrintUsage(my_basename(argv[0]), std::cerr);
398 
399  retval = 1;
400  }
401 
402  vpiStreamDestroy(stream);
403  vpiPayloadDestroy(remap);
404  vpiImageDestroy(tmpIn);
405  vpiImageDestroy(tmpOut);
406  vpiImageDestroy(vimg);
407 
408  return retval;
409 }
声明处理图像格式转换的函数。
#define VPI_IMAGE_FORMAT_NV12_ER
YUV420sp 8 位 pitch-linear 格式,具有全范围。
用于处理 VPI 图像的函数和结构。
声明基于常见镜头畸变模型生成扭曲映射的函数。
用于处理 OpenCV 与 VPI 的互操作性的函数。
声明实现 Remap 算法的函数。
VPI 状态代码处理函数的声明。
声明处理 VPI 流的函数。
VPIStatus vpiSubmitConvertImageFormat(VPIStream stream, uint64_t backend, VPIImage input, VPIImage output, const VPIConvertImageFormatParams *params)
将图像内容转换为所需的格式,具有可选的缩放和偏移。
void vpiImageDestroy(VPIImage img)
销毁图像实例。
struct VPIImageImpl * VPIImage
图像的句柄。
定义: Types.h:256
VPIStatus vpiImageCreate(int32_t width, int32_t height, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
使用指定的标志创建空的图像实例。
VPIFisheyeMapping mapping
像素角度与到图像中心的像素距离之间的映射。
VPIStatus vpiWarpMapGenerateFromFisheyeLensDistortionModel(const VPICameraIntrinsic Kin, const VPICameraExtrinsic X, const VPICameraIntrinsic Kout, const VPIFisheyeLensDistortionModel *distModel, VPIWarpMap *warpMap)
生成校正由鱼眼镜头引起的图像失真的映射。
float VPICameraExtrinsic[3][4]
相机外参矩阵。
定义: Types.h:668
float VPICameraIntrinsic[2][3]
相机内参矩阵。
定义: Types.h:655
@ VPI_FISHEYE_EQUIDISTANT
指定等距鱼眼映射。
保存鱼眼镜头畸变模型的系数。
VPIStatus vpiImageCreateWrapperOpenCVMat(const cv::Mat &mat, VPIImageFormat fmt, uint64_t flags, VPIImage *img)
使用给定的图像格式将 cv::Mat 包装在 VPIImage 中。
VPIStatus vpiImageSetWrappedOpenCVMat(VPIImage img, const cv::Mat &mat)
重新定义现有 VPIImage 包装器的包装 cv::Mat。
struct VPIPayloadImpl * VPIPayload
算法有效负载的句柄。
定义: Types.h:268
void vpiPayloadDestroy(VPIPayload payload)
释放有效负载对象和所有关联的资源。
VPIStatus vpiSubmitRemap(VPIStream stream, uint64_t backend, VPIPayload payload, VPIImage input, VPIImage output, VPIInterpolationType interp, VPIBorderExtension border, uint64_t flags)
将 Remap 操作提交到流。
VPIStatus vpiCreateRemap(uint64_t backends, const VPIWarpMap *warpMap, VPIPayload *payload)
为 Remap 算法创建有效负载。
struct VPIStreamImpl * VPIStream
流的句柄。
定义: Types.h:250
VPIStatus vpiStreamSync(VPIStream stream)
阻止调用线程,直到此流队列中的所有提交命令完成(队列为空)...
void vpiStreamDestroy(VPIStream stream)
销毁流实例并释放所有硬件资源。
VPIStatus vpiStreamCreate(uint64_t flags, VPIStream *stream)
创建流实例。
@ VPI_BACKEND_CUDA
CUDA 后端。
定义: Types.h:93
@ VPI_BORDER_ZERO
图像外部的所有像素都被视为零。
定义: Types.h:278
@ VPI_INTERP_CATMULL_ROM
Catmull-Rom 立方插值。
int8_t numHorizRegions
水平区域数。
VPIWarpGrid grid
扭曲网格控制点结构定义。
定义: WarpMap.h:91
int16_t horizInterval[VPI_WARPGRID_MAX_HORIZ_REGIONS_COUNT]
给定区域内控制点之间的水平间距。
int8_t numVertRegions
垂直区域数。
int16_t vertInterval[VPI_WARPGRID_MAX_VERT_REGIONS_COUNT]
给定区域内控制点之间的垂直间距。
int16_t regionWidth[VPI_WARPGRID_MAX_HORIZ_REGIONS_COUNT]
每个区域的宽度。
int16_t regionHeight[VPI_WARPGRID_MAX_VERT_REGIONS_COUNT]
每个区域的高度。
void vpiWarpMapFreeData(VPIWarpMap *warpMap)
释放 vpiWarpMapAllocData 分配的扭曲映射控制点。
VPIStatus vpiWarpMapAllocData(VPIWarpMap *warpMap)
为给定的扭曲网格分配扭曲映射的控制点数组。
定义输入和输出图像像素之间的映射。
定义: WarpMap.h:88