VPI - Vision Programming Interface

3.2 版本

背景消除器

概述

此应用程序从输入视频源获取帧,在当前图像上运行算法,然后计算前景掩码。输出前景掩码将保存到视频文件中。

说明

命令行参数为

<backend> <input video>

其中

  • backend: 定义将执行处理的后端。目前仅支持 cpucuda
  • input video: 输入视频文件名,它接受 .mp4、.avi 以及可能其他的格式,具体取决于 OpenCV 的支持。

这是一个示例

  • C++
    ./vpi_sample_14_background_subtractor cpu ../assets/pedestrians.mp4
  • Python
    python3 main.py cpu ../assets/pedestrians.mp4

该应用程序将处理 pedestrians.mp4 并创建 fgmask_cpu.mp4bgimage_cpu.mp4

结果

输入视频前景视频背景视频

源代码

为了方便起见,以下代码也安装在 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 parser = ArgumentParser()
37 parser.add_argument('backend', choices=['cpu','cuda'],
38  help='Backend to be used for processing')
39 
40 parser.add_argument('input',
41  help='Input video to be denoised')
42 
43 args = parser.parse_args();
44 
45 if args.backend == 'cuda'
46  backend = vpi.Backend.CUDA
47 else
48  assert args.backend == 'cpu'
49  backend = vpi.Backend.CPU
50 
51 # -----------------------------
52 # Open input and output videos
53 
54 inVideo = cv2.VideoCapture(args.input)
55 
56 fourcc = cv2.VideoWriter_fourcc(*'MPEG')
57 inSize = (int(inVideo.get(cv2.CAP_PROP_FRAME_WIDTH)), int(inVideo.get(cv2.CAP_PROP_FRAME_HEIGHT)))
58 fps = inVideo.get(cv2.CAP_PROP_FPS)
59 
60 outVideoFGMask = cv2.VideoWriter('fgmask_python'+str(sys.version_info[0])+'_'+args.backend+'.mp4',
61  fourcc, fps, inSize)
62 
63 outVideoBGImage = cv2.VideoWriter('bgimage_python'+str(sys.version_info[0])+'_'+args.backend+'.mp4',
64  fourcc, fps, inSize)
65 
66 #--------------------------------------------------------------
67 # Create the Background Subtractor object using the backend specified by the user
68 with backend
69  bgsub = vpi.BackgroundSubtractor(inSize, vpi.Format.BGR8)
70 
71 #--------------------------------------------------------------
72 # Main processing loop
73 idxFrame = 0
74 while True
75  print("Processing frame {}".format(idxFrame))
76  idxFrame+=1
77 
78  # Read one input frame
79  ret, cvFrame = inVideo.read()
80  if not ret
81  break
82 
83  # Get the foreground mask and background image estimates
84  fgmask, bgimage = bgsub(vpi.asimage(cvFrame, vpi.Format.BGR8), learnrate=0.01)
85 
86  # Mask needs to be converted to BGR8 for output
87  fgmask = fgmask.convert(vpi.Format.BGR8, backend=vpi.Backend.CUDA);
88 
89  # Write images to output videos
90  with fgmask.rlock_cpu(), bgimage.rlock_cpu()
91  outVideoFGMask.write(fgmask.cpu())
92  outVideoBGImage.write(bgimage.cpu())
29 #include <opencv2/core/version.hpp>
30 #include <opencv2/imgcodecs.hpp>
31 #include <opencv2/imgproc/imgproc.hpp>
32 #include <opencv2/videoio.hpp>
33 #include <vpi/OpenCVInterop.hpp>
34 
35 #include <vpi/Array.h>
36 #include <vpi/Image.h>
37 #include <vpi/ImageFormat.h>
38 #include <vpi/Pyramid.h>
39 #include <vpi/Status.h>
40 #include <vpi/Stream.h>
43 
44 #include <iostream>
45 #include <sstream>
46 
47 #define CHECK_STATUS(STMT) \
48  do \
49  { \
50  VPIStatus status = (STMT); \
51  if (status != VPI_SUCCESS) \
52  { \
53  char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
54  vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
55  std::ostringstream ss; \
56  ss << vpiStatusGetName(status) << ": " << buffer; \
57  throw std::runtime_error(ss.str()); \
58  } \
59  } while (0);
60 
61 int main(int argc, char *argv[])
62 {
63  // OpenCV image that will be wrapped by a VPIImage.
64  // Define it here so that it's destroyed *after* wrapper is destroyed
65  cv::Mat cvCurFrame;
66 
67  // VPI objects that will be used
68  VPIStream stream = NULL;
69  VPIImage imgCurFrame = NULL;
70  VPIImage bgimage = NULL;
71  VPIImage fgmask = NULL;
72  VPIPayload payload = NULL;
73 
74  int retval = 0;
75 
76  try
77  {
78  if (argc != 3)
79  {
80  throw std::runtime_error(std::string("Usage: ") + argv[0] + " <cpu|cuda> <input_video>");
81  }
82 
83  // Parse input parameters
84  std::string strBackend = argv[1];
85  std::string strInputVideo = argv[2];
86 
87  VPIBackend backend;
88  if (strBackend == "cpu")
89  {
90  backend = VPI_BACKEND_CPU;
91  }
92  else if (strBackend == "cuda")
93  {
94  backend = VPI_BACKEND_CUDA;
95  }
96  else
97  {
98  throw std::runtime_error("Backend '" + strBackend + "' not recognized.");
99  }
100 
101  // Load the input video
102  cv::VideoCapture invid;
103  if (!invid.open(strInputVideo))
104  {
105  throw std::runtime_error("Can't open '" + strInputVideo + "'");
106  }
107 
108  int32_t width = invid.get(cv::CAP_PROP_FRAME_WIDTH);
109  int32_t height = invid.get(cv::CAP_PROP_FRAME_HEIGHT);
110 
111  // Create the stream where processing will happen. We'll use user-provided backend.
112  CHECK_STATUS(vpiStreamCreate(backend, &stream));
113 
114  // Create background subtractor payload to be executed on the given backend
115  // OpenCV delivers us BGR8 images, so the algorithm is configured to accept that.
116  CHECK_STATUS(vpiCreateBackgroundSubtractor(backend, width, height, VPI_IMAGE_FORMAT_BGR8, &payload));
117 
118  // Create foreground image
119  CHECK_STATUS(vpiImageCreate(width, height, VPI_IMAGE_FORMAT_U8, 0, &fgmask));
120 
121  // Create background image
122  CHECK_STATUS(vpiImageCreate(width, height, VPI_IMAGE_FORMAT_BGR8, 0, &bgimage));
123 
124  int fourcc = cv::VideoWriter::fourcc('M', 'P', 'E', 'G');
125  double fps = invid.get(cv::CAP_PROP_FPS);
126 
127  cv::VideoWriter outVideo("fgmask_" + strBackend + ".mp4", fourcc, fps, cv::Size(width, height), false);
128  if (!outVideo.isOpened())
129  {
130  throw std::runtime_error("Can't create output video");
131  }
132 
133  cv::VideoWriter bgimageVideo("bgimage_" + strBackend + ".mp4", fourcc, fps, cv::Size(width, height));
134  if (!outVideo.isOpened())
135  {
136  throw std::runtime_error("Can't create output video");
137  }
138 
139  // Fetch a new frame until video ends
140  int idxFrame = 1;
141 
142  while (invid.read(cvCurFrame))
143  {
144  printf("Processing frame %d\n", idxFrame++);
145  // Wrap frame into a VPIImage
146  if (imgCurFrame == NULL)
147  {
148  CHECK_STATUS(vpiImageCreateWrapperOpenCVMat(cvCurFrame, 0, &imgCurFrame));
149  }
150  else
151  {
152  CHECK_STATUS(vpiImageSetWrappedOpenCVMat(imgCurFrame, cvCurFrame));
153  }
154 
156  CHECK_STATUS(vpiInitBackgroundSubtractorParams(&params));
157  params.learningRate = 0.01;
158 
159  CHECK_STATUS(
160  vpiSubmitBackgroundSubtractor(stream, backend, payload, imgCurFrame, fgmask, bgimage, &params));
161 
162  // Wait for processing to finish.
163  CHECK_STATUS(vpiStreamSync(stream));
164 
165  {
166  // Now add it to the output video stream
167  VPIImageData imgdata;
168  CHECK_STATUS(vpiImageLockData(fgmask, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &imgdata));
169 
170  cv::Mat outFrame;
171  CHECK_STATUS(vpiImageDataExportOpenCVMat(imgdata, &outFrame));
172 
173  outVideo << outFrame;
174 
175  CHECK_STATUS(vpiImageUnlock(fgmask));
176  }
177 
178  {
179  VPIImageData bgdata;
180  CHECK_STATUS(vpiImageLockData(bgimage, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &bgdata));
181 
182  cv::Mat outFrame;
183  CHECK_STATUS(vpiImageDataExportOpenCVMat(bgdata, &outFrame));
184 
185  bgimageVideo << outFrame;
186 
187  CHECK_STATUS(vpiImageUnlock(bgimage));
188  }
189  }
190  }
191  catch (std::exception &e)
192  {
193  std::cerr << e.what() << std::endl;
194  retval = 1;
195  }
196 
197  // Destroy all resources used
198  vpiStreamDestroy(stream);
199  vpiPayloadDestroy(payload);
200 
201  vpiImageDestroy(imgCurFrame);
202  vpiImageDestroy(fgmask);
203  vpiImageDestroy(bgimage);
204 
205  return retval;
206 }
用于处理 VPI 数组的函数和结构。
声明实现背景消除器算法的函数。
声明处理图像格式转换的函数。
定义用于处理图像格式的类型和函数。
#define VPI_IMAGE_FORMAT_BGR8
具有交错 BGR 8 位通道的单平面。
Definition: ImageFormat.h:324
#define VPI_IMAGE_FORMAT_U8
具有一个 8 位无符号整数通道的单平面。
Definition: ImageFormat.h:100
用于处理 VPI 图像的函数和结构。
用于处理 OpenCV 与 VPI 互操作性的函数。
用于处理 VPI 金字塔的函数和结构。
VPI 状态代码处理函数的声明。
声明处理 VPI 流的函数。
float learningRate
指示背景模型学习速度的学习率。
VPIStatus vpiSubmitBackgroundSubtractor(VPIStream stream, uint64_t backend, VPIPayload payload, VPIImage inFrame, VPIImage outFGMask, VPIImage outBGImage, const VPIBackgroundSubtractorParams *params)
向流提交背景消除器操作。
VPIStatus vpiInitBackgroundSubtractorParams(VPIBackgroundSubtractorParams *params)
使用默认值初始化 VPIBackgroundSubtractorParams。
VPIStatus vpiCreateBackgroundSubtractor(uint64_t backends, int32_t imageWidth, int32_t imageHeight, VPIImageFormat inputFormat, VPIPayload *payload)
为 vpiSubmitBackgroundSubtractor 创建有效负载。
定义 vpiCreateBackgroundSubtractor 参数的结构体。
void vpiImageDestroy(VPIImage img)
销毁图像实例。
struct VPIImageImpl * VPIImage
图像的句柄。
Definition: 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 内存布局中。
Definition: Image.h:172
存储有关图像特征和内容的信息。
Definition: 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。
VPIStatus vpiImageSetWrappedOpenCVMat(VPIImage img, const cv::Mat &mat)
重新定义现有 VPIImage 包装器的包装 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_CPU
CPU 后端。
定义: Types.h:92
@ VPI_LOCK_READ
仅锁定内存以进行读取。
定义: Types.h:617