VisionWorks快速入门--Immediate mode

VisionWorks快速入门(Immediate mode)

本教程演示如何通过用visionworks函数直接替换opencv函数,轻松地在计算机视觉应用程序中利用visionworks库。
好处是应用程序的性能得到了显著的提高(x2.5),而没有明显的代码复杂性。

准备工作

需要安装以下软件

  1. CMake2.8.12
  2. opencv2.4+
  3. VisionWorks 1.4+

基本问题和方法说明

我们使用视频去抖作为说明。视频去抖输入是一个抖动的视频,输出是一个稳定之后的视频。
视频稳定一个有点经典的算法是用来完成这项任务的。下面是一个简短的描述。
在执行过程中,算法保持了一个包含N个最新帧的队列 Q f Q_f Qf。每两个图像之间的Homography矩阵组成另一个队列Ah,长度为 2 ∗ ( N − 1 ) 2 *(N-1) 2(N1).

在这里插入图片描述在这里插入图片描述在这里插入图片描述下面说明一下步骤:

  1. 初始化 Q f Q_f Qf,包含0个frame,Ah中包含单位矩阵,i=0
  2. i= i+1,采集一张图片加入到 Q f Q_f Qf中,如果 Q f Q_f Qf中达到N了就删除最先压入的图像。
  3. 使用cv::Fast()从i-1帧中找特征点
  4. 使用特征点使用cv::calcOpticalFlowPyrLK()在当前帧上计算光流。
  5. 两帧图像中特征点计算出一个单应矩阵H(i)
  6. Push H(i)到Ah队列中,如果满了删除最早的
  7. 如果 i<4,这时图像还不够多,重复1,否则,通过多个单应矩相乘转换关系矩阵 T n T_n Tn,计算(i-3)帧和当前帧的变换关系。
    T1=H(i−5)⋅H(i−4)⋅H(i−3),
    T2=H(i−4)⋅H(i−3) ,
    T3=H(i−3),
    T4=E,
    T5=H−1(i−2),
    T6=(H(i−2)⋅H(i−1))−1,
    T7=(H(i−2)⋅H(i−1)⋅H(i))−1
    使用1维高斯过滤矩阵 T n T_n Tn,使用 HomographySmoother::getSmoothedHomography()函数,
    T = ∑ w n T n T=∑w_nT_n T=wnTn (n =1到7) , 其中 w i w_i wi是沿高斯曲线选择的高斯权重,使得帧最接近当前帧并且对结果矩阵的影响最大。
  8. 基于计算的矩阵T(cv::warpPerspective())将透视变换应用于第(i-3)帧。
  9. 在屏幕上显示第(i-3)帧并返回步骤1。
    这就是整个视频去抖的原理,主要在于特征匹配然后计算位移,再进行高斯滤波。

下图展示了代码的流水线操作流程,蓝色的表示数据,黑色的表示执行算法:

在这里插入图片描述

运行瓶颈分析

为了使从opencv到visionworks的转换更容易,我们在所谓的即时模式中使用这些函数。这意味着每个函数都会立即执行,结果马上就可以得到,类似于普通的opencv函数。另一种选择是所谓的图形模式,将在下一个教程中讨论。
让我们看看opencv程序的性能。

FastCorners : 1.053 ms
ColorConvert : 0.198 ms
BuildPyramid : 0.770 ms
OpticalFlow : 11.354 ms
FindHomography : 1.563 ms
WarpPerspective : 2.286 ms
TOTAL : 17.368 ms

这些每帧结果是在ntel i5-6500 CPU和GeForce GTX 650 GPU电脑上获得的。
显然, cv::calcOpticalFlowPyrLK()函数占用了大部分时间,因此它是我们优化的第一个目标,即用VisionWorks替代品替换它。为了简化数据转换,我们还替换了两个相关函数:cv::buildOpticalFlowPyramid() and cv::calcOpticalFlowPyrLK().

迁移步骤

