OpenCV FAST角点检测SSE实现演练

时间:2015-06-17 03:30:40

标签: c performance opencv optimization sse

有人可以帮我理解OpenCV中FAST角点检测的SSE实现吗?我理解算法而不是实现。有人可以引导我完成代码吗?

代码很长,所以提前谢谢你。

我正在使用OpenCV 2.4.11,代码如下:

__m128i delta = _mm_set1_epi8(-128);
__m128i t = _mm_set1_epi8((char)threshold);
__m128i m0, m1;
__m128i v0 = _mm_loadu_si128((const __m128i*)ptr);

我认为以下内容与阈值检查有关,但无法理解 delta 的使用

__m128i v1 = _mm_xor_si128(_mm_subs_epu8(v0, t), delta);
v0 = _mm_xor_si128(_mm_adds_epu8(v0, t), delta);

现在它会检查相邻的4个像素,但是, delta 的用途是什么?

__m128i x0 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[0])), delta);
__m128i x1 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[4])), delta);
__m128i x2 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[8])), delta);
__m128i x3 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[12])), delta);
m0 = _mm_and_si128(_mm_cmpgt_epi8(x0, v0), _mm_cmpgt_epi8(x1, v0));
m1 = _mm_and_si128(_mm_cmpgt_epi8(v1, x0), _mm_cmpgt_epi8(v1, x1));
m0 = _mm_or_si128(m0, _mm_and_si128(_mm_cmpgt_epi8(x1, v0), _mm_cmpgt_epi8(x2, v0)));
m1 = _mm_or_si128(m1, _mm_and_si128(_mm_cmpgt_epi8(v1, x1), _mm_cmpgt_epi8(v1, x2)));
m0 = _mm_or_si128(m0, _mm_and_si128(_mm_cmpgt_epi8(x2, v0), _mm_cmpgt_epi8(x3, v0)));
m1 = _mm_or_si128(m1, _mm_and_si128(_mm_cmpgt_epi8(v1, x2), _mm_cmpgt_epi8(v1, x3)));
m0 = _mm_or_si128(m0, _mm_and_si128(_mm_cmpgt_epi8(x3, v0), _mm_cmpgt_epi8(x0, v0)));
m1 = _mm_or_si128(m1, _mm_and_si128(_mm_cmpgt_epi8(v1, x3), _mm_cmpgt_epi8(v1, x0)));
m0 = _mm_or_si128(m0, m1);

这里检查相邻像素的连续性。 (右?)

int mask = _mm_movemask_epi8(m0);
if( mask == 0 )
    continue;

这对我来说是另一个难题。为什么要向左移8个字节?我假设掩码告诉我候选角的位置,但为什么是8个字节?

if( (mask & 255) == 0 )
{
    j -= 8;
    ptr -= 8;
    continue;
}

此时我放弃了......

__m128i c0 = _mm_setzero_si128(), c1 = c0, max0 = c0, max1 = c0;
for( k = 0; k < N; k++ )
{
    __m128i x = _mm_xor_si128(_mm_loadu_si128((const __m128i*)(ptr + pixel[k])), delta);
    m0 = _mm_cmpgt_epi8(x, v0);
    m1 = _mm_cmpgt_epi8(v1, x);

    c0 = _mm_and_si128(_mm_sub_epi8(c0, m0), m0);
    c1 = _mm_and_si128(_mm_sub_epi8(c1, m1), m1);

    max0 = _mm_max_epu8(max0, c0);
    max1 = _mm_max_epu8(max1, c1);
}

max0 = _mm_max_epu8(max0, max1);
int m = _mm_movemask_epi8(_mm_cmpgt_epi8(max0, K16));

for( k = 0; m > 0 && k < 16; k++, m >>= 1 )
    if(m & 1)
    {
        cornerpos[ncorners++] = j+k;
        if(nonmax_suppression)
            curr[j+k] = (uchar)cornerScore<patternSize>(ptr+k, pixel, threshold);
    }

2 个答案:

答案 0 :(得分:3)

正如哈罗德所说,delta用于进行无符号比较。

