Билинейная интерполяция от C до Neon

Я пытаюсь уменьшить изображение с помощью Neon. Итак, я попытался использовать неон, написав функцию, которая вычитает два изображения, используя неон, и мне это удалось. Теперь я вернулся, чтобы написать билинейную интерполяцию с использованием неоновых свойств. Прямо сейчас у меня есть две проблемы: получить 4 пикселя из одной строки и одного столбца, а также вычислить интерполированное значение (серый) из 4 пикселей или, если возможно, из 8 пикселей из одной строки и одного столбца. Я пытался думать об этом, но я думаю, что алгоритм должен быть переписан вообще?

void resizeBilinearNeon( uint8_t *src, uint8_t *dest,  float srcWidth,  float srcHeight,  float destWidth,  float destHeight)
{

    int A, B, C, D, x, y, index;

       float x_ratio = ((float)(srcWidth-1))/destWidth ;
       float y_ratio = ((float)(srcHeight-1))/destHeight ;
       float x_diff, y_diff;

       for (int i=0;i<destHeight;i++) {
          for (int j=0;j<destWidth;j++) {
               x = (int)(x_ratio * j) ;
               y = (int)(y_ratio * i) ;
               x_diff = (x_ratio * j) - x ;
               y_diff = (y_ratio * i) - y ;
               index = y*srcWidth+x ;

               uint8x8_t pixels_r = vld1_u8 (src[index]);
               uint8x8_t pixels_c = vld1_u8 (src[index+srcWidth]);

               // Y = A(1-w)(1-h) + B(w)(1-h) + C(h)(1-w) + Dwh
               gray = (int)(
                           pixels_r[0]*(1-x_diff)*(1-y_diff) +  pixels_r[1]*(x_diff)*(1-y_diff) +
                           pixels_c[0]*(y_diff)*(1-x_diff)   +  pixels_c[1]*(x_diff*y_diff)
                           ) ;

               dest[i*w2 + j] = gray ;
           }
  }  

person Ahmed Saleh    schedule 19.03.2013    source источник
comment
Правильная процедура понижения частоты дискретизации должна учитывать гораздо больше, чем 4 пикселя для каждого выходного пикселя. Билинейный - неправильный подход, в данном приложении он ничем не лучше ближайшего соседа.   -  person Mark Ransom    schedule 19.03.2013
comment
@MarkRansom Я попробовал обычного ближайшего соседа, но качество изображения повлияло на мои процедуры компьютерного зрения. Лучшее, что подходит для моего приложения, - это использование билинейной интерполяции, но проблема в том, что функция opencv работает медленно.   -  person Ahmed Saleh    schedule 19.03.2013
comment
Билинейность неплохо работает в диапазоне +-50% масштабирования. На другом конце возникает пикселизация, а на другом конце частотное наложение (например, эффект муара). Узким местом при распараллеливании/векторизации является необходимость обращения как минимум к 4 случайным элементам памяти на пиксель и генерация их эффективных адресов; и решение состоит в том, чтобы использовать pshufb на Intel и vtbl на Neon для доступа к 8, 16 или даже 32 (ymm) отдельным байтам из очень локальной справочной таблицы.   -  person Aki Suihkonen    schedule 20.03.2013


Ответы (2)


@MarkRansom неверен в отношении ближайшего соседа и билинейной интерполяции 2x2; билинейный с использованием 4 пикселей даст лучший результат, чем ближайший сосед. Он прав в том, что усреднение соответствующего количества пикселей (более 4 при масштабировании > 2:1) все равно даст лучший результат. Однако NEON не поможет с понижением частоты дискретизации изображения, если только масштабирование не выполняется с помощью целочисленного соотношения.

Максимальное преимущество NEON и других наборов инструкций SIMD заключается в возможности одновременной обработки 8 или 16 пикселей с использованием одних и тех же операций. Получая доступ к отдельным элементам так, как вы это делаете, вы теряете все преимущества SIMD. Другая проблема заключается в том, что перемещение данных из регистров NEON в регистры ARM является медленной операцией. Понижение разрешения изображений лучше всего выполнять с помощью графического процессора или оптимизированных инструкций ARM.

person BitBank    schedule 19.03.2013
comment
Я попробовал подход с графическим процессором, и чтение данных с графического процессора происходит медленнее, чем OpenCV!. Теперь это моя проблема, я не знаю, как работать с 8 пикселями одновременно, чтобы сделать эквивалентный код C. Я хочу, чтобы вы помогли с этим, по крайней мере, я вижу это своими глазами :). - person Ahmed Saleh; 19.03.2013
comment
Это не проблема увидеть это своими глазами, это проблема, которую трудно решить векторизованным способом. Если вы можете ограничить проблему передискретизацией изображения на фиксированную величину (например, 2: 1), то вы можете написать оптимизированное решение NEON. - person BitBank; 19.03.2013
comment
Вот почему я хотел увидеть это своими глазами. Я много думал об этом на бумаге, используя 8-байтовый вектор, и очень сложно перебрать столбцы и выполнить обработку. Как насчет функции, которая делает от 1280*960 до 400*300? тогда напишите еще один для другого разрешения? - person Ahmed Saleh; 19.03.2013
comment
Более быстрым способом, вероятно, будет 1280x960 -> 640x480 с NEON, затем 640x480 -> 400x300 с оптимизированной процедурой C/ASM. - person BitBank; 19.03.2013
comment
Мой комментарий был основан на коэффициентах понижения дискретизации, намного превышающих 2: 1, например, при создании миниатюр; поскольку в вопросе не упоминались соотношения, я поддерживаю свое первоначальное утверждение. В диапазоне от 1:1 до 2:1 вы можете получить приемлемые результаты из-за сглаживающего эффекта билинейного фильтра, который эквивалентен палаточному фильтру. - person Mark Ransom; 19.03.2013
comment
1280x960 -> 640x480 лучше всего делать с простым усреднением 4x4, вообще не нужно делать билинейное. - person Mark Ransom; 19.03.2013
comment
@BitBank Я бы сделал выборку на 2, используя NEON, и после этого буду использовать функцию изменения размера opencv. Надеюсь, это ускорит процесс. - person Ahmed Saleh; 19.03.2013
comment
Может быть, правильнее будет сказать, что использование NEON для помощи с понижением частоты дискретизации нецелочисленными коэффициентами сложно, а не то, что NEON не поможет. - person Eric Postpischil; 19.03.2013

