2013-03-13 98 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