0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用NVIDIA VPI降低图像的时间噪声

星星科技指导员 来源:NVIDIA 作者:NVIDIA 2022-04-27 10:37 次阅读

在本文中,我们将向您展示如何在 Jetson 产品系列上运行时间降噪( TNR )示例应用程序。

在 Jetson 设备上设置 VPI

通过 SDK 管理器设置 Jetson 设备时,请确保选中 Jetson SDK 组件框。然后在设备闪存时安装 VPI 。

安装完成后,可以在以下路径下找到 VPI :

/opt/nvidia/vpi1/

要验证环境设置是否正确,请将 VPI 示例应用程序复制到主目录中,然后构建 TNR 示例。

$ vpi1_install_samples.sh $HOME
$ cd $HOME/NVIDIA_VPI–samples/09-tnr
$ cmake .
$ make

VPI 在运行离散 GPU 的 x86 机器上也受支持。有关更多信息,请参阅 VPI – Vision 编程接口文档中的 Installation 。

TNR 示例应用程序

VPI 提供了一组 CV 算法,这些算法利用多个后端高效地使用设备的可用计算资源。 TNR 是在 Jetson 设备上运行的计算机视觉应用程序中常用的一种降噪方法。本文使用 TNR 示例应用程序来演示如何使用 VPI 中的一些关键概念和组件来实现自己的应用程序。

我们将在本帖中介绍以下主题:

创建构建 VPI 管道所需的元素

了解如何与 OpenCV 进行互操作

向流提交处理任务

同步流中的任务

锁定图像缓冲区以便可以从 CPU 访问

TNR 样本可在以下路径中找到:

$HOME/NVIDIA_VPI–samples/09-tnr/main.cpp

有关示例应用程序和算法的更多信息,请参阅以下参考资料

时间降噪示例应用程序

时域降噪算法

算法版本和后端支持

硬件引擎在 VPI 中被命名为 backends 。通过使用 Jetson 设备固有的可用系统级并行性,这些后端使您能够卸载可并行处理阶段并加速应用程序。后端是 CPU 、 CUDA ( GPU )、 PVA 和 VIC 。特定后端引擎的确切可用性取决于应用程序部署到的 Jetson 平台。有关特定平台上可用算法、后端支持和后端可用性的更多信息,请参阅 Algorithms 。

VPI 目前为 TNR 提供了两种不同的实现,每种实现都适合不同的场景和需求。这些版本采用双边滤波平滑平坦区域,同时保留边缘,和时间无限脉冲响应( IIR )滤波与运动检测器的结合,以处理跨帧的时间噪声。

VPI_TNR_V2 – 与 VPI_TNR_V3 相比,该版本提供了更轻的噪音降低,并且具有一定程度的可配置性,即可以调整照明条件以更好地适应给定场景。这个版本有一个减少的计算需求,这转化为速度。它适用于执行时间比降噪质量更重要的用例。

VPI_TNR_V3 —用于需要更好质量的降噪的用例。与 VPI_TNR_V2 相比,使用这个变体,您应该期望计算需求会增加。除此之外,还进一步扩展了可配置性。建议用于具有挑战性的弱光场景。

VPI_TNR_DEFAULT – 您可以使用默认值,而不是指定确切的版本,该值选择给定后端支持的噪声抑制最强的版本。

在决定哪个算法版本适合您的用例时,需要考虑的另一个标准是它对不同后端和设备的支持。下表总结了 TNR 支持。

VPI_TNR_V2 和 VPI_TNR_V3 都允许显式设置要捕获的场景的照明条件,从而启用调整。这在低光场景或以高增益捕获的流的上下文中是重要的,所述低光场景或流可能包含更高的噪声级,并且因此要求更高的噪声降低水平。

较高的强度级别可能会影响帧的纹理区域中的细节数量,从而使其平滑。另一个副作用是在有快速移动物体的场景中重影。支持的场景照明条件在类型(室内、室外)和强度(低、中、高)方面有所不同,如下表所示。

使用不同的版本和相关的照明条件预设,您可以根据用例的具体情况调整 TNR 算法。这可以通过所谓的强度系数进一步定制。它是一个浮动参数,范围从 0 到 1 ,其中较大的值对应于增加的去噪强度。

