2013-03-20 3 views
0

Я пытаюсь суммировать d0, d1, d2 + d3, d4, d5 + d6, d7, d8. Я не знаю лучшей инструкции для этого, а затем беру среднее значение на 9. Я знаю, как делать усреднение с помощью аппроксимации, но суммируя эти полосы, я не могу найти для этого инструкцию? У меня также есть неправильное выходное изображение, поэтому я подозреваю операцию усреднения, если это правильно или нет.Суммирование 3 векторов и получение результата в неоне

inline void downsample3dOnePass(uint8_t* src, uint8_t *dst, int srcWidth) 
{ 

    for (int r = 0; r < (int)srcWidth/3; r++) 
    { 
     // load 24 pixels (grayscale) 
     uint8x8x3_t r0  = vld3_u8(src); 
     // move to next 24 byes 
     src+=24; 
     uint8x8x3_t r1  = vld3_u8(src); 
     src+=24; 
     uint8x8x3_t r2  = vld3_u8(src); 

     uint16x8_t d0 = vmovl_u8(r0.val[0]); 
     uint16x8_t d1 = vmovl_u8(r0.val[1]); 
     uint16x8_t d2 = vmovl_u8(r0.val[2]); 

     uint16x8_t d3 = vmovl_u8(r1.val[0]); 
     uint16x8_t d4 = vmovl_u8(r1.val[1]); 
     uint16x8_t d5 = vmovl_u8(r1.val[2]); 

     uint16x8_t d6 = vmovl_u8(r2.val[0]); 
     uint16x8_t d7 = vmovl_u8(r2.val[1]); 
     uint16x8_t d8 = vmovl_u8(r2.val[2]); 

     uint16x8_t d0d3Sum  = vaddq_u16 (d0, d3); 
     uint16x8_t d0d3d6Sum = vaddq_u16 (d0d3Sum, d6); 

     uint16x8_t d1d4Sum  = vaddq_u16 (d1, d4); 
     uint16x8_t d1d4d7Sum = vaddq_u16 (d1d4Sum, d7); 

     uint16x8_t d2d5Sum  = vaddq_u16 (d2, d5); 
     uint16x8_t d2d5d8Sum = vaddq_u16 (d2d5Sum, d8); 

     uint16x8_t firstSum  = vaddq_u16(d0d3d6Sum, d1d4d7Sum); 
     uint16x8_t secondSum = vaddq_u16(firstSum, d2d5d8Sum); 
     uint16x8_t totalSum  = vaddq_u16 (firstSum, secondSum); 

     // average = r0+r1+r2/8 ~9 for test 
     uint16x8_t totalAverage = vshrq_n_u16(totalSum,3); 
     uint8x8_t finalValue = vmovn_u16(totalAverage); 
     // store 8 bytes 
     vst1_u8(dst, finalValue); 

     src+=24; 
     // move to next row 
     dst+=8; 

    } 

} 

void downsample3d(uint8_t* src, uint8_t *dest, int srcWidth, int srcHeight) 
{ 
    for (int r = 0; r < (int)srcHeight/3; r++) 
    { 
     downsample3dOnePass(src, dest, srcWidth); 
    } 
} 

UPDATE: По bitbank ответ:

