реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> Графический фильтр на Cortex-M7, Увеличение ровно в 2 раза
__inline__
сообщение Jul 16 2018, 05:35
Сообщение #1


Местный
***

Группа: Участник
Сообщений: 257
Регистрация: 5-09-17
Пользователь №: 99 126



Бьюсь над реализацией графического фильтра HQ2X на STM32H743 (Cortex-M7). Фильтр работает, но притормаживает, когда в кадре много мелких деталей.

Была предпринята оптимизация: Switch/Case из 256 значений был заменён на JumpTable. Не помогло. Исходный код фильтра (Keil ARM MDK):

Прикрепленный файл  HQ2x.rar ( 8.36 килобайт ) Кол-во скачиваний: 21


Требуется растянуть кадр в 2 раза по обеим осям.

Есть другие фильтры Scale2x, SaI2x , LQ2x - с ними проблем нет, на STM32H743 они идут довольно шустро(написанные на C, без Asm-а).

Вот тут чувак заточил под NEON и DSP фильтр HQnX (что не годится для Cortex-M7): https://pyra-handheld.com/boards/threads/ru...sp.69047/page-5

Существуют ли аналогичные графические фильтры (в частности HQ 2x), оптимизированные на ассемблере для ядер ARM Cortex-M7?

Работа фильтра пояснена на рисунке:

Прикрепленное изображение


Сообщение отредактировал __inline__ - Jul 16 2018, 05:37
Go to the top of the page
 
+Quote Post
Genadi Zawidowsk...
сообщение Jul 16 2018, 06:12
Сообщение #2


Профессионал
*****

Группа: Участник
Сообщений: 1 620
Регистрация: 22-06-07
Из: Санкт-Петербург, Россия
Пользователь №: 28 634



А попорбовать вместо ассемблерных вставок (которые обычно сбивают оптимизатор) использовть встроенные инлайны компилятора? В случае GCC, к примеру, могут оптимизатором варьироваться регистры, содержащие исходные значения/результаты...
Кстати, сравнение для четырех восьмибитных чисел должно быть тоже в SIMD... скорее всего. Вы его делаете "в ручную".
Код
Searching for 'SSUB8'...
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core\Include\cmsis_armcc.h(803):#define __SSUB8                           __ssub8
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core\Include\cmsis_armclang.h(1381):__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2)
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core\Include\cmsis_armclang.h(1385):  __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) );
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core\Include\cmsis_gcc.h(1590):__STATIC_FORCEINLINE uint32_t __SSUB8(uint32_t op1, uint32_t op2)
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core\Include\cmsis_gcc.h(1594):  __ASM volatile ("ssub8 %0, %1, %2" : "=r" (result) : "r" (op1), "r" (op2) );
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core\Include\cmsis_iccarm.h(397):    #define __SSUB8   __iar_builtin_SSUB8
C:\USER\SVN\CMSIS_5-5.3.0\CMSIS\Core_A\Include\cmsis_iccarm.h(310):  #define __SSUB8   __iar_builtin_SSUB8
7 occurrence(s) have been found.


И самый главный вопрос - DATA CACHE включен в процессоре?

Сообщение отредактировал Genadi Zawidowski - Jul 16 2018, 06:18
Go to the top of the page
 
+Quote Post
ViKo
сообщение Jul 16 2018, 07:26
Сообщение #3


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Рисунок врет. Например, глаза лягушонка он размыл (округлил), а такой же черный квадрат недалеко оставил, как есть. И диагональные линии выглядят слишком красиво.
Растянуть в 2 раза - половина пикселей (по одной оси) уже есть, а половину всунуть кубической интерполяцией. Еще есть пиксели по диагонали, те нужно интерполировать побеим осям.
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Jul 16 2018, 07:49
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(ViKo @ Jul 16 2018, 10:26) *
Рисунок врет. Например, глаза лягушонка он размыл (округлил), а такой же черный квадрат недалеко оставил, как есть. И диагональные линии выглядят слишком красиво.

Просто это не совсем фильтр в привычном понимании. Алгоритм.
Go to the top of the page
 
+Quote Post
__inline__
сообщение Jul 16 2018, 07:49
Сообщение #5


Местный
***

Группа: Участник
Сообщений: 257
Регистрация: 5-09-17
Пользователь №: 99 126



Цитата(Genadi Zawidowski @ Jul 16 2018, 07:12) *
А попорбовать вместо ассемблерных вставок (которые обычно сбивают оптимизатор) использовть встроенные инлайны компилятора? В случае GCC, к примеру, могут оптимизатором варьироваться регистры, содержащие исходные значения/результаты...
Кстати, сравнение для четырех восьмибитных чисел должно быть тоже в SIMD... скорее всего. Вы его делаете "в ручную".


Наскоряк не нашёл CMSIS-овский хедер, поэтому на ассемблере написал. Попробуем использовать инлайны.

Цитата(Genadi Zawidowski @ Jul 16 2018, 07:12) *
И самый главный вопрос - DATA CACHE включен в процессоре?


