2013-03-13 97 views
0

我想寫一個更好的版本的cv :: resize()的OpenCV,我來了一個十字架的代碼是在這裏:https://github.com/rmaz/NEON-Image-Downscaling/blob/master/ImageResize/BDPViewController.m 該代碼是下采樣圖像2但我無法獲得算法。我想首先將該算法轉換爲C,然後嘗試將其修改爲用於學習目的。將它轉換爲任意大小的下采樣也很容易嗎?解釋ARM霓虹燈圖像採樣

功能是:

static void inline resizeRow(uint32_t *dst, uint32_t *src, uint32_t pixelsPerRow) 
{ 
    const uint32_t * rowB = src + pixelsPerRow; 

    // force the number of pixels per row to a multiple of 8 
    pixelsPerRow = 8 * (pixelsPerRow/8); 

    __asm__ volatile("Lresizeloop: \n" // start loop 
        "vld1.32 {d0-d3}, [%1]! \n" // load 8 pixels from the top row 
        "vld1.32 {d4-d7}, [%2]! \n" // load 8 pixels from the bottom row 
        "vhadd.u8 q0, q0, q2 \n" // average the pixels vertically 
        "vhadd.u8 q1, q1, q3 \n" 
        "vtrn.32 q0, q2 \n" // transpose to put the horizontally adjacent pixels in different registers 
        "vtrn.32 q1, q3 \n" 
        "vhadd.u8 q0, q0, q2 \n" // average the pixels horizontally 
        "vhadd.u8 q1, q1, q3 \n" 
        "vtrn.32 d0, d1 \n" // fill the registers with pixels 
        "vtrn.32 d2, d3 \n" 
        "vswp d1, d2 \n" 
        "vst1.64 {d0-d1}, [%0]! \n" // store the result 
        "subs %3, %3, #8 \n" // subtract 8 from the pixel count 
        "bne Lresizeloop \n" // repeat until the row is complete 
: "=r"(dst), "=r"(src), "=r"(rowB), "=r"(pixelsPerRow) 
: "0"(dst), "1"(src), "2"(rowB), "3"(pixelsPerRow) 
: "q0", "q1", "q2", "q3", "cc" 
); 
} 

To call it: 

// downscale the image in place 
    for (size_t rowIndex = 0; rowIndex < height; rowIndex+=2) 
    { 
     void *sourceRow = (uint8_t *)buffer + rowIndex * bytesPerRow; 
     void *destRow = (uint8_t *)buffer + (rowIndex/2) * bytesPerRow; 
     resizeRow(destRow, sourceRow, width); 
    } 
+0

你發現了一個很不好的例子:1)它與每個半添加截斷,因此結果不太準確。 2)除了令人困惑之外,它正在浪費所有這些轉換的寶貴週期。使用VPADD和VPADAL而不是半加,函數將快很多(轉置消失)並且更準確。 (只截取一次) – 2013-06-23 05:35:23

回答

6

的算法是非常簡單的。它從當前行讀取8個像素,從下一行讀取8個像素。然後使用vhadd(half-add)指令垂直平均8個像素。然後它轉換像素的位置,以便水平相鄰的像素對現在位於不同的寄存器(垂直排列)中。然後再做一組加減平均值。然後再將結果轉換爲原始位置並寫入目的地。這個算法可以被重寫,以處理不同積分大小的縮放,但是按照寫法,它只能進行2x2到1的平均縮減。下面是相當於C代碼的代碼:

static void inline resizeRow(uint32_t *dst, uint32_t *src, uint32_t pixelsPerRow) 
{ 
    uint8_t * pSrc8 = (uint8_t *)src; 
    uint8_t * pDest8 = (uint8_t *)dst; 
    int stride = pixelsPerRow * sizeof(uint32_t); 
    int x; 
    int r, g, b, a; 

    for (x=0; x<pixelsPerRow; x++) 
    { 
     r = pSrc8[0] + pSrc8[4] + pSrc8[stride+0] + pSrc8[stride+4]; 
     g = pSrc8[1] + pSrc8[5] + pSrc8[stride+1] + pSrc8[stride+5]; 
     b = pSrc8[2] + pSrc8[6] + pSrc8[stride+2] + pSrc8[stride+6]; 
     a = pSrc8[3] + pSrc8[7] + pSrc8[stride+3] + pSrc8[stride+7]; 
     pDest8[0] = (uint8_t)((r + 2)/4); // average with rounding 
     pDest8[1] = (uint8_t)((g + 2)/4); 
     pDest8[2] = (uint8_t)((b + 2)/4); 
     pDest8[3] = (uint8_t)((a + 2)/4); 
     pSrc8 += 8; // skip forward 2 source pixels 
     pDest8 += 4; // skip forward 1 destination pixel 
    } 
+0

驚人的答案。像素的垂直平均值如何?兩個像素,然後兩個像素?以及支持不同規模的縮放需要什麼?我在紙上試過,我仍然沒有獲得轉置操作和水平相鄰的像素:/ – 2013-03-13 18:06:32

+0

vhadd指令相當於c =(a + b + 1)/ 2。代碼的第一部分將像素垂直平均,因爲包含頂部和底部線條的NEON寄存器被平均在一起。由於沒有在NEON向量元素上水平運行的vhadd,因此需要對這些值進行轉置,以便將水平相鄰的元素放置在單獨的寄存器中。轉位後,再次平均(平均「水平」像素)。請參閱此處的VTRN說明:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/CIHDJAEA.html – BitBank 2013-03-13 18:12:33

+0

它的寫法是正確的。我寫4個字節,然後將目標指針前移4個字節。 – BitBank 2013-03-13 20:37:02