VPI - Vision Programming Interface

0.4.4 Release

Temporal Noise Reduction

Overview

The Temporal Noise Reduction sample application takes a noisy input video, reduces its noise and writes the result to disk. The user can define what backend will be used for processing.

This sample shows the following:

  • Creating and destroying a VPI stream.
  • Wrapping an OpenCV image to be used by VPI.
  • Wrapping a VPI-managed image into an OpenCV's cv::Mat.
  • Use OpenCV to fetch frames from a video file.
  • Use OpenCV save frames into a video file.
  • Create pipeline that does Convert Image Format to convert from/to NV12 format and run Temporal Noise Reduction.
  • Shows how to properly use Temporal Noise Reduction to process a video sequence.

Instructions

The usage is:

./vpi_sample_09_tnr <backend> <input video> <output video>

where

  • backend: either cuda or pva, as cpu isn't available yet. It defines the backend that will perform the processing.
  • input video: video file to have noise reduced; it accepts .mp4, .avi and possibly others, depending on OpenCV's support.
  • output video: file to write the de-noised result. It'll use the same codec and fps as input video.

VPI samples installer includes a sample video with noise added artificially, found in /opt/nvidia/vpi-0.4/samples/assets/noisy.mp4.

Here's one invocation example:

./vpi_sample_09_tnr cuda ../assets/noisy.mp4 denoised.mp4

Results

Input videoDe-noised video

Source code

For convenience, here's the code that is also installed in the samples directory.

