VisionWorks快速入门--Graph Mode


本教程的第一部分(VisionWorks快速启动(立即模式))展示了如何使用优化的VisionWorks立即模式函数替换几个耗时的OpenCV函数,从而提高video stabilization应用程序的性能。visionworks库实现openvx标准,还可以使用图形模式执行来使应用程序更快。

本教程的第二部分与第一部分一样基于“video stabilization”示例。算法和完整的代码包含在第一部分中;您还可以在其中找到visionworks即时模式的完整代码。本教程的第二部分将修改此代码,例如,与数据转换相关的代码行。本教程重点介绍立即模式和图形模式之间的区别。

原语可以通过两种方式执行:

基于graph的执行原语被实例化为graph节点。graph是提前构建、验证和优化的,并且可以在运行时多次执行而无需重新验证。基于graph的执行对于多次执行的视觉管道(例如处理视频流)是首选的,因为它提供了最佳的性能。
立即执行原语通过调用函数(前缀为vxu或nvxu)直接执行,类似于OpenCV或NPP执行模式。当基本设置开销不是一个大问题时,立即执行模型对于一次性处理非常有用。它也可以作为应用程序开发的中间步骤,比如移植使用opencv的应用程序时。

本节讨论这些模式之间的对应关系。每个立即模式功能都有相应的节点,例如:

Immediate modeGraph mode
vxuFastCorners()vxFastCornersNode()
nvxuFindHomography()nvxFindHomographyNode()

每个节点被附加到一个graph中,然后验证graph。每次算法迭代只调用vxProcessGraph()函数,即代码的很大一部分从负责算法执行的函数转换到负责初始化的函数。函数后面都加一个“node”

让我们看看本例子如何操作。

从立即模式过渡到图形模式

在每次迭代中按顺序调用以下函数:

vxuFastCorners();
vxuColorConvert();
vxuGaussianPyramid();
vxuOpticalFlowPyrLK();
nvxuFindHomography();
homography_smoother_->push();
homography_smoother_->getSmoothedHomography();
vxuWarpPerspective();

上面所有VISIONWORKS函数都存在对应的节点,但是两个用户函数在库中没有对应的节点。不可能画出一个有“洞”的图;它必须包含一个算法的连续部分。解决这个问题有两种方法:

  • 将算法分成两部分,由用户代码分隔。因此,第一部分包含与前五个函数相对应的节点,而第二个图仅包含一个节点(或者不创建单节点图并保留vxuWarpPerspective());

  • 实现用户节点(详细教程位于
    VisionWorks之用户自定义结点),所有中间代码都隐藏在其中。您可以创建一个包含所有必要节点的图。

我们选择第二个是因为它保持了设计的纯洁性和代码的可读性。

在转换描述之前,请注意一种VisionWorks数据类型。库中有一种特殊的数据类型,名为vx_delay。vx_delay是一个循环缓冲区。我们使用它来存储smoothing_window_size_ + 1个最近的图像、2 * smoothing_window_size_ + 1个单应矩阵、最新的 2个 grayscale frame和 最新的2个 金字塔。如您所见,使用此数据类型存储有关最后几帧的信息非常方便。

让我们将从立即模式到图形模式的转换划分为以下步骤:

  1. 创建新节点。

  2. 向GraphModestabilizer类添加新字段和函数。

  3. 初始化字段。

  4. 执行算法的迭代。

  5. Release objects。

1. 创建新节点。

我们必须将所有位于nvxuFindHomography()和vxuWarpPerspective()函数之间的代码包装到用户节点我们把它命名为homographySmootherNode。

新节点必须执行以下操作:

  • 将新的单应矩阵添加到(2 * smoothing_window_size_)矩阵的循环缓冲区中;

  • 对单应矩阵应用了更平滑的单应运算稳定映射。

由于节点不应该存储其状态,因此它必须采用高斯权重数组(也可以在每次迭代中计算)、单应矩阵缓冲区和新的单应矩阵。转换矩阵作为输出参数传递给节点。

代码在文件homography_smooler_node.hpp和homography_smooler_node.cpp中。

2. 向GraphModestabilizer类添加新字段和函数。

有些字段必须删除,有些要替换,有些要添加。特别是,不使用“宽度”和“高度”字段。它们只是创建图像所必需的,这些图像被添加到帧队列中。现在我们使用vx_delay代替std::queue,并且这种数据类型保存有关帧大小的信息。