VPI 应用程序

VPI 的一个关键方面是如何管理和协调在不同后端之间运行应用程序所需的资源。使用 VPI ,可以避免在处理阶段之间浪费内存拷贝。 VPI 为实现高效的内存管理而实施的另一种机制是在其接口处进行内存包装。

利用 VPI 的所有内存管理特性取决于代码的结构。最佳实践是将代码视为三阶段工作流:

Initialization

处理回路

Cleanup

大部分内存分配应该在初始化阶段进行。对于在可用资源受限的设备上运行的嵌入式应用程序,这一点尤为重要。除此之外,还可以更有效、更谨慎地进行内存管理,以避免可能的内存泄漏。

VPI 中的一个好做法是指定使用内存的后端。在这点上,只将 VPI 对象订阅到所需的后端集可以保证在管道在这些后端之间流动时获得最有效的内存路径。

处理循环是执行处理管道的地方。想象一下,一个应用程序在一个包含数百个单独帧的视频文件上迭代。主循环将主要负责对像素信息执行所需的变换,以实现给定计算机视觉任务的预期结果。

最后,清理阶段处理在任务执行期间使用的资源的所有必要释放和释放。坚持这种模式可以使 VPI 尽可能使用最有效的处理管道,并帮助您坚持良好的编码实践。

与 OpenCV 接口

VPI 与 OpenCV 的互操作性是该库的一个显著特征。如果您熟悉 OpenCV ,您可以轻松地将 VPI 与工作流集成,或者扩展现有的数据管道,以便更好地使用 VPI 提供的硬件加速。

TNR 示例中通过以下实用函数演示了这一点,该函数将使用 OpenCV 捕获的输入视频帧包装到 VPI 图像对象中。

 69 // Utility function to wrap a cv::Mat into a VPIImage
 70 static VPIImage ToVPIImage(VPIImage image, const cv::Mat &frame)
 71 {
 72 if (image == nullptr)
 73 {
 74 // Create a VPIImage that wraps the frame
 75 CHECK_STATUS(vpiImageCreateOpenCVMatWrapper(frame, 0, &image));
 76 }
 77 else
 78 {
 79 // reuse existing VPIImage wrapper to wrap the new frame.
 80 CHECK_STATUS(vpiImageSetWrappedOpenCVMat(image, frame));
 81 }
 82 return image;
 83 }

从更深入地研究前面描述的函数开始。它意味着将 OpenCV 矩阵( cv::Mat )对象包装到 VPI 图像对象( VPIImage )。要上下文化, VPI 图像基本上是任何可以用宽度、高度和格式来描述的 2D 数据结构。尽管将图像数据视为 VPIImage 对象是直观的,但它的用法也可以扩展到其他类型的数据,例如二维向量场和热图。

The utility wrapping function invokes two other functions that pertain to the VPI OpenCVInterop.hpp module, which aims to provide useful infrastructure to integrate OpenCV-based code with VPI.

vpiImageCreateOpenCVMatWrapper —一个重载函数,它以两种不同的方式将 cv:Mat 对象包装到 VPIImage 中。第一种方法尝试直接从输入类型推断格式(遵循特定的规则),而第二种方法将显式格式作为其参数之一。

vpiImageSetWrappedOpenCVMat – 重用为特定 cv::Mat 对象定义的包装器来包装新的传入 cv::Mat 对象。这里的重点是避免在第一时间创建包装时产生的内存分配,这样效率更高。传入的 cv::Mat 对象必须呈现与创建时使用的原始对象相同的特征(格式和尺寸)。

流创建

main 函数捕获设置 VPI 管道以完成工作的相关步骤。管道的定义很简单,也很直观。在 VPI 中,管道是一个或多个数据流的组合,这些数据流流经不同的处理阶段。

图 1 以一种通用的方式显示了管道及其构建块(流、缓冲区、算法等)。为了简单起见,省略了一些组件。

图 1 通用 VPI 管道。

流的目的是强制执行一个排队的步骤序列,数据需要通过该序列来完成特定的计算机视觉任务。这些步骤可能包括数据的预处理或后处理,甚至包括 TNR 之类的成熟算法。图 2 显示了 VPIStream 对象的示例。

图 2 VPIStream 对象。