1 /*
2 * Copyright (c) 2020, 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 #if CV_MAJOR_VERSION >= 3
31 # include <opencv2/imgcodecs.hpp>
32 # include <opencv2/videoio.hpp>
33 #else
34 # include <opencv2/highgui/highgui.hpp>
35 #endif
36 
37 #include <opencv2/imgproc/imgproc.hpp>
38 
39 #include <vpi/Context.h>
40 #include <vpi/Image.h>
41 #include <vpi/Status.h>
42 #include <vpi/Stream.h>
45 
46 #include <algorithm>
47 #include <cstring> // for memset
48 #include <fstream>
49 #include <iostream>
50 #include <map>
51 #include <sstream>
52 #include <vector>
53 
54 #define CHECK_STATUS(STMT) \
55  do \
56  { \
57  VPIStatus status = (STMT); \
58  if (status != VPI_SUCCESS) \
59  { \
60  char buffer[VPI_MAX_STATUS_MESSAGE_LENGTH]; \
61  vpiGetLastStatusMessage(buffer, sizeof(buffer)); \
62  std::ostringstream ss; \
63  ss << vpiStatusGetName(status) << ": " << buffer; \
64  throw std::runtime_error(ss.str()); \
65  } \
66  } while (0);
67 
68 // Utility function to wrap a cv::Mat into a VPIImage
69 static VPIImage ToVPIImage(VPIImage image, const cv::Mat &frame)
70 {
71  VPIImageData imgData;
72  memset(&imgData, 0, sizeof(imgData));
73 
74  switch (frame.type())
75  {
76  case CV_8U:
77  imgData.type = VPI_IMAGE_FORMAT_U8;
78  break;
79  case CV_8UC3:
80  imgData.type = VPI_IMAGE_FORMAT_BGR8;
81  break;
82  case CV_8UC4:
83  imgData.type = VPI_IMAGE_FORMAT_BGRA8;
84  break;
85  default:
86  throw std::runtime_error("Frame type not supported");
87  }
88 
89  // First fill VPIImageData with the, well, image data...
90  imgData.numPlanes = 1;
91  imgData.planes[0].width = frame.cols;
92  imgData.planes[0].height = frame.rows;
93  imgData.planes[0].pitchBytes = frame.step[0];
94  imgData.planes[0].data = frame.data;
95 
96  if (image == nullptr)
97  {
98  // Now create a VPIImage that wraps it.
99  CHECK_STATUS(vpiImageCreateHostMemWrapper(&imgData, 0, &image));
100  }
101  else
102  {
103  // image is already created, we only have to update the wrapped memory,
104  // this is done without allocating memory.
105  CHECK_STATUS(vpiImageSetWrappedHostMem(image, &imgData));
106  }
107  return image;
108 };
109 
110 // Utility function to wrap a VPIImageData into a cv::Mat
111 static cv::Mat ToCV(const VPIImageData &imgData)
112 {
113  cv::Mat out;
114 
115  switch (imgData.type)
116  {
118  out = cv::Mat(imgData.planes[0].height, imgData.planes[0].width, CV_8UC3, imgData.planes[0].data,
119  imgData.planes[0].pitchBytes);
120  break;
121 
123  out = cv::Mat(imgData.planes[0].height, imgData.planes[0].width, CV_8UC4, imgData.planes[0].data,
124  imgData.planes[0].pitchBytes);
125  break;
126 
127  case VPI_IMAGE_FORMAT_U8:
128  out = cv::Mat(imgData.planes[0].height, imgData.planes[0].width, CV_8UC1, imgData.planes[0].data,
129  imgData.planes[0].pitchBytes);
130  break;
131 
132  default:
133  throw std::runtime_error("Frame type not supported");
134  }
135 
136  return out;
137 }
138 
139 int main(int argc, char *argv[])
140 {
141  // We'll create all our objects under this context, so that
142  // we don't have to track what objects to destroy. Just destroying
143  // the context will destroy all objects.
144  VPIContext ctx = nullptr;
145 
146  int retval = 0;
147 
148  try
149  {
150  if (argc != 4)
151  {
152  throw std::runtime_error(std::string("Usage: ") + argv[0] + " <cpu|vic|cuda> <input_video> <output>");
153  }
154 
155  std::string strBackend = argv[1];
156  std::string strInputVideo = argv[2];
157  std::string strOutputVideo = argv[3];
158 
159  // Load the input video
160  cv::VideoCapture invid;
161  if (!invid.open(strInputVideo))
162  {
163  throw std::runtime_error("Can't open '" + strInputVideo + "'");
164  }
165 
166  // Create our context.
167  CHECK_STATUS(vpiContextCreate(0, &ctx));
168 
169  // Activate it. From now on all created objects will be owned by it.
170  CHECK_STATUS(vpiContextSetCurrent(ctx));
171 
172  // Now parse the backend
173  VPIBackend backend;
174 
175  if (strBackend == "cpu")
176  {
177  backend = VPI_BACKEND_CPU;
178  }
179  else if (strBackend == "cuda")
180  {
181  backend = VPI_BACKEND_CUDA;
182  }
183  else if (strBackend == "vic")
184  {
185  backend = VPI_BACKEND_VIC;
186  }
187  else
188  {
189  throw std::runtime_error("Backend '" + strBackend +
190  "' not recognized, it must be either cpu, cuda or vic.");
191  }
192 
193  // Create the stream for the given backend.
194  VPIStream streamTNR;
195  CHECK_STATUS(vpiStreamCreate(backend, &streamTNR));
196 
197  VPIStream stream;
198  // PVA backend doesn't have currently Convert Image Format algorithm. We'll use CUDA
199  // backend to do that.
200  CHECK_STATUS(vpiStreamCreate(VPI_BACKEND_CUDA | backend, &stream));
201 
202 #if CV_MAJOR_VERSION >= 3
203  int w = invid.get(cv::CAP_PROP_FRAME_WIDTH);
204  int h = invid.get(cv::CAP_PROP_FRAME_HEIGHT);
205  int fourcc = invid.get(cv::CAP_PROP_FOURCC);
206  double fps = invid.get(cv::CAP_PROP_FPS);
207 #else
208  int w = invid.get(CV_CAP_PROP_FRAME_WIDTH);
209  int h = invid.get(CV_CAP_PROP_FRAME_HEIGHT);
210  int fourcc = invid.get(CV_CAP_PROP_FOURCC);
211  double fps = invid.get(CV_CAP_PROP_FPS);
212 #endif
213 
214  cv::VideoWriter outVideo(strOutputVideo, fourcc, fps, cv::Size(w, h));
215 
216  VPIImage imgPrevious, imgCurrent, imgOutput;
217  CHECK_STATUS(vpiImageCreate(w, h, VPI_IMAGE_FORMAT_NV12, 0, &imgPrevious));
218  CHECK_STATUS(vpiImageCreate(w, h, VPI_IMAGE_FORMAT_NV12, 0, &imgCurrent));
219  CHECK_STATUS(vpiImageCreate(w, h, VPI_IMAGE_FORMAT_NV12, 0, &imgOutput));
220 
221  // Create a Temporal Noise Reduction payload configured to process NV12
222  // frames under outdoor low light
223  VPIPayload tnr;
226 
227  VPIImage frameBGR = nullptr;
228 
229  int curFrame = 0;
230  cv::Mat cvFrame;
231  while (invid.read(cvFrame))
232  {
233  printf("Frame: %d\n", ++curFrame);
234 
235  frameBGR = ToVPIImage(frameBGR, cvFrame);
236 
237  // First convert it to NV12
238  CHECK_STATUS(
239  vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, frameBGR, imgCurrent, VPI_CONVERSION_CAST, 1, 0));
240 
241  // Apply temporal noise reduction
242  // For first frame, we have to pass nullptr as previous frame, this will reset internal
243  // state.
244  CHECK_STATUS(vpiSubmitTemporalNoiseReduction(stream, tnr, curFrame == 1 ? nullptr : imgPrevious, imgCurrent,
245  imgOutput));
246 
247  // Convert output back to BGR
248  CHECK_STATUS(
249  vpiSubmitConvertImageFormat(stream, VPI_BACKEND_CUDA, imgOutput, frameBGR, VPI_CONVERSION_CAST, 1, 0));
250  CHECK_STATUS(vpiStreamSync(stream));
251 
252  // Now add it to the output video stream
253  VPIImageData imgdata;
254  CHECK_STATUS(vpiImageLock(frameBGR, VPI_LOCK_READ, &imgdata));
255  outVideo << ToCV(imgdata);
256  CHECK_STATUS(vpiImageUnlock(frameBGR));
257 
258  // this iteration's output will be next's previous. Previous, which would be discarded, will be reused
259  // to store next frame.
260  std::swap(imgPrevious, imgOutput);
261  };
262  }
263  catch (std::exception &e)
264  {
265  std::cerr << e.what() << std::endl;
266  retval = 1;
267  }
268 
269  // Clean up
270  vpiContextDestroy(ctx);
271 
272  return retval;
273 }
VPIImagePlane::height
uint32_t height
Height of this plane in pixels.
Definition: Image.h:138
VPIContext
struct VPIContextImpl * VPIContext
A handle to a context.
Definition: Types.h:178
TemporalNoiseReduction.h
Declares functions that implement the Temporal Noise Reduction algorithm.
VPIImagePlane::width
uint32_t width
Width of this plane in pixels.
Definition: Image.h:137
VPI_IMAGE_FORMAT_NV12
@ VPI_IMAGE_FORMAT_NV12
YUV420sp 8-bit pitch-linear format composed of two planes:
Definition: ImageFormat.h:123
vpiStreamCreate
VPIStatus vpiStreamCreate(uint32_t flags, VPIStream *stream)
Create a stream instance.
VPIBackend
VPIBackend
VPI Backend types.
Definition: Types.h:89
vpiContextCreate
VPIStatus vpiContextCreate(uint32_t flags, VPIContext *ctx)
Create a context instance.
vpiContextSetCurrent
VPIStatus vpiContextSetCurrent(VPIContext ctx)
Sets the context for the calling thread.
VPI_LOCK_READ
@ VPI_LOCK_READ
Lock memory only for reading.
Definition: Types.h:447
VPI_IMAGE_FORMAT_BGRA8
@ VPI_IMAGE_FORMAT_BGRA8
Single plane with interleaved BGRA 8-bit channel.
Definition: ImageFormat.h:141
VPI_IMAGE_FORMAT_BGR8
@ VPI_IMAGE_FORMAT_BGR8
Single plane with interleaved BGR 8-bit channel.
Definition: ImageFormat.h:135
vpiImageUnlock
VPIStatus vpiImageUnlock(VPIImage img)
Releases the lock on an image object.
vpiStreamSync
VPIStatus vpiStreamSync(VPIStream stream)
Blocks the calling thread until all submitted commands in this stream queue are done (queue is empty)...
VPI_BACKEND_CUDA
@ VPI_BACKEND_CUDA
CUDA backend.
Definition: Types.h:91
VPIImageData
Stores information about image characteristics and content.
Definition: Image.h:159
VPIStream
struct VPIStreamImpl * VPIStream
A handle to a stream.
Definition: Types.h:190
vpiContextDestroy
void vpiContextDestroy(VPIContext ctx)
Destroy a context instance as well as all resources it owns.
VPIImageData::planes
VPIImagePlane planes[VPI_MAX_PLANE_COUNT]
Data of all image planes.
Definition: Image.h:166
VPI_TNR_DEFAULT
@ VPI_TNR_DEFAULT
Chooses the version with best quality available in the current device and given backend.
Definition: TemporalNoiseReduction.h:99
vpiImageCreate
VPIStatus vpiImageCreate(uint32_t width, uint32_t height, VPIImageFormat fmt, uint32_t flags, VPIImage *img)
Create an empty image instance with the specified flags.
Image.h
Functions and structures for dealing with VPI images.
vpiSubmitTemporalNoiseReduction
VPIStatus vpiSubmitTemporalNoiseReduction(VPIStream stream, VPIPayload payload, VPIImage prevFrame, VPIImage curFrame, VPIImage outFrame)
Submits a Temporal Noise Reduction operation to the stream associated with the given payload.
VPI_IMAGE_FORMAT_U8
@ VPI_IMAGE_FORMAT_U8
Single plane with one 8-bit unsigned integer channel.
Definition: ImageFormat.h:99
VPIImagePlane::pitchBytes
uint32_t pitchBytes
Difference in bytes of beginning of one row and the beginning of the previous.
Definition: Image.h:139
VPIImage
struct VPIImageImpl * VPIImage
A handle to an image.
Definition: Types.h:196
VPIImageData::numPlanes
int32_t numPlanes
Number of planes.
Definition: Image.h:161
VPI_BACKEND_VIC
@ VPI_BACKEND_VIC
VIC backend.
Definition: Types.h:93
VPI_TNR_PRESET_INDOOR_LOW_LIGHT
@ VPI_TNR_PRESET_INDOOR_LOW_LIGHT
Low light indoor scene.
Definition: TemporalNoiseReduction.h:88
vpiSubmitConvertImageFormat
VPIStatus vpiSubmitConvertImageFormat(VPIStream stream, VPIBackend backend, VPIImage input, VPIImage output, VPIConversionPolicy convPolicy, float scale, float offset)
Converts the image contents to the desired format, with optional scaling and offset.
VPIPayload
struct VPIPayloadImpl * VPIPayload
A handle to an algorithm payload.
Definition: Types.h:208
vpiImageLock
VPIStatus vpiImageLock(VPIImage img, VPILockMode mode, VPIImageData *hostData)
Acquires the lock on an image object and returns a pointer to the image planes.
VPIImageData::type
VPIImageFormat type
Image type.
Definition: Image.h:160
Status.h
Declaration of VPI status codes handling functions.
ConvertImageFormat.h
Declares functions that handle image format conversion.
VPI_BACKEND_CPU
@ VPI_BACKEND_CPU
CPU backend.
Definition: Types.h:90
VPI_CONVERSION_CAST
@ VPI_CONVERSION_CAST
Casts input to the output type.
Definition: Types.h:336
vpiCreateTemporalNoiseReduction
VPIStatus vpiCreateTemporalNoiseReduction(VPIBackend backend, uint32_t width, uint32_t height, VPIImageFormat imgFormat, VPITNRVersion version, VPITNRPreset preset, float strength, VPIPayload *payload)
Creates a payload for Temporal Noise Reduction algorithm.
vpiImageCreateHostMemWrapper
VPIStatus vpiImageCreateHostMemWrapper(const VPIImageData *hostData, uint32_t flags, VPIImage *img)
Create an image object by wrapping around an existing host memory block.
Stream.h
Declares functions dealing with VPI streams.
vpiImageSetWrappedHostMem
VPIStatus vpiImageSetWrappedHostMem(VPIImage img, const VPIImageData *hostData)
Redefines the wrapped host memory in an existing VPIImage wrapper.
VPIImagePlane::data
void * data
Pointer to the first row of this plane.
Definition: Image.h:147
Context.h
Functions and structures for dealing with VPI contexts.