带霓虹灯的边境检查
Border check with neon
参考我之前关于边界检查条件的问题 -
Border check in image processing?
我正在用霓虹灯为边框编写代码 check.I 在编写代码时遇到以下问题:
输入:
--------------------------------
|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
如何使用通用代码逻辑处理边框??
看来您需要了解如何使用邻域操作实现 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]
的四个总和。换句话说,我们在一个内核中评估了四个输出点。现在我们将所有向量向左打乱,并为右侧列加载 3 个新向量,然后用 j += 4
重新做一遍。如果您查看上一个问题的原始伪代码,您会发现边界情况都是通过用边缘值填充向量来处理的。
参考我之前关于边界检查条件的问题 - Border check in image processing? 我正在用霓虹灯为边框编写代码 check.I 在编写代码时遇到以下问题:
输入:
--------------------------------
|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
如何使用通用代码逻辑处理边框??
看来您需要了解如何使用邻域操作实现 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]
的四个总和。换句话说,我们在一个内核中评估了四个输出点。现在我们将所有向量向左打乱,并为右侧列加载 3 个新向量,然后用 j += 4
重新做一遍。如果您查看上一个问题的原始伪代码,您会发现边界情况都是通过用边缘值填充向量来处理的。