替换字段有:

Immediate mode stabilizerGraph mode stabilizer
std::gueue<vx_image> framesvx_delay frames_delay_
vx_image gray_latest_frame_, vx_image gray_current_frame_vx_delay gray_frames_delay_
vx_pyramid latest_pyr_, vx_pyramid current_pyr_vx_delay pyr_delay_

仍然需要为HomographySmoother节点添加字段:

    vx_array gaussian_weights_;
    vx_delay homography_matrices_;

图的字段,即图及其节点:

    vx_graph main_graph_;
    vx_node fast_corners_node_;
    vx_node color_convert_node_;
    vx_node gaussian_pyramid_node_;
    vx_node opt_flow_node_;
    vx_node find_homography_node_;
    vx_node homography_smoother_node_;
    vx_node warp_perspective_node_;

我们添加了参数初始化和图形创建功能。

    void createMainGraph();
    vx_status initGaussianWeights();
    vx_status initHomographyMatrices();

3. 初始化字段。

与立即模式相比,有哪些初始化要做呢?新字段出现在类中,一些旧字段已更改,因此我们必须初始化它们。类字段之间有几个vx_delay对象。我们必须传递实例(例如,如果是图像的vx_delay,我们必须传递vx_image)和缓冲区大小来初始化vx_delay。vx_delay从delay-s元素读取元数据(图像大小和类型)。例如,frames_delay_ 初始化:

   frames_delay_ = vxCreateDelay(context_, (vx_reference)start_frame, params_.smoothing_window_size + 1);
    NVXIO_CHECK_REFERENCE(frames_delay_);
    NVXIO_SAFE_CALL(nvxuCopyImage(context_, start_frame, (vx_image)vxGetReferenceFromDelay(frames_delay_, 0)));

在initGaussianWeights()函数中初始化高斯权重数组:

vx_status GraphModeStabilizer::initGaussianWeights()
{
    vx_status status = VX_SUCCESS;
    vx_float32 sigma = (vx_float32)params_.smoothing_window_size * 0.7;
    vx_int32 num_items = 2 * (vx_int32)params_.smoothing_window_size + 1;
    std::vector<vx_float32> gaussian_weights_data;
    gaussian_weights_data.resize(num_items);
    vx_float32 sum = 0;
    for (vx_int32 i = 0; i < num_items; ++i)
    {
        gaussian_weights_data[i] = exp(-(i - (vx_float32)params_.smoothing_window_size) * (i - (vx_float32)params_.smoothing_window_size) / (2.f * sigma * sigma));
        sum += gaussian_weights_data[i];
    }
    //normalize weights
    assert((sum > 0.00000000001) || (sum < - 0.00000000001));
    vx_float32 scaler = 1.f / sum;
    for (vx_int32 i = 0; i < num_items; i++)
    {
        gaussian_weights_data[i] *= scaler;
    }
    status |= vxAddArrayItems(gaussian_weights_, (vx_size)num_items, (void *)&gaussian_weights_data[0], sizeof(gaussian_weights_data[0]));
    return status;
}

在initHomographyMatrices()函数中,用身份矩阵初始化单应矩阵的vx_delay:

vx_status GraphModeStabilizer::initHomographyMatrices()
{
    vx_status status = VX_SUCCESS;
    vx_float32 homography_data[3][3] = { {1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f} };
    for (vx_size i = 0; i < 2 * params_.smoothing_window_size; i++)
    {
        status |= vxCopyMatrix((vx_matrix)vxGetReferenceFromDelay(homography_matrices_, -i), homography_data, VX_WRITE_ONLY, VX_MEMORY_TYPE_HOST);
    }
    return status;
}

graph的初始化:

