Skip to content

Commit e155818

Browse files
committed
VP- limit number of frames processed
1 parent f34bd0e commit e155818

2 files changed

Lines changed: 278 additions & 1 deletion

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ src/cuda/GPU_Microbenchmark/ubench/**/*
2727
!src/cuda/GPU_Microbenchmark/ubench/**/Makefile
2828

2929
# Ignore VPI symlinks
30-
src/cuda/HPC/vpi/*
30+
src/cuda/HPC/vpi/*
31+
!src/cuda/HPC/vpi/vpi_subtractor/main.cpp
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
/*
2+
* Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions
6+
* are met:
7+
* * Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* * Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
* * Neither the name of NVIDIA CORPORATION nor the names of its
13+
* contributors may be used to endorse or promote products derived
14+
* from this software without specific prior written permission.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
17+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24+
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27+
*/
28+
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>
41+
#include <vpi/algo/BackgroundSubtractor.h>
42+
#include <vpi/algo/ConvertImageFormat.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+
// Parse named arguments
79+
std::string strBackend;
80+
std::string strInputVideo;
81+
int numFramesToProcess = -1; // -1 means process all frames
82+
83+
for (int i = 1; i < argc; i++)
84+
{
85+
std::string arg = argv[i];
86+
87+
if (arg == "--backend" || arg == "-b")
88+
{
89+
if (i + 1 < argc)
90+
{
91+
strBackend = argv[++i];
92+
}
93+
else
94+
{
95+
throw std::runtime_error("--backend requires a value");
96+
}
97+
}
98+
else if (arg == "--input" || arg == "-i")
99+
{
100+
if (i + 1 < argc)
101+
{
102+
strInputVideo = argv[++i];
103+
}
104+
else
105+
{
106+
throw std::runtime_error("--input requires a value");
107+
}
108+
}
109+
else if (arg == "--num-frames" || arg == "-n")
110+
{
111+
if (i + 1 < argc)
112+
{
113+
numFramesToProcess = std::atoi(argv[++i]);
114+
if (numFramesToProcess <= 0)
115+
{
116+
throw std::runtime_error("--num-frames must be a positive integer");
117+
}
118+
}
119+
else
120+
{
121+
throw std::runtime_error("--num-frames requires a value");
122+
}
123+
}
124+
else if (arg == "--help" || arg == "-h")
125+
{
126+
std::cout << "Usage: " << argv[0] << " [OPTIONS]\n"
127+
<< "Options:\n"
128+
<< " --backend, -b <cpu|cuda> Backend to use (required)\n"
129+
<< " --input, -i <video_file> Input video file (required)\n"
130+
<< " --num-frames, -n <count> Number of frames to process (optional, default: all)\n"
131+
<< " --help, -h Show this help message\n";
132+
return 0;
133+
}
134+
else
135+
{
136+
throw std::runtime_error("Unknown argument: " + arg);
137+
}
138+
}
139+
140+
// Validate required arguments
141+
if (strBackend.empty())
142+
{
143+
throw std::runtime_error("--backend is required\n\nUse --help for usage information");
144+
}
145+
if (strInputVideo.empty())
146+
{
147+
throw std::runtime_error("--input is required\n\nUse --help for usage information");
148+
}
149+
150+
VPIBackend backend;
151+
if (strBackend == "cpu")
152+
{
153+
backend = VPI_BACKEND_CPU;
154+
}
155+
else if (strBackend == "cuda")
156+
{
157+
backend = VPI_BACKEND_CUDA;
158+
}
159+
else
160+
{
161+
throw std::runtime_error("Backend '" + strBackend + "' not recognized.");
162+
}
163+
164+
// Load the input video
165+
cv::VideoCapture invid;
166+
if (!invid.open(strInputVideo))
167+
{
168+
throw std::runtime_error("Can't open '" + strInputVideo + "'");
169+
}
170+
171+
int32_t width = invid.get(cv::CAP_PROP_FRAME_WIDTH);
172+
int32_t height = invid.get(cv::CAP_PROP_FRAME_HEIGHT);
173+
174+
// Create the stream where processing will happen. We'll use user-provided backend.
175+
CHECK_STATUS(vpiStreamCreate(backend, &stream));
176+
177+
// Create background subtractor payload to be executed on the given backend
178+
// OpenCV delivers us BGR8 images, so the algorithm is configured to accept that.
179+
CHECK_STATUS(vpiCreateBackgroundSubtractor(backend, width, height, VPI_IMAGE_FORMAT_BGR8, &payload));
180+
181+
// Create foreground image
182+
CHECK_STATUS(vpiImageCreate(width, height, VPI_IMAGE_FORMAT_U8, 0, &fgmask));
183+
184+
// Create background image
185+
CHECK_STATUS(vpiImageCreate(width, height, VPI_IMAGE_FORMAT_BGR8, 0, &bgimage));
186+
187+
int fourcc = cv::VideoWriter::fourcc('M', 'P', 'E', 'G');
188+
double fps = invid.get(cv::CAP_PROP_FPS);
189+
190+
cv::VideoWriter outVideo("fgmask_" + strBackend + ".mp4", fourcc, fps, cv::Size(width, height), false);
191+
if (!outVideo.isOpened())
192+
{
193+
throw std::runtime_error("Can't create output video");
194+
}
195+
196+
cv::VideoWriter bgimageVideo("bgimage_" + strBackend + ".mp4", fourcc, fps, cv::Size(width, height));
197+
if (!outVideo.isOpened())
198+
{
199+
throw std::runtime_error("Can't create output video");
200+
}
201+
202+
// Fetch a new frame until video ends or desired frame count is reached
203+
int idxFrame = 1;
204+
205+
while (invid.read(cvCurFrame))
206+
{
207+
// Check if we've reached the desired number of frames
208+
if (numFramesToProcess > 0 && idxFrame > numFramesToProcess)
209+
{
210+
printf("Processed %d frames (limit reached)\n", idxFrame - 1);
211+
break;
212+
}
213+
214+
printf("Processing frame %d\n", idxFrame++);
215+
// Wrap frame into a VPIImage
216+
if (imgCurFrame == NULL)
217+
{
218+
CHECK_STATUS(vpiImageCreateWrapperOpenCVMat(cvCurFrame, 0, &imgCurFrame));
219+
}
220+
else
221+
{
222+
CHECK_STATUS(vpiImageSetWrappedOpenCVMat(imgCurFrame, cvCurFrame));
223+
}
224+
225+
VPIBackgroundSubtractorParams params;
226+
CHECK_STATUS(vpiInitBackgroundSubtractorParams(&params));
227+
params.learningRate = 0.01;
228+
229+
CHECK_STATUS(
230+
vpiSubmitBackgroundSubtractor(stream, backend, payload, imgCurFrame, fgmask, bgimage, &params));
231+
232+
// Wait for processing to finish.
233+
CHECK_STATUS(vpiStreamSync(stream));
234+
235+
{
236+
// Now add it to the output video stream
237+
VPIImageData imgdata;
238+
CHECK_STATUS(vpiImageLockData(fgmask, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &imgdata));
239+
240+
cv::Mat outFrame;
241+
CHECK_STATUS(vpiImageDataExportOpenCVMat(imgdata, &outFrame));
242+
243+
outVideo << outFrame;
244+
245+
CHECK_STATUS(vpiImageUnlock(fgmask));
246+
}
247+
248+
{
249+
VPIImageData bgdata;
250+
CHECK_STATUS(vpiImageLockData(bgimage, VPI_LOCK_READ, VPI_IMAGE_BUFFER_HOST_PITCH_LINEAR, &bgdata));
251+
252+
cv::Mat outFrame;
253+
CHECK_STATUS(vpiImageDataExportOpenCVMat(bgdata, &outFrame));
254+
255+
bgimageVideo << outFrame;
256+
257+
CHECK_STATUS(vpiImageUnlock(bgimage));
258+
}
259+
}
260+
}
261+
catch (std::exception &e)
262+
{
263+
std::cerr << e.what() << std::endl;
264+
retval = 1;
265+
}
266+
267+
// Destroy all resources used
268+
vpiStreamDestroy(stream);
269+
vpiPayloadDestroy(payload);
270+
271+
vpiImageDestroy(imgCurFrame);
272+
vpiImageDestroy(fgmask);
273+
vpiImageDestroy(bgimage);
274+
275+
return retval;
276+
}

0 commit comments

Comments
 (0)