Neon точно поможет с даунсэмплингом в произвольном соотношении с помощью билинейной фильтрации. Ключевым моментом является разумное использование инструкции vtbl.8, которая может выполнять параллельную справочную таблицу для 8 последовательных пикселей назначения из предварительно загруженного массива:

 d0 = a [b] c [d] e [f]  g  h, d1 =  i  j  k  l  m  n  o  p 
 d2 = q  r  s  t  u  v  [w] x, d3 = [y] z [A] B [C][D] E  F ...
 d4 = G  H  I  J  K  L   M  N, d5 =  O  P  Q  R  S  T  U  V ...

Можно легко вычислить дробные позиции пикселей в скобках:

 [b] [d] [f] [w] [y] [A] [C] [D],  accessed with vtbl.8 d6, {d0,d1,d2,d3}
 The row below would be accessed with            vtbl.8 d7, {d2,d3,d4,d5} 

Увеличение vadd.8 d6, d30 ; с d30 = [1 1 1 1 1 ... 1] дает индексы поиска для пикселей справа от начала координат и т. д.

Нет причин получать пиксели из двух строк, кроме как проиллюстрировать, что это возможно, и что этот метод можно использовать для реализации небольших искажений, если это необходимо.

В приложениях реального времени, использующих, например. lanzcos может быть немного излишним, но все же осуществимым с использованием NEON. Понижение дискретизации более крупных факторов, конечно, требует (сильной) фильтрации, но может быть легко достигнуто с помощью итеративного усреднения и прореживания в соотношении 2: 1 и только в конце с использованием дробной выборки.

Для записи любых 8 последовательных пикселей можно вычислить вектор

  x_positions = (X + [0 1 2 3 4 5 6 7]) * source_width / target_width;
  y_positions = (Y + [0 0 0 0 0 0 0 0]) * source_height / target_height;

  ptr = to_int(x_positions) + y_positions * stride;
  x_position += (ptr & 7); // this pointer arithmetic goes only for 8-bit planar
  ptr &= ~7;               // this is to adjust read pointer to qword alignment

  vld1.8 {d0,d1}, [r0]
  vld1.8 {d2,d3], [r0], r2 // wasn't this possible? (use r2==stride)

  d4 = int_part_of (x_positions);
  d5 = d4 + 1;
  d6 = fract_part_of (x_positions);
  d7 = fract_part_of (y_positions);

  vtbl.8 d8,d4,{d0,d1}  // read top row
  vtbl.8 d9,d5,{d0,d1}  // read top row +1
  MIX(d8,d9,d6)             // horizontal mix of ptr[] & ptr[1]
  vtbl.8 d10,d4,{d2,d3} // read bottom row
  vtbl.8 d11,d5,{d2,d3} // read bottom row
  MIX(d10,d11,d6)           // horizontal mix of ptr[1024] & ptr[1025]
  MIX(d8,d10,d7)

  // MIX (dst, src, fract) is a macro that somehow does linear blending
  // should be doable with ~3-4 instructions

Для вычисления целых частей достаточно использовать разрешение 8,8 бит (на самом деле не нужно вычислять 666+[0 1 2 3 .. 7]) и хранить все промежуточные результаты в регистре simd.

Отказ от ответственности - это концептуальный псевдо-c/векторный код. В SIMD нужно оптимизировать две параллельные задачи: какое минимальное количество необходимых арифметических операций и как свести к минимуму ненужную перетасовку/копирование данных. В этом отношении NEON с трехрегистровым подходом гораздо лучше подходит для серьезного DSP, чем SSE. Во-вторых, это количество команд умножения, а в-третьих, преимущества инструкций чередования.

person Aki Suihkonen    schedule 19.03.2013
comment
Я так и не понял, как правильно уменьшить на 2:1, когда входное измерение нечетное. Особенно, когда вы повторяете это, ошибки на краю будут накапливаться. - person Mark Ransom; 19.03.2013
comment
[режим хвастовства] Моя память, вероятно, неверна, но на 600 МГц Cortex-A8 моя реализация NEON смогла запустить билинейную интерполяцию 30 кадров в секунду на нерегулярной сетке для стереографического (3D) выравнивания видео: калибровка 3D-оси, масштабирование, коррекция ствола, выравнивание гистограммы и многое другое -- все параметры изменяются во время выполнения. Ключевые слова: неон и билинейность. [\режим хвастовства выключен] - person Aki Suihkonen; 20.03.2013
comment
@Ahi Suihkonen: Если вы предоставите указатель на исходный код комбинированного конвейера, я поддержу ваш хвастливый комментарий ;-) - person FrankH.; 20.03.2013
comment
Боюсь, о доставке реального кода не может быть и речи только по юридическим причинам. Но я могу реконструировать отдельные части / ответить на конкретные вопросы. - person Aki Suihkonen; 20.03.2013