29 #include <opencv2/core/version.hpp>
30 #include <opencv2/imgcodecs.hpp>
31 #include <opencv2/imgproc/imgproc.hpp>
32 #include <opencv2/videoio.hpp>
48 #define CHECK_STATUS(STMT) \
51 VPIStatus status = (STMT); \
52 if (status != VPI_SUCCESS) \
54 char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
55 vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
56 std::ostringstream ss; \
57 ss << "line " << __LINE__ << ": "; \
58 ss << vpiStatusGetName(status) << ": " << buffer; \
59 throw std::runtime_error(ss.str()); \
63 static void ProcessMotionVector(
VPIImage mvImg, cv::Mat &outputImage)
74 cv::Mat flow(mvImage.size(), CV_32FC2);
75 mvImage.convertTo(flow, CV_32F, 1.0f / (1 << 5));
83 cv::Mat magnitude, angle;
85 cv::Mat flowChannels[2];
86 split(flow, flowChannels);
87 cv::cartToPolar(flowChannels[0], flowChannels[1], magnitude, angle,
true);
91 cv::threshold(magnitude, magnitude, clip, clip, cv::THRESH_TRUNC);
94 cv::Mat _hsv[3], hsv, bgr;
96 _hsv[1] = cv::Mat::ones(angle.size(), CV_32F);
97 _hsv[2] = magnitude / clip;
100 cv::cvtColor(hsv, bgr, cv::COLOR_HSV2BGR);
101 bgr.convertTo(outputImage, CV_8U, 255.0);
104 int main(
int argc,
char *argv[])
108 cv::Mat cvPrevFrame, cvCurFrame;
132 throw std::runtime_error(std::string(
"Usage: ") + argv[0] +
133 " <ofa> <input_video> <low|medium|high> <gridsize> <numlevels>");
137 std::string strBackend = argv[1];
138 std::string strInputVideo = argv[2];
139 std::string strQuality = argv[3];
140 std::string strGridSize = argv[4];
141 std::string strNumLevels = argv[5];
144 if (strQuality ==
"low")
148 else if (strQuality ==
"medium")
152 else if (strQuality ==
"high")
158 throw std::runtime_error(
"Unknown quality provided");
162 if (strBackend ==
"ofa")
168 throw std::runtime_error(
"Backend '" + strBackend +
"' not recognized, it must be ofa.");
172 int gridSize = strtol(strGridSize.c_str(), &endptr, 10);
175 throw std::runtime_error(
"Syntax error parsing gridsize " + strGridSize);
178 int numLevels = strtol(strNumLevels.c_str(), &endptr, 10);
181 throw std::runtime_error(
"Syntax error parsing numlevels " + strNumLevels);
185 cv::VideoCapture invid;
186 if (!invid.open(strInputVideo))
188 throw std::runtime_error(
"Can't open '" + strInputVideo +
"'");
196 if (!invid.read(cvPrevFrame))
198 throw std::runtime_error(
"Cannot read frame from input video");
210 int32_t width = cvPrevFrame.cols;
211 int32_t height = cvPrevFrame.rows;
214 std::vector<int32_t> pyrGridSize(numLevels, gridSize);
223 CHECK_STATUS(
vpiImageCreate(width, height, imgFmt, 0, &imgPrevFrameTmp));
224 CHECK_STATUS(
vpiImageCreate(width, height, imgFmt, 0, &imgCurFrameTmp));
229 CHECK_STATUS(
vpiPyramidCreate(width, height, imgFmt, pyrGridSize.size(), 0.5, 0, &prevPyrTmp));
230 CHECK_STATUS(
vpiPyramidCreate(width, height, imgFmt, pyrGridSize.size(), 0.5, 0, &curPyrTmp));
232 CHECK_STATUS(
vpiPyramidCreate(width, height, imgFmtBL, pyrGridSize.size(), 0.5, 0, &prevPyrBL));
233 CHECK_STATUS(
vpiPyramidCreate(width, height, imgFmtBL, pyrGridSize.size(), 0.5, 0, &curPyrBL));
236 int32_t mvWidth = (width + gridSize - 1) / gridSize;
237 int32_t mvHeight = (height + gridSize - 1) / gridSize;
240 int fourcc = cv::VideoWriter::fourcc(
'M',
'P',
'E',
'G');
241 double fps = invid.get(cv::CAP_PROP_FPS);
243 cv::VideoWriter outVideo(
"denseoptflow_mv_" + strBackend +
".mp4", fourcc, fps, cv::Size(mvWidth, mvHeight));
244 if (!outVideo.isOpened())
246 throw std::runtime_error(
"无法创建输出视频");
260 cv::Mat mvOutputImage;
264 while (invid.read(cvCurFrame))
266 printf(
"正在处理帧 %d\n", idxFrame++);
286 ProcessMotionVector(imgMotionVecPL, mvOutputImage);
289 outVideo << mvOutputImage;
292 std::swap(cvPrevFrame, cvCurFrame);
293 std::swap(imgPrevFramePL, imgCurFramePL);
294 std::swap(prevPyrBL, curPyrBL);
297 catch (std::exception &e)
299 std::cerr << e.what() << std::endl;
用于处理 OpenCV 与 VPI 互操作性的函数。
VPIStatus vpiSubmitGaussianPyramidGenerator(VPIStream stream, uint64_t backend, VPIImage input, VPIPyramid output, VPIBorderExtension border)
从输入图像计算高斯金字塔。
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
主机可访问,平面采用倾斜线性内存布局。
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 vpiCreateOpticalFlowDense(uint64_t backends, int32_t width, int32_t height, VPIImageFormat inputFmt, const int32_t *gridSize, int32_t numLevels, VPIOpticalFlowQuality quality, VPIPayload *payload)
为 vpiSubmitOpticalFlowDense 创建负载。
VPIStatus vpiSubmitOpticalFlowDensePyramid(VPIStream stream, uint64_t backend, VPIPayload payload, VPIPyramid prevPyr, VPIPyramid curPyr, VPIImage mvImg)
在两个帧上运行密集光流,输出运动向量。
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 后端。
VPIOpticalFlowQuality
定义光流算法的质量。
@ VPI_BORDER_CLAMP
边界像素无限重复。
@ VPI_OPTICAL_FLOW_QUALITY_LOW
快速但低质量的光流实现。
@ VPI_OPTICAL_FLOW_QUALITY_HIGH
慢速但高质量的光流实现。
@ VPI_OPTICAL_FLOW_QUALITY_MEDIUM
速度和质量介于 VPI_OPTICAL_FLOW_QUALITY_LOW 和 VPI_OPTICAL_FLOW_QUALITY_HIGH 之间。