void GraphModeStabilizer::createMainGraph()
{
    main_graph_ = vxCreateGraph(context_);
    fast_corners_node_ = vxFastCornersNode(main_graph_,
                                           (vx_image)vxGetReferenceFromDelay(gray_frames_delay_, -1),
                                           params_.s_fast_threshold,
                                           vx_true_e,
                                           points_,
                                           0);
    color_convert_node_ = vxColorConvertNode( main_graph_,
                                              (vx_image)vxGetReferenceFromDelay(frames_delay_, 0),
                                              (vx_image)vxGetReferenceFromDelay(gray_frames_delay_, 0));
    gaussian_pyramid_node_ = vxGaussianPyramidNode(main_graph_,
                                                   (vx_image)vxGetReferenceFromDelay(gray_frames_delay_, 0),
                                                   (vx_pyramid)vxGetReferenceFromDelay(pyr_delay_, 0));
    opt_flow_node_ = vxOpticalFlowPyrLKNode(main_graph_,
                                            (vx_pyramid)vxGetReferenceFromDelay(pyr_delay_, -1),
                                            (vx_pyramid)vxGetReferenceFromDelay(pyr_delay_, 0),
                                            points_,
                                            points_,
                                            corresponding_points_,
                                            VX_TERM_CRITERIA_BOTH,
                                            params_.s_opt_flow_epsilon,
                                            params_.s_opt_flow_num_iterations,
                                            params_.s_opt_flow_use_initial_estimate,
                                            params_.opt_flow_win_size);
    find_homography_node_ = nvxFindHomographyNode(main_graph_,points_,
                                                  corresponding_points_,
                                                  (vx_matrix)vxGetReferenceFromDelay(homography_matrices_, 0),
                                                  params_.homography_method,
                                                  params_.homography_ransac_threshold,
                                                  params_.homography_max_estimate_iters,
                                                  params_.homography_max_refine_iters,
                                                  params_.homography_confidence,
                                                  params_.homography_outlier_ratio,
                                                  NULL);
    registerHomographySmootherKernel(context_);
    homography_smoother_node_ = homographySmootherNode(main_graph_,
                                               gaussian_weights_,
                                               homography_matrices_,
                                               perspective_matrix_);
    warp_perspective_node_ = vxWarpPerspectiveNode(main_graph_,
                                                   (vx_image)vxGetReferenceFromDelay(frames_delay_, -params_.smoothing_window_size),
                                                   perspective_matrix_,
                                                   VX_INTERPOLATION_TYPE_BILINEAR,
                                                   stabilized_frame_);
    //
    // Graph verification
    // Note: This verification is mandatory prior to graph execution
    //
    NVXIO_SAFE_CALL(vxVerifyGraph(main_graph_));
}

4. 执行算法的迭代。

由于大多数算法都转换为图形创建,因此每个步骤只有4个操作:

  • 将当前帧添加到帧的循环缓冲区中;

  • 执行图形;

  • 缓冲区移动;

  • 返回稳定帧。

以下代码中显示了此操作序列

    NVXIO_SAFE_CALL(vxAgeDelay(homography_matrices_));
    NVXIO_SAFE_CALL(vxAgeDelay(pyr_delay_));
    NVXIO_SAFE_CALL(vxAgeDelay(gray_frames_delay_));
    NVXIO_SAFE_CALL(vxAgeDelay(frames_delay_));
    NVXIO_SAFE_CALL(nvxuCopyImage(context_, current_frame, (vx_image)vxGetReferenceFromDelay(frames_delay_, 0)));
    NVXIO_SAFE_CALL(vxProcessGraph(main_graph_));
    return stabilized_frame_;

5. Release objects。

GraphModeStabilizer::~GraphModeStabilizer()
{
    vxReleaseArray(&points_);
    vxReleaseArray(&corresponding_points_);
    vxReleaseArray(&gaussian_weights_);
    vxReleaseDelay(&homography_matrices_);
    vxReleaseMatrix(&perspective_matrix_);
    vxReleaseImage(&stabilized_frame_);
    vxReleaseDelay(&gray_frames_delay_);
    vxReleaseDelay(&pyr_delay_);
    vxReleaseDelay(&frames_delay_);
    vxReleaseGraph(&main_graph_);
};

结果

FastCorners : 0.544 ms
ColorConvert : 0.030 ms
BuildPyramid : 0.090 ms
OpticalFlow : 4.435 ms
FindHomography : 1.295 ms
HomographySmoother : 0.019 ms
WarpPerspective : 0.146 ms
TOTAL : 6.766 ms

这个版本比以前快了5%,立即模式和graph模式的性能优化主要在于,graph模式不需要在执行过程中对内存分配以及内存的复制。

从开发流程上来说最好是先使用opencv或者立即模式开发算法流程,测试处理流程没问题了然后再移植到graph模式下,最大程度的优化性能。这个处理算法不算复杂,所以性能提供有限,对于一些节点可以并行执行的更复杂算法,可以获得更大的性能增益。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值