包含6步:

  1. 包含VisionWorks库
  2. 添加VisionWorks类数据代码,并初始化它们
  3. 将函数的输入数据从opencv数据类型转换为visionworks数据类型。
  4. 使用相应的visionworks函数。
  5. 将函数输出数据从visionworks数据类型转换为opencv类型。
  6. 释放申请的objects。

以下各节详细描述了这些步骤。

1. 包含VisionWorks库

CMakeLists.txt添加添加三行代码

  • find_package(VisionWorks)
  • include_directories("${VisionWorks_INCLUDE_DIRS}")
  • target_link_libraries(vstab_demo ${VisionWorks_LIBRARIES})
    库文件 头文件,然后添加上链接目标。然后 就可以在程序中调用 VisionWorks函数了。
    类似地,我们也需要添加 NVXIO库,这个库包含工具和IO函数。例如,
    NVXIO_SAFE_CALL用于处理VisionWorks函数返回的错误代码。
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(vstab_demo)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
find_package(OpenCV REQUIRED core imgproc video highgui calib3d)
find_package(VisionWorks REQUIRED)
find_package(VisionWorks-NVXIO)
include_directories("${OpenCV_INCLUDE_DIRS}")
include_directories("${VisionWorks_INCLUDE_DIRS}")
include_directories("${VisionWorks-NVXIO_INCLUDE_DIRS}")
add_executable( vstab_demo main.cpp
  homography_smoother.cpp homography_smoother.hpp
  stabilizer.cpp stabilizer.hpp
  )
target_link_libraries(vstab_demo
  ${OpenCV_LIBS}
  ${VisionWorks_LIBRARIES}
  ${VisionWorks-NVXIO_LIBRARIES}
  )
2.添加VisionWorks类数据代码,并初始化它们

必要的函数

vxuGaussianPyramid(vx_context context,
                   vx_image input,
                   vx_pyramid gaussian
                   )

vxuOpticalFlowPyrLK(vx_context context,
                    vx_pyramid old_images,
                    vx_pyramid new_images,
                    vx_array old_points,
                    vx_array new_points_estimates,
                    vx_array new_points,
                    vx_enum termination,
                    vx_scalar epsilon,
                    vx_scalar num_iterations,
                    vx_scalar use_initial_estimate,
                    vx_size window_dimension
                    )

OpenCVStabilizer类中创建以下字段:

    vx_context context_;
    vx_image gray_frame_;
    vx_pyramid latest_pyr_;
    vx_pyramid current_pyr_;
    vx_array vx_points_;
    vx_array vx_corresponding_points_;

注意,vx_pyramid latest_pyr_ 和 vx_pyramid current_pyr_用于替换程序opencv版本中的相应字段,即std::vectorcv::Mat latest_pyr_ and std::vectorcv::Mat current_pyr_.
这些字段初始化在OpenCVStabilizer::init()中

    context_ = vxCreateContext();
    vx_image gray = nvx_cv::createVXImageFromCVMat(context_, gray_latest_frame_);
    NVXIO_CHECK_REFERENCE(gray);
    vx_size width = gray_latest_frame_.cols;
    vx_size height = gray_latest_frame_.rows;
    latest_pyr_ = vxCreatePyramid(context_,
                                  (vx_size)params_.num_pyramid_levels,
                                  VX_SCALE_PYRAMID_HALF,
                                  width,
                                  height,
                                  VX_DF_IMAGE_U8);
    NVXIO_CHECK_REFERENCE(latest_pyr_);
    NVXIO_SAFE_CALL(vxuGaussianPyramid(context_, gray, latest_pyr_));
    current_pyr_ = vxCreatePyramid(context_,
                                   (vx_size)params_.num_pyramid_levels,
                                   VX_SCALE_PYRAMID_HALF,
                                   width,
                                   height,
                                   VX_DF_IMAGE_U8);
    vxReleaseImage(&gray);
    vx_size capacity = 15000;
    vx_enum item_type = NVX_TYPE_KEYPOINTF;
    vx_points_ = vxCreateArray(context_, item_type, capacity);
    NVXIO_CHECK_REFERENCE(vx_points_);
    vx_corresponding_points_ = vxCreateArray(context_, item_type, capacity);
    NVXIO_CHECK_REFERENCE(vx_corresponding_points_);