VPI 可适应各种不同的管道复杂性。您可以用一个流实现一个简单的管道,或者用几个不同阶段的并行流实现一个更复杂的实现,并将这些并行流卸载到不同的计算后端。这是 API 的一个强大功能,因为它使您能够获得对 Jetson 设备提供的系统级并行性的更多控制。

下面的代码示例演示如何在 TNR 示例中创建流。

143 VPIStream stream;
144 // PVA backend doesn't have currently Convert Image Format algorithm.
145 // Use the CUDA backend to do that.
146 CHECK_STATUS(vpiStreamCreate(VPI_BACKEND_CUDA | backend, &stream));

选择的后端正在传递到流中。这是一个可选步骤。使用零值将启用所有可用的后端。但是,分配一组特定的后端是推荐的做法,因为它有助于优化内存分配。

TNR 有效载荷

有效负载基本上是管道执行期间所需的临时资源。例如,有效负载可以是中间内存缓冲区,用于存储流的后续阶段之间交换的数据。包括 TNR 在内的许多算法都需要显式地创建有效负载,具体实现如下。

172 // Create a TNR payload configured to process NV12
173 // frames under outdoor low-light scenarios.
174 VPIPayload tnr;
175 CHECK_STATUS(vpiCreateTemporalNoiseReduction(backend, w, h, VPI_IMAGE_FORMAT_NV12_ER, VPI_TNR_DEFAULT,
176 VPI_TNR_PRESET_INDOOR_LOW_LIGHT, 1, &tnr));

对于 TNR 有效负载,请提供以下参数:

图像尺寸(宽度和高度)

Backend

图像数据格式(目前仅支持 NV12 )

TNR 算法版本

照明条件

降噪强度

对算法有效负载的引用

最终,该函数创建一个有效负载并将其绑定到指定的后端。

图像缓冲区

除了创建流和有效负载外,还必须创建 VPI 算法所需的图像缓冲区。在 TNR 中,使用双边和 IIR 滤波器的组合,因此需要三个不同的缓冲器,即当前和先前的图像输入和图像输出。

可以按如下方式创建图像缓冲区:

167 VPIImage imgPrevious, imgCurrent, imgOutput;
168 CHECK_STATUS(vpiImageCreate(w, h,VPI_IMAGE_FORMAT_NV12_ER, 0, &imgPrevious));
169 CHECK_STATUS(vpiImageCreate(w, h,VPI_IMAGE_FORMAT_NV12_ER, 0, &imgCurrent));
170 CHECK_STATUS(vpiImageCreate(w, h,VPI_IMAGE_FORMAT_NV12_ER, 0, &imgOutput));

这将创建具有以下指定特征的空缓冲区:

图像尺寸(宽度和高度)

格式(根据算法要求)

图像标志(当前用于分配后端)

指向返回所创建映像的 VPIImage 句柄的变量的指针

流处理

在构建块已经就位的情况下,您可以转到主处理循环,在那里执行降噪算法。在 TNR 样本上,循环迭代来自视频文件的每个单独帧,并执行必要的连续步骤以获得所需的结果。

当从视频中收集帧时,第一步是使用前面描述的实用程序函数将其包装成 VPIImage 对象。

186 frameBGR = ToVPIImage(frameBGR, cvFrame);

包装完成后, VPI 现在可以对 VPIImage 对象中的像素数据进行操作。因为 TNR 要求帧是 NV12 格式,所以需要一个转换步骤。

188 // First convert it to NV12
189 CHECK_STATUS(vpiSubmitConvertImageFormat(stream,VPI_BACKEND_CUDA, frameBGR, imgCurrent, NULL));

在此阶段,转换图像的特定任务与先前实例化的流相关联。除此之外,任务被设置为在 GPU 上执行。输入帧的图像缓冲区以及刚刚从cv::Mat对象包装的数据都用于此目的。

格式转换完成后,可以将输入缓冲区传递给 TNR 算法进行处理。

191 // Apply TNR
192 // For first frame, you must pass nullptr as the previous frame, this resets the internal
193 // state.
194 CHECK_STATUS(vpiSubmitTemporalNoiseReduction(stream, 0, tnr, curFrame == 1 ? nullptr : imgPrevious,
195 imgCurrent, imgOutput));
196

