2015-06-17 61 views
4

有人可以幫助我理解OpenCV中FAST角點檢測的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); 

我認爲下面有一些做與閾值檢查,但無法瞭解使用三角洲

__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); 
    } 

回答

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))); ......

這不是檢查的4個相鄰像素。它檢查4個點,例如,是這樣的: enter image description here

  • 在這裏,他們檢查「角條件」爲真此4個點,因爲如果這不是真的沒有8滿足「轉角條件」的相鄰像素,所以它不是轉角像素。如果mask爲零,則意味着矢量中的所有像素都不能成爲轉角,所以我們向左移動16個像素。
  • int mask = _mm_movemask_epi8(m0); 
    if(mask == 0) 
        continue; 
    
  • 如果掩模是不爲零,但是對於第一8個像素「角條件」是不正確的,他們左移只爲8個象素,以檢查殘留下一次迭代時的像素。
  • if((mask & 255) == 0) 
    { 
        j -= 8; 
        ptr -= 8; 
        continue; 
    } 
    
  • 步和最後一步。在這裏,他們計數大於x + thresholdc0計數器的相鄰像素的數量,並且它們小於x - thresholdc1計數器。
  • 這裏產生面具這樣的條件:

    __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); 
    

    注意,如果條件爲真爲載體的元素他的價值設置爲0xFF或-1,因爲我們把他當作符號字符。

    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); 
    

    所以他們存儲滿足「拐角條件」相鄰像素的最大數量。

    在這裏,他們確定哪些像素實際上角落,哪些不是:

    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); 
        } 
    

    我希望這將有助於。我很抱歉我的英語不好。

    +0

    「如果掩碼不爲零,但對於前8個像素」拐角條件「不正確,它們只會左移8個像素以檢查下一次迭代中的剩餘像素。 但是,如果前8個像素的第4個像素是角落怎麼辦?不應該將它移到** mask **中的第一個0xff? – will2km

    +0

    你是對的,計數前導零位和轉換到特定數量的像素會更方便,但它可能過於複雜並導致性能下降。我認爲這種策略對於x86/64 CPU的許多情況是最佳的。 – akarsakov

    2

    delta是其中僅signbits都設置了屏蔽。他們使用它,因爲他們想要比較大於未簽名的,但只有一個簽名比較。

    添加128(或減去,因爲-128 == 128)和異或用它做相同的(如果你用字節工作),因爲

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

    如果b只有頂部位設置,((a & b) << 1)項必須爲零(a & b可以設置最高位,但它已移出)。

    然後,如下圖所示,減去128「向下移動」整個範圍,使得帶符號的比較結果與原始範圍的無符號比較結果相同。

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

    我不知道其餘的,我希望別人能回答。

    +0

    感謝您的解釋。但是我必須接受來自@akarsakov的答案 – will2km