【问题标题】:Border check with neon与霓虹灯的边境检查
【发布时间】:2015-04-15 10:56:20
【问题描述】:

参考我之前关于边境检查条件的问题 - Border check in image processing? 我正在用霓虹灯编写代码进行边界检查。编写代码时遇到以下问题:

输入:

 --------------------------------
|221 220 228 223 230 233 234 235 ..
|71  73  70  78  92  130 141 143 ..
|

要求:

  -1 * v_m1_m1 + 0 * v_m1_0 + 1 * v_m1_p1
  -1 * v_0_m1  + 0 * v_0_0  + 1 * v_0_p1    --> v_out
  -1 * v_p1_m1 + 0 * v_p1_0 + 1 * v_p1_p1

伪代码:

for i = 0 to nrows - 1
        // init row pointers
        p_row_m1 = src + src_width * MAX(i-1, 0);           // pointing to minus1 row
        p_row_0  = src + src_width * i;                     // pointing to current row
        p_row_p1 = src + src_width * MIN(i+1, src_width-1); // pointing to plus1 row

        v_m1_m1 = vdupq_n_u32(p_row_m1[0]);   // fill left vector from src[i-1][0]
        v_0_m1  = vdupq_n_u32(p_row_0[0]);    // fill left vector from src[i][0]
        v_p1_m1 = vdupq_n_u32(p_row_p1[0]);   // fill left vector from src[i+1][0]

        v_m1_0 = vld1q_u32(&p_row_m1[0]);   // load center vector from src[i-1][0..7]
        v_0_0  = vld1q_u32(&p_row_0[0]);    // load center vector from src[i][0..7]
        v_p1_0 = vld1q_u32(&p_row_p1[0]);   // load center vector from src[i+1][0..7]

        for j = 0 to (ncols - 4) step 4         // assuming 4 elements per SIMD vector

            v_m1_p1  = vld1q_u32(&p_row_m1[j+4]);   // load right vector from src[i-1][0..7]
            v_0_p1   = vld1q_u32(&p_row_0[j+4]);    // load right vector from src[i][0..7]
            v_p1_p1  = vld1q_u32(&p_row_p1[j+4]);   // load right vector from src[i+1][0..7]
    //
    // you now have a 3x3 arrangement of vectors on which
    // you can perform a neighbourhood operation and generate
    // 16 output pixels for the current iteration:
    //
    //    v_m1_m1  v_m1_0  v_m1_p1
    //    v_0_m1   v_0_0   v_0_p1
    //    v_p1_m1  v_p1_0  v_p1_p1
    //
    //               |
    //               V
    //
    //              v_out
    vst1q_s32(v_out, &image_out[i][j])      // store output vector at image_out[i][j..j+15]
    // shuffle vectors so that we can use them on next iteration
    v_m1_m1 = v_m1_0
    v_m1_0  = v_m1_p1

    v_0_m1  = v_0_0 
    v_0_0   = v_0_p1

    v_p1_m1 = v_p1_0
    v_p1_0  = v_p1_p1

      end_for
      // for final iteration we need to handle right edge pixels...
      v_m1_p1 = vdupq_n_u32(p_row_m1[ncols-1])     // fill right vector from image[i-1][ncols-1]
      v_0_p1  = vdupq_n_u32(p_row_0[ncols-1])       // fill right vector from image[i][ncols-1]
      v_p1_p1 = vdupq_n_u32(p_row_p1[ncols-1])     // fill right vector from image[i+1][ncols-1]
      // calculate v_out as above
      vst1q_s32(v_out, &image_out[i][j])        // store output vector at image_out[i][ncols_16..ncols-1]
    end_for

在 C 代码中,如果我想计算 221 (@i,j =0,0) ,边框看起来像:

 221  221 220
      -----------
 221 |221 220
     |
 71  |71  73

在 NEON 代码中,如果我想计算 221 (@i,j =0,0) ,边框看起来像:

221 221 221 221     221 220 228 223   230 233 234 235
                    -------------------------------
221 221 221 221    |221 220 228 223   230 233 234 235
                   |
71  71  71  71     |71  73  70  78    92  130 141 143

对于计算 221: 在霓虹灯代码中,邻居如下所示,根据要求它出错了:

v_m1_m1.0  v_m1_0.0  v_m1_p1.0
v_0_m1.0   v_0_0.0   v_0_p1.0
v_p1_m1.0  v_p1_0.0  v_p1_p1.0

221  221  230
     ---
221 |221| 230
     ---
71   71   92

如何使用通用代码逻辑处理边框??

【问题讨论】:

  • 不清楚您的问题是什么 - 九个 v_xxx 向量包含 3x3 邻域操作所需的所有数据 - 也许您因为相邻向量中的冗余元素而感到困惑?如果是这样,您可以忽略这些,当您向左/向右移动向量对时,您将只使用一个元素。
  • @PaulR,我希望 221 的右侧邻居应该是 220 而不是 230。对于 228 具有相同的霓虹灯代码,邻居是 221(左),234(右)。并且按照 228 的 C 代码,邻居应该是 220(左),223(右)。我是否解释得足够清楚我的问题 // v_m1_m1 v_m1_0 v_m1_p1 // v_0_m1 v_0_0 v_0_p1 // v_p1_m1 v_p1_0 v_p1_p1
  • @PaulR,它将在第一次迭代中正常工作:v_0_m1: 221 221 221 221 ||| v_0_0:221 220 228 223 ||| v_0_p1 : 220 228 223 230 对于第二次迭代,我必须将宽度(或 j)循环增加 1 而不是 4,这将破坏矢量化的重要性。
  • 您需要将 j 循环增加 4,因为您每次迭代处理 4 个输出元素 - 这就是 SIMD 的重点。
  • 另外,重要的一点:对于水平邻域运算,您需要使用vextq将一对向量左移或右移。

标签: arm simd neon


【解决方案1】:

看来您需要了解如何使用邻域运算来实现 SIMD 内核。这是重要的部分:

//
// you now have a 3x3 arrangement of vectors on which
// you can perform a neighbourhood operation and generate
// 4 output pixels for the current iteration:
//
//    v_m1_m1  v_m1_0  v_m1_p1
//    v_0_m1   v_0_0   v_0_p1
//    v_p1_m1  v_p1_0  v_p1_p1
//
//               |
//               V
//
//              v_out

假设你想在一个十字形内核上实现求和,即:

y[i][j] = x[i][j] + x[i-1][j] + x[i+1][j] + x[i][j-1] + x[i][j+1];

SIMD 中的伪代码是:

// sum vertically: x[i][j] + x[i-1][j] + x[i+1][j]
v_out = v_m1_0;
v_out = v_out + v_0_0;      // vaddq
v_out = v_out + v_p1_0;     // vaddq

// add the x[i][j-1] components
v_temp = v_0_m1:v_0_0 >> 1; // vextq - use this to get a right-shifted vector
v_out = v_out + v_temp;     // vaddq

// add the x[i][j+1] components
v_temp = v_0_0:v_0_p1 << 1; // vextq - use this to get a left-shifted vector
v_out = v_out + v_temp;     // vaddq

此时v_out 现在包含输出元素y[i][j]..y[i][j+3] 的四个总和。换句话说,我们在一个内核中评估了四个输出点。现在我们将所有向量左移 1,并为右侧列加载 3 个新向量,然后使用 j += 4 重新执行此操作。如果您查看上一个问题的原始伪代码,您会发现边界情况都是通过用边缘值填充向量来处理的。

【讨论】:

    猜你喜欢
    • 2015-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多