VPI - Vision Programming Interface

0.3.7 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 Image Format Converter 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.3/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.

/*
* Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of NVIDIA CORPORATION nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <opencv2/core/version.hpp>
#if CV_MAJOR_VERSION >= 3
# include <opencv2/imgcodecs.hpp>
# include <opencv2/videoio.hpp>
#else
# include <opencv2/highgui/highgui.hpp>
#endif
#include <opencv2/imgproc/imgproc.hpp>
#include <vpi/Context.h>
#include <vpi/Image.h>
#include <vpi/Stream.h>
#include <algorithm>
#include <cstring> // for memset
#include <fstream>
#include <iostream>
#include <map>
#include <vector>
#define CHECK_STATUS(STMT) \
do \
{ \
VPIStatus status = (STMT); \
if (status != VPI_SUCCESS) \
{ \
throw std::runtime_error(vpiStatusGetName(status)); \
} \
} while (0);
// Utility function to wrap a cv::Mat into a VPIImage
static VPIImage ToVPIImage(const cv::Mat &frame)
{
VPIImageData imgData;
memset(&imgData, 0, sizeof(imgData));
switch (frame.type())
{
case CV_8U:
break;
case CV_8UC3:
break;
case CV_8UC4:
break;
default:
throw std::runtime_error("Frame type not supported");
}
// First fill VPIImageData with the, well, image data...
imgData.numPlanes = 1;
imgData.planes[0].width = frame.cols;
imgData.planes[0].height = frame.rows;
imgData.planes[0].rowStride = frame.step[0];
imgData.planes[0].data = frame.data;
// Now create a VPIImage that wraps it.
VPIImage img;
CHECK_STATUS(vpiImageWrapHostMem(&imgData, 0, &img));
return img;
};
// Utility function to wrap a VPIImageData into a cv::Mat
static cv::Mat ToCV(const VPIImageData &imgData)
{
cv::Mat out;
switch (imgData.type)
{
out = cv::Mat(imgData.planes[0].height, imgData.planes[0].width, CV_8UC3, imgData.planes[0].data,
imgData.planes[0].rowStride);
break;
out = cv::Mat(imgData.planes[0].height, imgData.planes[0].width, CV_8UC4, imgData.planes[0].data,
imgData.planes[0].rowStride);
break;
out = cv::Mat(imgData.planes[0].height, imgData.planes[0].width, CV_8UC1, imgData.planes[0].data,
imgData.planes[0].rowStride);
break;
default:
throw std::runtime_error("Frame type not supported");
}
return out;
}
int main(int argc, char *argv[])
{
// We'll create all our objects under this context, so that
// we don't have to track what objects to destroy. Just destroying
// the context will destroy all objects.
VPIContext ctx = nullptr;
int retval = 0;
try
{
if (argc != 4)
{
throw std::runtime_error(std::string("Usage: ") + argv[0] + " <cpu|pva|cuda> <input_video> <output>");
}
std::string strDevType = argv[1];
std::string strInputVideo = argv[2];
std::string strOutputVideo = argv[3];
// Load the input video
cv::VideoCapture invid;
if (!invid.open(strInputVideo))
{
throw std::runtime_error("Can't open '" + strInputVideo + "'");
}
// Create our context.
CHECK_STATUS(vpiContextCreate(0, &ctx));
// Activate it. From now on all created objects will be owned by it.
CHECK_STATUS(vpiContextSetCurrent(ctx));
// Now process the device type
VPIDeviceType devType;
if (strDevType == "cpu")
{
}
else if (strDevType == "cuda")
{
}
else if (strDevType == "pva")
{
}
else
{
throw std::runtime_error("Backend '" + strDevType +
"' not recognized, it must be either cpu, cuda or pva.");
}
// Create the stream for the given backend.
VPIStream streamTNR;
CHECK_STATUS(vpiStreamCreate(devType, &streamTNR));
VPIStream streamConv;
if (devType == VPI_DEVICE_TYPE_PVA)
{
// PVA backend doesn't have currently Image Format Converter algorithm. We'll use CUDA
// backend to do that.
CHECK_STATUS(vpiStreamCreate(VPI_DEVICE_TYPE_CUDA, &streamConv));
}
else
{
// On other cases, we can use the same stream to do format conversion.
streamConv = streamTNR;
}
int w = invid.get(cv::CAP_PROP_FRAME_WIDTH);
int h = invid.get(cv::CAP_PROP_FRAME_HEIGHT);
cv::VideoWriter outVideo(strOutputVideo, invid.get(cv::CAP_PROP_FOURCC), invid.get(cv::CAP_PROP_FPS),
cv::Size(w, h));
VPIImage imgPrevious, imgCurrent, imgOutput;
CHECK_STATUS(vpiImageCreate(w, h, VPI_IMAGE_TYPE_NV12, 0, &imgPrevious));
CHECK_STATUS(vpiImageCreate(w, h, VPI_IMAGE_TYPE_NV12, 0, &imgCurrent));
CHECK_STATUS(vpiImageCreate(w, h, VPI_IMAGE_TYPE_NV12, 0, &imgOutput));
// Create a Temporal Noise Reduction payload configured to process NV12
// frames under outdoor low light
int curFrame = 1;
cv::Mat cvFrame;
while (invid.read(cvFrame))
{
printf("Frame: %d\n", curFrame++);
VPIImage frameBGR = ToVPIImage(cvFrame);
// First convert it to NV12
CHECK_STATUS(vpiSubmitImageFormatConverter(streamConv, frameBGR, imgCurrent, VPI_CONVERSION_CAST, 1, 0));
if (streamConv != streamTNR)
{
// Ideally we'd use VPIEvents to synchronize streamTNR and streamConv
// (vpiEventRecord and vpiStreamWaitFor) but currently this doesn't work.
// We have to synchronize the whole stream.
CHECK_STATUS(vpiStreamSync(streamConv));
}
// Apply temporal noise reduction
// For first frame, we have to pass nullptr as previous frame, this will reset internal
// state.
CHECK_STATUS(
vpiSubmitTemporalNoiseReduction(tnr, curFrame == 1 ? nullptr : imgPrevious, imgCurrent, imgOutput));
if (streamConv != streamTNR)
{
CHECK_STATUS(vpiStreamSync(streamTNR));
}
// Convert output back to BGR
CHECK_STATUS(vpiSubmitImageFormatConverter(streamConv, imgOutput, frameBGR, VPI_CONVERSION_CAST, 1, 0));
CHECK_STATUS(vpiStreamSync(streamConv));
// Now add it to the output video stream
VPIImageData imgdata;
CHECK_STATUS(vpiImageLock(frameBGR, VPI_LOCK_READ, &imgdata));
outVideo << ToCV(imgdata);
CHECK_STATUS(vpiImageUnlock(frameBGR));
// Not needed anymore
vpiImageDestroy(frameBGR);
// this iteration's output will be next's previous. Previous, which would be discarded, will be reused
// to store next frame.
std::swap(imgPrevious, imgOutput);
};
}
catch (std::exception &e)
{
std::cerr << e.what() << std::endl;
retval = 1;
}
// Clean up
return retval;
}
VPIImagePlane::height
uint32_t height
Height of this plane in pixels.
Definition: Image.h:137
VPIContext
struct VPIContextImpl * VPIContext
A handle to a context.
Definition: Types.h:165
vpiCreateTemporalNoiseReduction
VPIStatus vpiCreateTemporalNoiseReduction(VPIStream stream, uint32_t width, uint32_t height, VPIImageType imgType, VPITNRVersion version, VPITNRPreset preset, float strength, VPIPayload *payload)
Creates a payload for Temporal Noise Reduction algorithm.
TemporalNoiseReduction.h
VPIImagePlane::width
uint32_t width
Width of this plane in pixels.
Definition: Image.h:136
VPI_IMAGE_TYPE_NV12
@ VPI_IMAGE_TYPE_NV12
8-bit NV12.
Definition: Types.h:212
VPI_IMAGE_TYPE_BGR8
@ VPI_IMAGE_TYPE_BGR8
8-bit BGR, B having the lowest address.
Definition: Types.h:217
VPI_CONVERSION_CAST
@ VPI_CONVERSION_CAST
Casts input to the output type.
Definition: Types.h:388
vpiContextCreate
VPIStatus vpiContextCreate(uint32_t flags, VPIContext *ctx)
Create a context instance.
VPI_DEVICE_TYPE_CPU
@ VPI_DEVICE_TYPE_CPU
CPU backend.
Definition: Types.h:556
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:499
VPIImagePlane::rowStride
uint32_t rowStride
Difference in bytes of beginning of one row and the beginning of the previous.
Definition: Image.h:138
vpiSubmitTemporalNoiseReduction
VPIStatus vpiSubmitTemporalNoiseReduction(VPIPayload payload, VPIImage prevFrame, VPIImage curFrame, VPIImage outFrame)
Submits a Temporal Noise Reduction operation to the stream associated with the given payload.
vpiImageUnlock
VPIStatus vpiImageUnlock(VPIImage img)
Releases the lock on an image object.
vpiStreamCreate
VPIStatus vpiStreamCreate(VPIDeviceType devType, VPIStream *stream)
Create a stream instance.
vpiStreamSync
VPIStatus vpiStreamSync(VPIStream stream)
Blocks the calling thread until all submitted commands in this stream queue are done (queue is empty)...
VPIImageData
Stores information about image characteristics and content.
Definition: Image.h:155
VPIStream
struct VPIStreamImpl * VPIStream
A handle to a stream.
Definition: Types.h:177
vpiContextDestroy
void vpiContextDestroy(VPIContext ctx)
Destroy a context instance as well as all resources it owns.
VPI_DEVICE_TYPE_PVA
@ VPI_DEVICE_TYPE_PVA
PVA backend.
Definition: Types.h:558
VPIImageData::planes
VPIImagePlane planes[VPI_MAX_PLANE_COUNT]
Data of all image planes.
Definition: Image.h:162
VPI_TNR_DEFAULT
@ VPI_TNR_DEFAULT
Chooses the version with best quality available in the current device and given backend.
Definition: TemporalNoiseReduction.h:98
VPI_DEVICE_TYPE_CUDA
@ VPI_DEVICE_TYPE_CUDA
CUDA backend.
Definition: Types.h:557
vpiImageDestroy
void vpiImageDestroy(VPIImage img)
Destroy an image instance as well as all resources it owns.
Image.h
ImageFormatConverter.h
vpiImageWrapHostMem
VPIStatus vpiImageWrapHostMem(const VPIImageData *hostData, uint32_t flags, VPIImage *img)
Create an image object by wrapping around an existing host-memory block.
VPIImage
struct VPIImageImpl * VPIImage
A handle to an image.
Definition: Types.h:183
VPIImageData::numPlanes
int32_t numPlanes
Number of planes.
Definition: Image.h:157
VPI_TNR_PRESET_INDOOR_LOW_LIGHT
@ VPI_TNR_PRESET_INDOOR_LOW_LIGHT
Low light indoor scene.
Definition: TemporalNoiseReduction.h:87
vpiSubmitImageFormatConverter
VPIStatus vpiSubmitImageFormatConverter(VPIStream stream, 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:195
vpiImageLock
VPIStatus vpiImageLock(VPIImage img, VPILockMode mode, VPIImageData *hostData)
Acquires the lock on an image object and returns a pointer to the image planes Depending on the inter...
VPIImageData::type
VPIImageType type
Image type.
Definition: Image.h:156
vpiImageCreate
VPIStatus vpiImageCreate(uint32_t width, uint32_t height, VPIImageType type, uint32_t flags, VPIImage *img)
Create an empty image instance with the specified flags.
VPI_IMAGE_TYPE_U8
@ VPI_IMAGE_TYPE_U8
unsigned 8-bit grayscale.
Definition: Types.h:208
VPI_IMAGE_TYPE_BGRA8
@ VPI_IMAGE_TYPE_BGRA8
8-bit BGRA, B having the lowest address.
Definition: Types.h:218
Stream.h
VPIDeviceType
VPIDeviceType
Device types.
Definition: Types.h:554
VPIImagePlane::data
void * data
Pointer to the first row of this plane.
Definition: Image.h:146
Context.h