要调用 TNR 算法,请设置以下参数:

  • 与算法关联的流
  • 后端
  • 算法有效负载,如前面实例化的一样
  • 图像缓冲区:以前和当前的输入和输出

在第一次迭代(curFrame == 1)时,缓冲区上没有有效的前一个映像,而是传递一个空指针。对于下面的迭代,缓冲区将相应地填充。在执行 TNR 算法之后,输出缓冲区可以从 NV12 转换回以前的 BGR 格式。

197 // Convert output back to BGR
198 CHECK_STATUS(vpiSubmitConvertImageFormat(stream,VPI_BACKEND_CUDA, imgOutput, frameBGR, NULL));

在这一点上,必须提到 VPI 对流阶段实施了非阻塞异步范例。这对于作为后端分布在不同协处理器之间的工作负载的平滑和高效的编排是必不可少的。对于进一步的步骤,请确保在继续之前已完成向流发出的所有活动。这时同步功能就派上用场了。

199 CHECK_STATUS(vpiStreamSync(stream));

VPI 现在确保与流相关的所有正在进行的活动在进入管道的下一个阶段之前都已完成。同步完成后,帧就可以在连接到指定后端的输出缓冲区中使用了。为了能够将其写入输出视频流(在这种情况下是一个文件),必须锁定图像,以便 CPU 可以使用缓冲区。

这就解释了为什么在锁定帧之前进行同步是避免处理问题的关键步骤。因为 VPI 是异步操作的,所以在没有同步的情况下,缓冲区会在前一阶段完成之前被锁定。结果是不可预测的。

201 // Now add it to the output video stream
202 VPIImageData imgdata;
203 CHECK_STATUS(vpiImageLock(frameBGR,VPI_LOCK_READ, &imgdata));
204
205 cv::Mat outFrame;
206 CHECK_STATUS(vpiImageDataExportOpenCVMat(imgdata, &outFrame));
207 outVideo << outFrame;
 208
 209             CHECK_STATUS(vpiImageUnlock(frameBGR));

如您所见,锁定的缓冲区由 CPU 处理,以供进一步使用。锁被设置为只读,然后图像缓冲区被映射到 CPU 。锁定时, VPI 无法在缓冲区上工作。在 CPU 将输出帧提供给视频编码器后,缓冲区可以被解锁并被 VPI 进一步使用。

VPI 数据流

TNR 示例应用程序可以概括为以下数据流。其他的小步骤也是应用程序不可分割的一部分,但是为了简单起见,图 3 中只包含了宏步骤。

图 3 TNR 示例应用程序中的数据流。

从视频流或文件中收集输入帧。 OpenCV 已用于此目的。

必要的 VPI 元素被实例化:单个流、 TNR 算法负载以及用于先前和当前输入和输出图像的图像缓冲区。

输入帧被包装到 VPIImage 缓冲区中。

缓冲区上的像素数据被转换成 NV12 ,以便 TNR 算法可以处理它。当算法完成执行时,它会恢复到原始格式。

图像缓冲区被锁定,以便 CPU 可以访问数据。将图像提供给视频输出后,可以解锁缓冲区, VPI 可以进一步处理它。

概括

在本文中,我们向您展示了如何在 Jetson 产品系列上运行 TNR 示例应用程序。

关于作者

Maycon da Silva Carvalho 是 Jetson 的现场应用工程师。他负责与部署基于 Jetson 的应用程序的不同行业的客户进行多学科技术合作。

Rodolfo Schulz de Lima 是 VPI 的首席工程师。他拥有 UFRJ 里约热内卢联邦大学电子工程学士学位,并在巴西里约热内卢的 IMPA (纯数学和应用数学研究所)学习计算机图形学。

审核编辑:郭婷

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    5017

    文章

    18496

    浏览量

    293165
  • gpu
    gpu
    +关注

    关注

    27

    文章

    4508

    浏览量

    127529
  • SDK
    SDK
    +关注

    关注

    3

    文章

    981

    浏览量

    45076