inline void downsample3dOnePass(uint8_t* src, uint8_t *dst, int srcWidth, int srcHeight, int strideSrc, int strideDest) 
    { 
     int iDestPitch = (strideDest); 
     uint8_t *s, *d; 
     uint8x8x3_t u88line0; 
     uint8x8x3_t u88line1; 
     uint8x8x3_t u88line2; 
     uint8x8_t u88Final; 
     uint16x8_t u168Sum; 
     int16x8_t i168divisor = vdupq_n_s16(7282/2); // 65536/9 - used with doubling saturating return high multiply 

     for (int r = 0; r < srcHeight/3; r++) 
     { 
      d = &dst[iDestPitch * r]; 
      s = &src[srcWidth * r*3]; 

      for (int c = 0; c < srcWidth/3; c+=8) 
      { 
       // load 8 sets of 3x3 pixels (grayscale) 
       u88line0 = vld3_u8(&s[0]); 
       u88line1 = vld3_u8(&s[srcWidth]); 
       u88line2 = vld3_u8(&s[srcWidth*2]); 
       s += 24; 
       // Sum vertically 
       u168Sum = vaddl_u8(u88line0.val[0], u88line0.val[1]); // add with widening 
       u168Sum = vaddw_u8(u168Sum, u88line0.val[2]); // accumulate with widening (horizontally) 
       u168Sum = vaddw_u8(u168Sum, u88line1.val[0]); // add the other vectors together 
       u168Sum = vaddw_u8(u168Sum, u88line1.val[1]); 
       u168Sum = vaddw_u8(u168Sum, u88line1.val[2]); 
       u168Sum = vaddw_u8(u168Sum, u88line2.val[0]); 
       u168Sum = vaddw_u8(u168Sum, u88line2.val[1]); 
       u168Sum = vaddw_u8(u168Sum, u88line2.val[2]); 
       // we now have the 8 sets of 3x3 pixels summed to 8 16-bit values 
       // To divide by 9 we will instead multiply by the inverse (65536/9) = 7282 
       u168Sum = vreinterpretq_u16_s16(vqrdmulhq_s16(i168divisor, vreinterpretq_s16_u16(u168Sum))); 
       u88Final = vmovn_u16(u168Sum); // narrow to 8 bits 
       // store 8 bytes 
       vst1_u8(d, u88Final); 
       d += 8; 
      } // for column 
     } // for row 
    } 


usage: 
//1280*920*grayscale 
QImage normalImage("/data/normal_image.png"); 

uint8_t *resultImage = new uint8_t[440*306]; 
    downsample3dOnePass(normalImage.bits(),resultImage, normalImage.width(), normalImage.height(), 1280, 440); 
+1

Вы добавляете несколько байтов вместе и сохраняете их в байте, а затем принимаете среднее значение этого. Картинка, в качестве примера, что произойдет, если соответствующие байты в двух векторах равны 0xff и 0x01, и вы добавили их вместе в виде байтов. Вы должны либо увеличить все пиксели до 16-битных значений при их суммировании, либо сдвинуть их вправо до добавления (избегайте использования последнего метода, если это возможно, поскольку это приведет к ненужной потере точности). – Michael

+0

@Michael Я ищу внутренность, которая преобразует uint8x8 в uint16x8, но не может ее найти. +1 для уведомления :) –

+1

'VMOVL (Vector Move Long) берет каждый элемент в векторе двойного слова, знак или нуль расширяет их в два раза до их первоначальной длины и помещает результаты в квадратный вектор.'. Таким образом, вы хотите получить 'uint16x8_t vmovl_u8 (uint8x8_t)' – Michael

ответ

3

С кодом возникает несколько проблем. Неисправности NEON довольно плохи, когда дело касается обработки VLDx, но ваши большие ошибки заключаются в том, что вы переполняете свои байтовые значения и загружаете пиксели по горизонтали, а не по вертикали. Вот лучший алгоритм, который будет обрабатывать исходные пиксели 8 * 3x3 на 8 целевых пикселей за раз. В вашей функции также отсутствует параметр rows.