让我们按步骤描述这种实现:

  1.   

    __m128i x0 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[0])), delta); __m128i x1 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[4])), delta); __m128i x2 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[8])), delta); __m128i x3 = _mm_sub_epi8(_mm_loadu_si128((const __m128i*)(ptr + pixel[12])), delta); m0 = _mm_and_si128(_mm_cmpgt_epi8(x0, v0), _mm_cmpgt_epi8(x1, v0)); m1 = _mm_and_si128(_mm_cmpgt_epi8(v1, x0), _mm_cmpgt_epi8(v1, x1)); m0 = _mm_or_si128(m0, _mm_and_si128(_mm_cmpgt_epi8(x1, v0), _mm_cmpgt_epi8(x2, v0))); ......

  2. 这里没有检查4个相邻像素。它会检查4个点,例如,像这样: enter image description here

    1. 在这里他们检查&#34;角落条件&#34;对于这4个点是正确的,因为如果它不正确,则没有8个相邻像素满足&#34;角条件&#34;,所以它不是角像素。如果mask为零,则意味着vector中的所有像素都不能成为角落,所以我们向左移动16个像素。
    2. int mask = _mm_movemask_epi8(m0);
      if( mask == 0 )
          continue;
      
      1. 如果蒙版不为零,但是对于前8个像素&#34;角条件&#34;不是这样,他们只向左移8个像素,以检查下一次迭代时的剩余像素。
      2. if( (mask & 255) == 0 )
        {
            j -= 8;
            ptr -= 8;
            continue;
        }
        
        1. 最后一步。在这里,他们计算的相邻像素数大于x + thresholdc0个计数器,且小于x - thresholdc1个计数器。
        2. 这里为这些条件生成掩码:

          __m128i x = _mm_xor_si128(_mm_loadu_si128((const __m128i*)(ptr + pixel[k])), delta);
          m0 = _mm_cmpgt_epi8(x, v0);
          m1 = _mm_cmpgt_epi8(v1, x);
          

          请注意,如果vector的元素的条件为true,则将其值设置为0xFF或-1,因为我们将其视为signed char。

          c0 = _mm_and_si128(_mm_sub_epi8(c0, m0), m0); 
          c1 = _mm_and_si128(_mm_sub_epi8(c1, m1), m1);
          

          如果掩码元素为-1,则自减法以来累积到c0c1计数器(例如c0 - (-1))。但如果它等于零,则将其重置为零(_mm_and_si128)。

          他们需要存储最大值的计数器:

          max0 = _mm_max_epu8(max0, c0);
          max1 = _mm_max_epu8(max1, c1);
          

          因此,它们存储了满足&#34;角条件&#34;的最大邻近像素数。

          在这里,他们确定哪些像素实际是角落,哪些不是:

          max0 = _mm_max_epu8(max0, max1);
          int m = _mm_movemask_epi8(_mm_cmpgt_epi8(max0, K16));
          
          for( k = 0; m > 0 && k < 16; k++, m >>= 1 )
              if(m & 1)
              {
                  cornerpos[ncorners++] = j+k;
                  if(nonmax_suppression)
                      curr[j+k] = (uchar)cornerScore<patternSize>(ptr+k, pixel, threshold);
              }
          

          我希望它会有所帮助。我很抱歉我的英语不好。

答案 1 :(得分:2)

delta是一个只设置符号位的掩码。他们使用它是因为他们希望比较 unsigned ,但只有一个签名的比较。

添加128(或减去它,因为-128 == 128)并使用xoring执行相同的操作(如果您使用的是字节),因为

a + b == (a ^ b) + ((a & b) << 1)

如果b只设置了最高位,那么((a & b) << 1)项必须为零(a & b可以设置最高位,但它会被移出)。

然后正如您在下图中所看到的那样,将整个范围减去128“移位”,使得有符号的比较将给出与原始范围上的无符号比较相同的结果。

         |0 ... 127 ... 255|  unsigned
|-128 ... 0 ... 127|          signed

我不知道其他人,我希望其他人可以回答这个问题。