3.将函数的输入数据从opencv数据类型转换为visionworks数据类型。

数据格式转换已经在《VisionWorks学习之 如何和Opencv交互使用》中有专门的介绍,但在这里,我们将简要介绍一些重要的要点,例如opencv和visionworks数据类型之间的差异、使用visionworks类型的特殊性、用户很容易产生的错误/假设。
众所周知,opencv库中的基本数据类型是cv:Mat。从名称上看,它用于矩阵处理。同一类型用于存储“普通”矩阵和图像(cv::Mat可以有多个通道来表示图像平面)。VisionWorks有单独的数据类型用于这些目的:vx_image和vx_matrix。有关cv::Mat或cv::Gpu::GpuMat与vx_image或vx_matrix之间的转换的信息,请参见:《VisionWorks学习之 如何和Opencv交互使用
我们的这个例子中相应的输入参数对应如下表

OpenCVVisionWorks
cv::Mat gray_current_frame_vx_image input
std::vector<cv::KeyPoint> key_points_vx_array old_points
float opt_flow_epsilonvx_scalar epsilon
int opt_flow_num_iterationsvx_scalar num_iterations
int opt_flow_use_initial_estimatevx_scalar use_initial_estimate
int opt_flow_win_sizevx_size window_dimension

我们已经提到了如何将cv::Mat转换为vx_image,所以让我们考虑数组的转换。由于cv::KeyPoint和nvx::nvx_keypointf_t不兼容,我们需要在循环中转换元素。注意, cv::calcOpticalFlowPyrLK()使用std::vector<cv::Point2f>, vxuOpticalFlowPyrLK()使用VX_TYPE_KEYPOINT。与opencv函数不同,对vxuOpticalFlowPyrLK()来说,输入数组点的tracking_status非常重要。因为我们有所有有效点,所以将状态设置为1。

    int points_count = key_points_.size();
    std::vector<nvx_keypointf_t> vx_keypoint;
    vx_keypoint.resize(points_count);
    for (int i = 0; i < points_count; i++)
    {
        vx_keypoint[i].x = key_points_[i].pt.x;
        vx_keypoint[i].y = key_points_[i].pt.y;
        vx_keypoint[i].tracking_status = 1;
    }

std::vector转化为vx_array还是比较方便的,因为他们有相同的数据类型。

 vxTruncateArray(vx_points_, 0);
 NVXIO_SAFE_CALL(vxAddArrayItems(vx_points_, (vx_size)points_count, (void *)&vx_keypoint[0], sizeof(vx_keypoint[0])));

其余的就是参数的转换了,float和int 转化为vx_scalar, int 转化为vx_size.

  vx_scalar s_opt_flow_epsilon = vxCreateScalar(context_, VX_TYPE_FLOAT32, &params_.opt_flow_epsilon);
    NVXIO_CHECK_REFERENCE(s_opt_flow_epsilon);
    vx_scalar s_opt_flow_num_iterations = vxCreateScalar(context_, VX_TYPE_UINT32, &params_.opt_flow_num_iterations);
    NVXIO_CHECK_REFERENCE(s_opt_flow_num_iterations);
    assert(params_.opt_flow_use_initial_estimate == 0);
    vx_bool use_initial_estimate = vx_false_e;
    vx_scalar s_opt_flow_use_initial_estimate = vxCreateScalar(context_, VX_TYPE_BOOL, &use_initial_estimate);
    NVXIO_CHECK_REFERENCE(s_opt_flow_use_initial_estimate);

