2013-03-13 4 views
0

Я пытаюсь написать лучшую версию cv :: resize() OpenCV, и у меня появился код, который находится здесь: https://github.com/rmaz/NEON-Image-Downscaling/blob/master/ImageResize/BDPViewController.m Код предназначен для понижающей дискретизации изображения на 2 но я не могу получить алгоритм. Я хотел бы сначала преобразовать этот алгоритм в C, затем попытаться изменить его для целей обучения. Легко ли также преобразовать его в downsample любого размера?Объяснение ARM Neon Image Sampling

Функция:

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 вместо полудобавок функция будет намного быстрее (транспорты исчезнут) и более точны. (усекает только один раз) –

ответ

6

Алгоритм достаточно прост. Он считывает 8 пикселей из текущей строки и 8 из строки ниже. Затем он использует команду vhadd (halving-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

Удивительный ответ. Как пиксели усредняются по вертикали? два пикселя, затем два пикселя? и что необходимо для поддержки разного масштаба масштабирования? Я попробовал это на бумаге. Я все еще не получаю операцию транспонирования и горизонтально смежные пиксели:/ –

+0

Инструкция vhadd эквивалентна c = (a + b + 1)/2. Первая часть кода усредняет пиксели по вертикали, потому что регистры NEON, содержащие верхнюю и нижнюю строки, усредняются вместе. Поскольку нет vhadd, который работает горизонтально через векторные элементы NEON, значения должны быть транспонированы так, чтобы горизонтально смежные элементы помещались в отдельные регистры. После транспонирования они снова усредняются (усредняя пиксели «по горизонтали»). См. Описание VTRN здесь: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0489c/CIHDJAEA.html – BitBank

+0

Это правильно, как написано. Я пишу 4 байта, затем продвигаю указатель назначения на 4 байта. – BitBank

Смежные вопросы