我正在使用 AVX2 优化视差估计算法的“赢家通吃”部分。我的标量例程是准确的,但在 QVGA 分辨率和 48 个视差下,运行时间在我的笔记本电脑上大约 14 毫秒,令人失望。我创建了 LR 和 RL 视差图像,但为了简单起见,我将只包含用于 RL 搜索的代码。
我的标量例程:
int MAXCOST = 32000;
for (int i = maskRadius; i < rstep-maskRadius; i++) {
// WTA "RL" Search:
for (int j = maskRadius; j+maskRadius < cstep; j++) {
int minCost = MAXCOST;
int minDisp = 0;
for (int d = 0; d < numDisp && j+d < cstep; d++) {
if (asPtr[(i*numDisp*cstep)+(d*cstep)+j] < minCost) {
minCost = asPtr[(i*numDisp*cstep)+(d*cstep)+j];
minDisp = d;
}
}
dRPtr[(i*cstep)+j] = minDisp;
}
}
我尝试使用 AVX2:
int MAXCOST = 32000;
int* dispVals = (int*) _mm_malloc( sizeof(int32_t)*16, 32 );
for (int i = maskRadius; i < rstep-maskRadius; i++) {
// WTA "RL" Search AVX2:
for( int j = 0; j < cstep-16; j+=16) {
__m256i minCosts = _mm256_set1_epi16( MAXCOST );
__m128i loMask = _mm_setzero_si128();
__m128i hiMask = _mm_setzero_si128();
for (int d = 0; d < numDisp && j+d < cstep; d++) {
// Grab 16 costs to compare
__m256i costs = _mm256_loadu_si256((__m256i*) (asPtr[(i*numDisp*cstep)+(d*cstep)+j]));
// Get the new minimums
__m256i newMinCosts = _mm256_min_epu16( minCosts, costs );
// Compare new mins to old to build mask to store minDisps
__m256i mask = _mm256_cmpgt_epi16( minCosts, newMinCosts );
__m128i loMask = _mm256_extracti128_si256( mask, 0 );
__m128i hiMask = _mm256_extracti128_si256( mask, 1 );
// Sign extend to 32bits
__m256i loMask32 = _mm256_cvtepi16_epi32( loMask );
__m256i hiMask32 = _mm256_cvtepi16_epi32( hiMask );
__m256i currentDisp = _mm256_set1_epi32( d );
// store min disps with mask
_mm256_maskstore_epi32( dispVals, loMask32, currentDisp ); // RT error, why?
_mm256_maskstore_epi32( dispVals+8, hiMask32, currentDisp ); // RT error, why?
// Set minCosts to newMinCosts
minCosts = newMinCosts;
}
// Write the WTA minimums one-by-one to the RL disparity image
int index = (i*cstep)+j;
for( int k = 0; k < 16; k++ ) {
dRPtr[index+k] = dispVals[k];
}
}
}
_mm_free( dispVals );
视差空间图像 (DSI) 的大小为 HxWxD (320x240x48),我将其水平布局以便更好地访问内存,这样每一行的大小都是 WxD。
视差空间图像具有每像素匹配成本。这与一个简单的盒子过滤器聚合在一起,以制作另一张完全相同大小的图像,但成本总和超过了一个 3x3 或 5x5 的窗口。这种平滑使结果更加“稳健”。当我使用 asPtr 访问时,我正在索引这个聚合成本图像。
此外,为了节省不必要的计算,我一直在以掩码半径偏移的行开始和结束。这个掩码半径是我的人口普查掩码的半径。我可以做一些花哨的边界反射,但它更简单更快,只是为了不打扰这个边界的差异。这当然也适用于开始和结束列,但是当我强制我的整个算法只在列是 16 的倍数(例如 QVGA:320x240)的图像上运行时,在这里搞乱索引并不好,这样我就可以简单地索引并使用 SIMD 命中所有内容(无残留标量处理)。
此外,如果您认为我的代码一团糟,我鼓励您查看高度优化的 OpenCV 立体算法。我发现它们是不可能的,并且几乎没有使用它们。
我的代码编译但在运行时失败。我正在使用 VS 2012 Express Update 4。当我使用调试器运行时,我无法获得任何见解。我对使用内在函数相对较新,所以我不确定调试时应该看到哪些信息、寄存器数量、__m256i 变量是否应该可见等。
听从下面的评论建议,我通过使用更智能的索引将标量时间从 ~14 提高到 ~8。我的 CPU 是 i7-4980HQ,我在同一个文件的其他地方成功使用了 AVX2 内部函数。