Да. Без него всё очень сильно медленно.
Go to the top of the page
 
+Quote Post
Genadi Zawidowsk...
сообщение Jul 16 2018, 07:53
Сообщение #6


Профессионал
*****

Группа: Участник
Сообщений: 1 620
Регистрация: 22-06-07
Из: Санкт-Петербург, Россия
Пользователь №: 28 634



CMSIS обычно подключен через соответствующий процессорный header - например, в моем случае:
через stm32h7xx.h включен stm32h743xx.h - а в нем в районе 240 строки core_cm7.h:

Код
#define __CM7_REV               0x0100U   /*!< Cortex-M7 revision r1p0                       */
#define __MPU_PRESENT             1       /*!< CM7 provides an MPU                           */
#define __NVIC_PRIO_BITS          4       /*!< CM7 uses 4 Bits for the Priority Levels       */
#define __Vendor_SysTickConfig    0       /*!< Set to 1 if different SysTick Config is used  */
#define __FPU_PRESENT             1       /*!< FPU present                                   */
#define __ICACHE_PRESENT          1       /*!< CM7 instruction cache present                 */
#define __DCACHE_PRESENT          1       /*!< CM7 data cache present                        */
#include "core_cm7.h"                     /*!< Cortex-M7 processor and core peripherals      */


Сообщение отредактировал Genadi Zawidowski - Jul 16 2018, 07:54
Go to the top of the page
 
+Quote Post
ViKo
сообщение Jul 16 2018, 07:54
Сообщение #7


Универсальный солдатик
******

Группа: Модераторы
Сообщений: 8 634
Регистрация: 1-11-05
Из: Минск
Пользователь №: 10 362



Цитата(aaarrr @ Jul 16 2018, 10:49) *
Просто это не совсем фильтр в привычном понимании. Алгоритм.

Неужели глаза размером в пиксель отличает от просто кубика?
Go to the top of the page
 
+Quote Post
__inline__
сообщение Jul 16 2018, 07:54
Сообщение #8


Местный
***

Группа: Участник
Сообщений: 257
Регистрация: 5-09-17
Пользователь №: 99 126



Цитата(ViKo @ Jul 16 2018, 08:26) *
Рисунок врет. Например, глаза лягушонка он размыл (округлил), а такой же черный квадрат недалеко оставил, как есть. И диагональные линии выглядят слишком красиво.
Растянуть в 2 раза - половина пикселей (по одной оси) уже есть, а половину всунуть кубической интерполяцией. Еще есть пиксели по диагонали, те нужно интерполировать побеим осям.


Не врёт. Это волшебный фильтр. Не просто билинейная-бикубическая фильтрация , а смарт-фильтр, заточенный под пиксель-арт.

В доказательство прикрепляю программу (под винду) вместе с исходником и мейкфайлом. Фильтр LQ2x (практически результат схож с HQ2x, но быстрее и легче для STM32):

Прикрепленный файл  LQ2x_WIN32.rar ( 218.86 килобайт ) Кол-во скачиваний: 16


Входные данные: файл test.raw, 160x102 пикселя RGB 8:8:8
Выходные данные после отработки программы : LQ2x.raw 204x320 пикселей RGB 8:8:8
В программе фильтр делает ещё поворот на 90 градусов и работает в цветовом пространстве RGB 5:6:5 (для моих целей).

RAW смотреть к примеру IrfanView, выставив длину, ширину и пиксель-формат.

Ну и ниже картинка с результатами фильтров (с википедии):

Прикрепленное изображение


Сообщение отредактировал __inline__ - Jul 16 2018, 07:57
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jul 16 2018, 08:00
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(__inline__ @ Jul 16 2018, 08:35) *
Была предпринята оптимизация: Switch/Case из 256 значений был заменён на JumpTable. Не помогло. Исходный код фильтра (Keil ARM MDK):

Уже писали много раз и тут в том числе: все эти рукопашные inline - мёртвому припарки. При включённой оптимизации компилятор сам заинлайнит что нужно.
И тем более - какие-то таблицы переходов: компилятор сам прекрасно умеет их делать если switch/case удовлетворяет некоторым условиям: достаточно большое число case и последовательное расположение case-значений. Прежде чем пытаться что-то делать в подобном духе, надо хотя-бы листинг при полной оптимизации посмотреть и подумать.
А ускорить тут можно только ассемблером.
Go to the top of the page
 
+Quote Post
__inline__
сообщение Jul 16 2018, 08:07
Сообщение #10


Местный
***

Группа: Участник
Сообщений: 257
Регистрация: 5-09-17
Пользователь №: 99 126



Цитата(jcxz @ Jul 16 2018, 09:00) *
Уже писали много раз и тут в том числе: все эти рукопашные inline - мёртвому припарки. При включённой оптимизации компилятор сам заинлайнит что нужно.
И тем более - какие-то таблицы переходов: компилятор сам прекрасно умеет их делать если switch/case удовлетворяет некоторым условиям: достаточно большое число case и последовательное расположение case-значений. Прежде чем пытаться что-то делать в подобном духе, надо хотя-бы листинг при полной оптимизации посмотреть и подумать.
А ускорить тут можно только ассемблером.


