c++ - Halide 在归一化互相关期间挂起

标签 c++ halide

我正在尝试在 Halide 中实现归一化互相关。

下面的代码构建,Halide JIT 编译不会抛出任何错误。但是,Halide 似乎在 JIT 编译后挂起。无论我对不同的 Func 调用了多少次 trace_*,都只会打印一条跟踪(在 Func 输出 上):

Begin realization normxcorr.0(0, 2028, 0, 2028)
Produce normxcorr.0(0, 2028, 0, 2028)

任何建议都会有所帮助。

这个算法相当于CV_TM_CCOEFF_NORMED in OpenCV , 和 normxcorr2 in MATLAB :

void normxcorr( Halide::ImageParam input,
                Halide::ImageParam kernel,
                Halide::Param<pixel_t> kernel_mean,
                Halide::Param<pixel_t> kernel_var,
                Halide::Func& output )
{
    Halide::Var x, y;
    Halide::RDom rk( kernel );

    // reduction domain for cumulative sums
    Halide::RDom ri( 1, input.width() - kernel.width() - 1, 
                     1, input.height() - kernel.height() - 1 );

    Halide::Func input_32( "input32" ),
             bounded_input( "bounded_input"),
             kernel_32( "kernel32" ),
             knorm( "knorm" ),
             conv( "conv" ),
             normxcorr( "normxcorr_internal" ),
             sq_sum_x( "sq_sum_x" ),
             sq_sum_x_local( "sq_sum_x_local" ),
             sq_sum_y( "sq_sum_y" ),
             sq_sum_y_local( "sq_sum_y_local" ),
             sum_x( "sum_x" ),
             sum_x_local( "sum_x_local" ),
             sum_y( "sum_y" ),
             sum_y_local( "sum_y_local" ),
             win_var( "win_var" ),
             win_mean( "win_mean" );

    Halide::Expr ksize = kernel.width() * kernel.height();

    // accessing outside the input image always returns 0
    bounded_input( x, y ) = Halide::BoundaryConditions::constant_exterior( input, 0 )( x, y );

    // cast to 32-bit to make room for multiplication
    input_32( x, y ) = Halide::cast<int32_t>( bounded_input( x, y ) );
    kernel_32( x, y ) = Halide::cast<int32_t>( kernel( x, y ) );

    // cumulative sum along each row
    sum_x( x, y ) = input_32( x, y );
    sum_x( ri.x, ri.y ) += sum_x( ri.x - 1, ri.y );

    // sum of 1 x W strips
    // (W is the width of the kernel)
    sum_x_local( x, y ) = sum_x( x + kernel.width() - 1, y );
    sum_x_local( x, y ) -= sum_x( x - 1, y );

    // cumulative sums of the 1 x W strips along each column
    sum_y( x, y ) = sum_x_local( x, y );
    sum_y( ri.x, ri.y ) += sum_y( ri.x, ri.y - 1);

    // sums up H strips (as above) to get the sum of an H x W rectangle
    // (H is the height of the kernel)
    sum_y_local( x, y ) = sum_y( x, y + kernel.height() - 1 );
    sum_y_local( x, y ) -= sum_y( x, y - 1 );

    // same as above, just with squared image values
    sq_sum_x( x, y ) = input_32( x, y ) * input_32( x, y );
    sq_sum_x( ri.x, ri.y ) += sq_sum_x( ri.x - 1, ri.y );

    sq_sum_x_local( x, y ) = sq_sum_x( x + kernel.width() - 1, y );
    sq_sum_x_local( x, y ) -= sq_sum_x( x - 1, y );

    sq_sum_y( x, y ) = sq_sum_x_local( x, y );
    sq_sum_y( ri.x, ri.y ) += sq_sum_y( ri.x, ri.y - 1);

    sq_sum_y_local( x, y ) = sq_sum_y( x, y + kernel.height() - 1 );
    sq_sum_y_local( x, y ) -= sq_sum_y( x, y - 1 );

    // the mean value of each window
    win_mean( x, y ) = sum_y_local( x, y ) / ksize;

    // the variance of each window
    win_var( x, y ) =  sq_sum_y_local( x, y ) / ksize;
    win_var( x, y) -= win_mean( x, y ) * win_mean( x, y );

    // partially normalize the kernel
    // (we'll divide by std. dev. at the end)
    knorm( x, y ) = kernel_32( x, y ) - kernel_mean;

    // convolve kernel and the input
    conv( x, y ) = Halide::sum( knorm( rk.x, rk.y ) * input_32( x + rk.x, y + rk.y ) );

    // calculate normxcorr, except scaled to 0 to 254 (for an 8-bit image)
    normxcorr( x, y ) = conv( x, y ) * 127 / Halide::sqrt( kernel_var * win_var( x, y ) ) + 127;

    // after scaling pixel values, it's safe to cast down to 8-bit
    output( x, y ) = Halide::cast<pixel_t>( normxcorr( x, y ) );
}

最佳答案

我认为这里的问题很简单,就是您没有为您的任何函数指定任何时间表,所以一切都被内联,导致中间值的大量冗余计算。所以实际上这在技术上并不是挂起,而是简单地为每个像素做了大量的工作,因此未能在合理的时间内完成。

首先,尝试说每个函数都应该是 compute_root(例如,sum_x.compute_root();),最简单的方法是在函数末尾的一个 block 中功能。这应该以更合理的速度进行,应该一个接一个地打印每个函数(从输入开始),而不仅仅是 normxcore.0,并且应该完成。

实际上,您的许多函数实际上只是对其输入进行逐点转换,因此这些输入可以保留为内联(而不是 compute_root),这应该会进一步加快处理速度(尤其是在您开始并行化后并对某些阶段进行矢量化)。乍一看,[sq_]sum_{x,y} 可能不应该内联,但其他所有内容都可以内联。 knorminput_32 是一个折腾,否则取决于你的目标和你的日程安排。

我添加了这个简单的时间表和一些其他小的清理,在此处进行了快速可运行的修订:

https://gist.github.com/d64823d754a732106a60

在我的测试中,它在一秒内以 2K^2 输入运行,没有任何花哨的东西。

顺便说一句,一个小提示:使用调试符号 (-g) 编译生成器代码应该使您不必在所有 Func 声明中提供名称字符串。这在早期的实现中是一个不幸的缺陷,但我们现在能够合理地直接从 C++ 源符号名称设置这些名称,只要您在启用调试符号的情况下进行编译。

关于c++ - Halide 在归一化互相关期间挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31060974/

相关文章:

opencv - 用 Halide Funcs 替换 Opencv Fillpoly 函数

c++ - Halide 中的 Cholesky 分解

c++ - 设置从函数返回的 Func 中的输入

c++ - C++ 中的 typedef 变量

c++ - 游戏寻路算法

C++ 数组到 Halide Image(和返回)

c++ - 从 RGB 到 YUV 的颜色转换 (YCoCg)

c++ - 动态类型定义

c++ - CUDA 探查器 : Calculate memory and compute utilization

c++ - 在 SAS 中使用带有 PROC PROTO 的 linux c++ 库