29 #include <opencv2/core/version.hpp>
30 #if CV_MAJOR_VERSION >= 3
31 # include <opencv2/imgcodecs.hpp>
32 # include <opencv2/videoio.hpp>
34 # include <opencv2/highgui/highgui.hpp>
37 #include <opencv2/imgproc/imgproc.hpp>
60 constexpr
int MAX_HARRIS_CORNERS = 8192;
63 constexpr
int MAX_KEYPOINTS = 100;
65 #define CHECK_STATUS(STMT) \
68 VPIStatus status__ = (STMT); \
69 if (status__ != VPI_SUCCESS) \
71 char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
72 vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
73 std::ostringstream ss; \
74 ss << vpiStatusGetName(status__) << ": " << buffer; \
75 throw std::runtime_error(ss.str()); \
79 static void SaveFileToDisk(
VPIImage img, cv::Mat cvMask, std::string baseFileName, int32_t frameCounter)
89 cvtColor(tmp, cvImage, cv::COLOR_GRAY2BGR);
99 add(cvImage, cvMask, cvImage);
102 std::string fname = baseFileName;
103 int ext = fname.rfind(
'.');
105 char buffer[512] = {};
106 snprintf(buffer,
sizeof(buffer) - 1,
"%s_%04d%s", fname.substr(0, ext).c_str(), frameCounter,
107 fname.substr(ext).c_str());
110 if (!imwrite(buffer, cvImage, {cv::IMWRITE_JPEG_QUALITY, 70}))
112 throw std::runtime_error(
"Can't write to " + std::string(buffer));
126 std::vector<int> indices(*aosKeypoints.
sizePointer);
127 std::iota(indices.begin(), indices.end(), 0);
129 stable_sort(indices.begin(), indices.end(), [&aosScores](
int a,
int b) {
130 uint32_t *score = reinterpret_cast<uint32_t *>(aosScores.data);
131 return score[a] >= score[b];
135 indices.resize(std::min<size_t>(indices.size(), max));
140 std::vector<VPIKeypointF32> kpt;
141 std::transform(indices.begin(), indices.end(), std::back_inserter(kpt),
142 [kptData](
int idx) { return kptData[idx]; });
143 std::copy(kpt.begin(), kpt.end(), kptData);
152 static int UpdateMask(cv::Mat &cvMask,
const std::vector<cv::Scalar> &trackColors,
VPIArray prevFeatures,
171 const uint8_t *pStatus = (uint8_t *)aosStatus.
data;
182 pPrevFeatures = NULL;
185 int numTrackedKeypoints = 0;
188 for (
int i = 0; i < totKeypoints; i++)
194 cv::Point curPoint{(int)round(pCurFeatures[i].x), (int)round(pCurFeatures[i].y)};
195 if (pPrevFeatures != NULL)
197 cv::Point2f prevPoint{pPrevFeatures[i].
x, pPrevFeatures[i].
y};
198 line(cvMask, prevPoint, curPoint, trackColors[i], 2);
201 circle(cvMask, curPoint, 5, trackColors[i], -1);
203 numTrackedKeypoints++;
215 return numTrackedKeypoints;
218 int main(
int argc,
char *argv[])
228 VPIPyramid pyrPrevFrame = NULL, pyrCurFrame = NULL;
229 VPIArray prevFeatures = NULL, curFeatures = NULL, status = NULL;
243 throw std::runtime_error(std::string(
"Usage: ") + argv[0] +
247 std::string strBackend = argv[1];
248 std::string strInputVideo = argv[2];
249 int32_t pyrLevel = std::stoi(argv[3]);
250 std::string strOutputFiles = argv[4];
255 if (strBackend ==
"cpu")
259 else if (strBackend ==
"cuda")
263 else if (strBackend ==
"pva")
269 "后端 '" + strBackend +
270 "' 无法识别,它必须是 cpu、cuda 或 pva 之一。");
274 int ext = strOutputFiles.rfind(
'.');
275 strOutputFiles = strOutputFiles.substr(0, ext) +
"_" + strBackend + strOutputFiles.substr(ext);
280 cv::VideoCapture invid;
281 if (!invid.open(strInputVideo))
283 "无法打开 '" + strInputVideo +
"'");
288 if (!invid.read(cvFrame))
290 "无法从 '" + strInputVideo +
"' 中检索第一帧");
324 cv::Mat cvMask = cv::Mat::zeros(cvFrame.size(), CV_8UC3);
345 SortKeypoints(curFeatures, scores, MAX_KEYPOINTS);
349 std::vector<cv::Scalar> trackColors;
351 std::vector<cv::Vec3b> tmpTrackColors;
360 for (
int i = 0; i < *aosKeypoints.
sizePointer; i++)
363 int hue = ((int)pts[i].x ^ (
int)pts[i].
y) % 180;
365 tmpTrackColors.push_back(cv::Vec3b(hue, 255, 255));
369 cvtColor(tmpTrackColors, tmpTrackColors, cv::COLOR_HSV2BGR);
371 for (
size_t i = 0; i < tmpTrackColors.size(); i++)
373 trackColors.push_back(cv::Scalar(tmpTrackColors[i]));
378 int numTrackedKeypoints = UpdateMask(cvMask, trackColors, NULL, curFeatures, status);
392 SaveFileToDisk(imgFrame, cvMask, strOutputFiles, idxFrame);
394 printf(
"帧 ID=%d: 跟踪了 %d 个点。\n", idxFrame, numTrackedKeypoints);
398 std::swap(prevFeatures, curFeatures);
399 std::swap(pyrPrevFrame, pyrCurFrame);
402 if (!invid.read(cvFrame))
421 curFeatures, status, &lkParams));
427 numTrackedKeypoints = UpdateMask(cvMask, trackColors, prevFeatures, curFeatures, status);
430 if (numTrackedKeypoints == 0)
432 printf(
"没有关键点可以跟踪。\n");
437 catch (std::exception &e)
439 std::cerr << e.what() << std::endl;
用于处理 OpenCV 与 VPI 互操作性的函数。
VPIArrayBuffer buffer
存储数组内容。
int32_t * sizePointer
指向数组中元素的数量。
VPIArrayBufferAOS aos
以结构体数组布局存储的数组。
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
数组的句柄。
@ VPI_ARRAY_TYPE_U32
无符号 32 位。
@ VPI_ARRAY_TYPE_KEYPOINT_F32
VPIKeypointF32 元素。
@ VPI_ARRAY_TYPE_U8
无符号 8 位。
@ VPI_ARRAY_BUFFER_HOST_AOS
主机可访问的结构体数组。
VPIStatus vpiSubmitGaussianPyramidGenerator(VPIStream stream, uint64_t backend, VPIImage input, VPIPyramid output, VPIBorderExtension border)
从输入图像计算高斯金字塔。
float strengthThresh
指定用于消除 Harris 角点得分的最小阈值。
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
图像的句柄。
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 内存布局。
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。
VPIStatus vpiImageSetWrappedOpenCVMat(VPIImage img, const cv::Mat &mat)
重新定义现有 VPIImage 封装器的被封装的 cv::Mat。
VPIStatus vpiSubmitOpticalFlowPyrLK(VPIStream stream, uint64_t backend, VPIPayload payload, VPIPyramid prevPyr, VPIPyramid curPyr, VPIArray prevPts, VPIArray curPts, VPIArray trackingStatus, const VPIOpticalFlowPyrLKParams *params)
在两个帧上运行金字塔 LK 光流。
VPIStatus vpiInitOpticalFlowPyrLKParams(uint64_t backends, VPIOpticalFlowPyrLKParams *params)
使用默认值初始化 VPIOpticalFlowPyrLKParams。
VPIStatus vpiCreateOpticalFlowPyrLK(uint64_t backends, int32_t width, int32_t height, VPIImageFormat fmt, int32_t levels, float scale, VPIPayload *payload)
为 vpiSubmitOpticalFlowPyrLK 创建负载。
定义 vpiSubmitOpticalFlowPyrLK 参数的结构体。
struct VPIPayloadImpl * VPIPayload
算法负载的句柄。
void vpiPayloadDestroy(VPIPayload payload)
释放负载对象和所有相关的资源。
VPIStatus vpiPyramidCreate(int32_t width, int32_t height, VPIImageFormat fmt, int32_t numLevels, float scale, uint64_t flags, VPIPyramid *pyr)
使用指定的标志创建空的图像金字塔实例。
struct VPIPyramidImpl * VPIPyramid
图像金字塔的句柄。
void vpiPyramidDestroy(VPIPyramid pyr)
销毁图像金字塔实例以及它拥有的所有资源。
struct VPIStreamImpl * VPIStream
流的句柄。
VPIStatus vpiStreamSync(VPIStream stream)
阻塞调用线程,直到此流队列中所有提交的命令都完成(队列为空)。。。
void vpiStreamDestroy(VPIStream stream)
销毁流实例并释放所有硬件资源。
VPIStatus vpiStreamCreate(uint64_t flags, VPIStream *stream)
创建流实例。
@ VPI_BACKEND_CUDA
CUDA 后端。
@ VPI_BORDER_CLAMP
边界像素无限重复。
@ VPI_LOCK_READ_WRITE
锁定内存以进行读取和写入。
@ VPI_LOCK_READ
仅锁定内存以进行读取。
存储 float32 关键点坐标。坐标相对于图像的左上角。