inline void downsample3dOnePass(uint8_t* src, uint8_t *dst, int srcWidth, int srcHeight) 
{ 
int iDestPitch = ((srcWidth/3)+3) & 0xfffffffc; // DWORD aligned 
uint8_t *s, *d; 
uint8x8x3_t u88line0, u88line, u88line2; 
uint8x8_t u88Final; 
uint16x8_t u168Sum; 
int16x8_t i168divisor = vdupq_n_s16(7282/2); // 65536/9 - used with doubling saturating return high multiply 

    for (int r = 0; r < srcHeight/3; r++) 
    { 
    d = &dst[iDestPitch * r]; 
    s = &src[srcWidth * r*3]; 

    for (int c = 0; c < srcWidth/3; c+=8) 
    { 
     // load 8 sets of 3x3 pixels (grayscale) 
     u88line0 = vld3_u8(&s[0]); 
     u88line1 = vld3_u8(&s[srcWidth]); 
     u88line2 = vld3_u8(&s[srcWidth*2]); 
     s += 24; 
     // Sum vertically 
     u168Sum = vaddl_u8(u88Line0.val[0], u88Line0.val[1]); // add with widening 
     u168Sum = vaddw_u8(u168Sum, u88Line0.val[2]); // accumulate with widening (horizontally) 
     u168Sum = vaddw_u8(u168Sum, u88Line1.val[0]); // add the other vectors together 
     u168Sum = vaddw_u8(u168Sum, u88Line1.val[1]); 
     u168Sum = vaddw_u8(u168Sum, u88Line1.val[2]); 
     u168Sum = vaddw_u8(u168Sum, u88Line2.val[0]); 
     u168Sum = vaddw_u8(u168Sum, u88Line2.val[1]); 
     u168Sum = vaddw_u8(u168Sum, u88Line2.val[2]); 
     // we now have the 8 sets of 3x3 pixels summed to 8 16-bit values 
     // To divide by 9 we will instead multiply by the inverse (65536/9) = 7282 
     u168Sum = vreinterpretq_u16_s16(vqrdmulhq_s16(i168divisor, vreinterpretq_s16_u16(u168Sum))); 
     u88Final = vmovn_u16(u168Sum); // narrow to 8 bits 
     // store 8 bytes 
     vst1_u8(d, u88Final); 
     d += 8;  
    } // for column 
} // for row 
+0

изображение искажено. вот как я его называю uint8_t * resultImage = new uint8_t [450 * 310]; downsample3dOnePass (normalImage.bits(), resultImage, normalImage.width(), normalImage.height()); NormalImage 1280 * 920 +1 –

+0

«искаженный» не очень описательный. Убедитесь, что высота исходного и целевого изображений выполнена правильно. В моем коде выше, я использую шаг назначения, выровненный по левому краю. Вы не можете принимать это значение, поэтому измените его на srcWidth/3. Из вашего исходного кода кажется, что у вас нет четкого понимания того, как изображения выкладываются в памяти, и это похоже на текущую проблему. – BitBank

+0

извините, но я вас не понял. Должен ли я изменить int iDestPitch = ((srcWidth/3) +3) & 0xfffffffc; to int iDestPitch = (srcWidth/3); Изображение показано здесь http://i45.tinypic.com/2wnwqw9.png –

0

Для того, чтобы избежать переполнения при добавлении байтов нескольких векторов вместе, вы должны расширяться от байтов до полуслов (16-разрядных) до суммирования , После того как вы суммировали все пиксели и разделили результат, вы можете сузить результат до байтов.

неоновые присущее использовать для расширения байт полуслов в GCC является
uint16x8_t vmovl_u8 (uint8x8_t)

И соответствующим внутренний для сужения
uint8x8_t vmovn_u16 (uint16x8_t)

Обратите внимание, что если вы добавите 9 пикселей и разделят на 8 вы может все еще подвергать риску переполнение при сокращении до байтов. В этом случае вы можете использовать vqmovn_u16, который ведет себя как vmovn_u16, но также выполняет насыщенность.

+0

У меня все еще есть искаженное изображение:/Я обновил полный код. –

+0

'uint16x8_t totalSum = vaddq_u16 (firstSum, secondSum);' эта строка выглядит некорректной для меня. 'secondSum' должен уже содержать общую сумму всех пикселей в этой точке, поэтому другое добавление просто суммирует пиксели несколько раз. – Michael

+0

ошибка ... Я изменил ее, но такой же результат нет: S –