При -O3 -Otime switch/case заменяется на Jumptable при условии, что есть некая упорядоченность в проверяемых значениях. А в фильтре она идёт в разнобой. Максимум на что можно надеяться при таком подходе - это на поиск с бинарным разделением
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jul 16 2018, 08:17
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(__inline__ @ Jul 16 2018, 11:07) *
При -O3 -Otime switch/case заменяется на Jumptable при условии, что есть некая упорядоченность в проверяемых значениях. А в фильтре она идёт в разнобой. Максимум на что можно надеяться при таком подходе - это на поиск с бинарным разделением

Ещё и не факт что подобная раскрутка циклов:
Код
   if ((w1 != w5) && (Diff(y, RGBtoYUV[w1]))) pattern |= (1 << 0);
   if ((w2 != w5) && (Diff(y, RGBtoYUV[w2]))) pattern |= (1 << 1);
   if ((w3 != w5) && (Diff(y, RGBtoYUV[w3]))) pattern |= (1 << 2);
   if ((w4 != w5) && (Diff(y, RGBtoYUV[w4]))) pattern |= (1 << 3);
   if ((w6 != w5) && (Diff(y, RGBtoYUV[w6]))) pattern |= (1 << 4);
   if ((w7 != w5) && (Diff(y, RGBtoYUV[w7]))) pattern |= (1 << 5);
   if ((w8 != w5) && (Diff(y, RGBtoYUV[w8]))) pattern |= (1 << 6);
   if ((w9 != w5) && (Diff(y, RGBtoYUV[w9]))) pattern |= (1 << 7);

даст ускорение. Тело цикла достаточно большое, чтобы одна команда перехода в конце незначительно влияла на скорость. Зато кеша надо намного меньше.
Go to the top of the page
 
+Quote Post
AVI-crak
сообщение Jul 16 2018, 08:35
Сообщение #12


Частый гость
**

Группа: Участник
Сообщений: 182
Регистрация: 16-10-15
Пользователь №: 88 894



Цитата(__inline__ @ Jul 16 2018, 11:35) *
Вот тут чувак заточил под NEON и DSP фильтр HQnX (что не годится для Cortex-M7)

NEON имеет множество пробелов по сравнению с возможностями Cortex-M7. И уж точно Cortex-M7 может выполнять всё что есть в NEON.
Нужно просто перелопатить код, удаляя и заменяя вставки NEON функций.
Go to the top of the page
 
+Quote Post
Genadi Zawidowsk...
сообщение Jul 16 2018, 08:39
Сообщение #13


Профессионал
*****

Группа: Участник
Сообщений: 1 620
Регистрация: 22-06-07
Из: Санкт-Петербург, Россия
Пользователь №: 28 634



Там тоже ассемблер с "неоном". Неон умеет по 16 байт за один раз обсчитывать, Cortex-M7 только по четыре.
лично я делал FIR фильтры (многоканальные, для float) с импользованием NEON. Получилось.


ps: "там" это тут
Цитата


Сообщение отредактировал Genadi Zawidowski - Jul 16 2018, 08:44
Go to the top of the page
 
+Quote Post
jcxz
сообщение Jul 16 2018, 08:50
Сообщение #14


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Я так понимаю - Вы сами пытались оптимизировать функцию Diff() asm-вставками? И в её начале - закомментаренное си-тело?
Если так, то действие:
Код
  int c1v = (c1 & Vmask) - (c2 & Vmask);
  if (Absolute(c1v) > trV)

совсем не аналогично варианту на асме ниже.
Подумайте, что будет если к примеру c1==0x81, c2==0 в первом и во втором случае.
Если конечно в реализации изначально не заложено какое-то ограничение по количеству цветов (диапазону значений байтов).
Но в любом случае: взятие модуля от числа - не аналогично операции x=-x.
Да и вычитать SSUB8 c1,c2 чтобы потом находить NEG от каждого байта - это как-то бессмысленно. Почему бы тогда сразу не сделать SSUB8 c2,c1 ?

PS: Так что похоже у Вас ещё и реализация кривая.... laughing.gif
Go to the top of the page
 
+Quote Post
KnightIgor
сообщение Jul 16 2018, 11:54
Сообщение #15


Знающий
****

Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725



Цитата(__inline__ @ Jul 16 2018, 08:54) *
Не врёт. Это волшебный фильтр. Не просто билинейная-бикубическая фильтрация , а смарт-фильтр, заточенный под пиксель-арт.
Фильтр LQ2x (практически результат схож с HQ2x, но быстрее и легче для STM32):

ОТ: Back to 80's? Не в смысле фильтров, а моды на 8-битные игры? Интересно узнать, каков back ground изысканий...
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 6th July 2025 - 05:53
Рейтинг@Mail.ru


Страница сгенерированна за 0.01514 секунд с 7
ELECTRONIX ©2004-2016