请注意,必须将对应于值类型的枚举值传递给vxCreateScalar。在不匹配的情况下,这可能会导致很难找到的错误,例如,当我们需要32位整数,而传递32位浮点类型而不是32位整数。由于vx_size是size_t的包装,我们使用static_cast转换opt_flow_win_size,这个数是浮点而不是整数,是不确定的。

4. 使用相应的visionworks函数。
    NVX_TIMER(build_pyramid, "BuildPyramid");
    NVXIO_SAFE_CALL(vxuGaussianPyramid(context_,
                                 gray,
                                 current_pyr_
                        )
        );
    NVX_TIMEROFF(build_pyramid);
    NVX_TIMER(optical_flow, "OpticalFlow");
    NVXIO_SAFE_CALL(vxuOpticalFlowPyrLK(context_,
                                        latest_pyr_,
                                        current_pyr_,
                                        vx_points_,
                                        vx_points_,
                                        vx_corresponding_points_,
                                        VX_TERM_CRITERIA_BOTH,
                                        s_opt_flow_epsilon,
                                        s_opt_flow_num_iterations,
                                        s_opt_flow_use_initial_estimate,
                                        static_cast<vx_size>(params_.opt_flow_win_size)
                        )
        );
    NVX_TIMEROFF(optical_flow);
5. 将函数输出数据从visionworks数据类型转换为opencv类型。

从算法中可以清楚地看出,只需要corresponding_points_ and status_ arrays就可以进行进一步的运算。用vx_corresponding_points_ array填充它们:

   status_.clear();
    corresponding_points_.clear();
    vx_size stride = 0;
    vx_size num_items = 0;
    vxQueryArray(vx_corresponding_points_, VX_ARRAY_ATTRIBUTE_NUMITEMS, &num_items, sizeof(num_items));
    corresponding_points_.resize(num_items);
    status_.resize(num_items);
    void *base;
    vx_map_id map_id;
    NVXIO_SAFE_CALL(vxMapArrayRange(vx_corresponding_points_, 0, num_items, &map_id, &stride, (void**)&base, VX_READ_ONLY, VX_MEMORY_TYPE_HOST, 0));
    for (vx_size i = 0; i < num_items; i++)
    {
        nvx_keypointf_t cur_item = vxArrayItem(nvx_keypointf_t, base, i, stride);
        corresponding_points_[i].x = cur_item.x;
        corresponding_points_[i].y = cur_item.y;
        status_[i] = cur_item.tracking_status;
    }
    NVXIO_SAFE_CALL(vxUnmapArrayRange(vx_corresponding_points_, map_id));
6. 释放申请的objects。
    vxReleaseImage(&gray);
    vxReleaseScalar(&s_opt_flow_epsilon);
    vxReleaseScalar(&s_opt_flow_num_iterations);
    vxReleaseScalar(&s_opt_flow_use_initial_estimate);

总结

下面来看一下处理性能
opencv版本:

FastCorners : 1.047 ms
ColorConvert : 0.201 ms
BuildPyramid : 0.779 ms
OpticalFlow : 11.407 ms
FindHomography : 1.556 ms
WarpPerspective : 2.293 ms
TOTAL : 17.397 ms
block for change with OpenCV: 12.194 ms

VisionWorks version results (replaces BuildPyramid and OpticalFlow):

FastCorners : 1.194 ms
ColorConvert : 0.215 ms
BuildPyramid : 0.307 ms
OpticalFlow : 4.098 ms
FindHomography : 2.608 ms
WarpPerspective : 2.978 ms
TOTAL : 11.899 ms
changed block with Visionworks : 4.505 ms

VisionWorks version results (replaces all OpenCV functions):

FastCorners : 0.802 ms
ColorConvert : 0.124 ms
BuildPyramid : 0.131 ms
OpticalFlow : 4.411 ms
FindHomography : 1.249 ms
WarpPerspective : 0.115 ms
TOTAL : 7.170 ms

与OpenCV版本相比,这给出了大约2.5X的加速。

未完。。。。。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值