收藏 人收藏

    评论

    相关推荐

    OpenCV图像降噪算法的中值滤波与高斯滤波详解

    图像噪声是指图像中不希望出现的随机亮度或颜色变化,通常会降低图像的清晰度和可辨识度,以及会降低
    的头像 发表于 04-03 14:58 1695次阅读
    OpenCV<b class='flag-5'>图像</b>降噪算法的中值滤波与高斯滤波详解

    电源滤波器是怎么降低电源噪声的?

    电源滤波器是怎么降低电源噪声的? 电源滤波器是一种电子装置,用于减少或消除电源中的噪声噪声是电力系统中的一个常见问题,它可以由各种因素引起,包括电源脉动、电源杂散、电磁干扰等。这些
    的头像 发表于 12-15 14:37 544次阅读

    FPGA图像处理方法

    图像在采集和传输的过程中,通常会产生噪声,使图像质量降低,影响后续处理。因此须对图像进行一些图像
    的头像 发表于 12-02 13:15 764次阅读

    降低开关电源噪声

    降低开关电源噪声
    的头像 发表于 11-24 15:39 306次阅读

    什么是变压器的电气噪声?如何降低电气噪声

    什么是变压器的电气噪声?如何降低电气噪声? 电气噪声是指由变压器本身或其工作过程中引起的声音。变压器是电能传输和变换的重要设备,广泛应用于电力系统中。然而,变压器不可避免地会产生
    的头像 发表于 11-23 14:13 433次阅读

    PD放大电路主要的噪声源是哪些?如何降低PD放大电路噪声

    PD放大电路主要的噪声源是哪些?如何降低PD放大电路噪声? PD放大电路的主要噪声源包括热噪声、随机噪声
    的头像 发表于 11-06 11:14 518次阅读

    如何降低无数电机的换相噪声

    如何降低无数电机的换相噪声
    发表于 10-28 07:36

    如何降低微控制器系统中的噪声影响(2)

    如何降低微控制器系统中的噪声影响(2)
    的头像 发表于 10-25 17:41 345次阅读
    如何<b class='flag-5'>降低</b>微控制器系统中的<b class='flag-5'>噪声</b>影响(2)

    降低电源纹波噪声的方法与实例

    降低电源纹波噪声的方法与实例
    的头像 发表于 10-24 17:37 383次阅读
    <b class='flag-5'>降低</b>电源纹波<b class='flag-5'>噪声</b>的方法与实例

    如何降低放大器噪声

    如何降低放大器噪声?  放大器噪声是实际电路中不可避免的。噪声会对放大器的性能产生负面影响,因此在电子设计中,降低噪声是一个非常重要的任务。
    的头像 发表于 09-19 16:50 1172次阅读

    降低MOSFET 1/f噪声的三种办法是什么?

    降低MOSFET 1/f噪声的三种办法是什么?  MOSFET是一种重要的半导体器件,广泛应用于各种电子设备中。然而,MOSFET存在着一种非常特殊的噪声,即1/f噪声。这种
    的头像 发表于 09-17 17:17 1727次阅读

    图像噪声基本知识介绍

    噪声对人的影响噪声可以理解为“ 妨碍人们感觉器官对所接收的信源信息理解的因素”。而图像中各种妨碍人们对其信息接受的因素即可称为图像噪声
    的头像 发表于 08-18 09:19 1557次阅读
    <b class='flag-5'>图像</b><b class='flag-5'>噪声</b>基本知识介绍

    图像噪声基本知识的介绍和应用

    图像噪声在数字图像处理技术中的重要性越来越明显,如高放大倍数航片的判读,X射线图像系统中的噪声去除等已经成为不可缺少的技术步骤。
    发表于 08-18 09:18 595次阅读
    <b class='flag-5'>图像</b><b class='flag-5'>噪声</b>基本知识的介绍和应用

    降低运动控制应用中可闻噪声的方法

    由于这些不同的产品和策略都可以降低运动控制应用中的可闻噪声,因此可能很难确定哪种策略更适合您的应用。在本文中,我将以 BLDC集成控制栅极驱动器为例,列出降低运动控制应用中可闻噪声的三
    的头像 发表于 07-17 11:39 822次阅读
    <b class='flag-5'>降低</b>运动控制应用中可闻<b class='flag-5'>噪声</b>的方法

    降低PCB设计中噪声与电磁干扰24条

    降低PCB设计中噪声与电磁干扰24条
    的头像 发表于 07-04 16:57 441次阅读