
Данная публикация служит пояснительным материалом к предыдущей, а так-же самостоятельной для тех, кто читает по данной теме мои публикации впервые.
Сначала о том, каким алгоритмом я планирую заменить в своих работах свёрточные нейросети. Чтобы это работало быстро — нужны карты трассировок. Линии трассировок на карте расположены параллельно под определённым углом на каждой карте — так и происходит условная поляризация. Генератор карт работает быстро и генерирует он карты трассировок направленных прямыми линиями, обрыв каждой линии он отмечает в данных. То-есть сначала запускатеся генератор карт и генерирует картинку, данная анимация существенно отличается от работы генератора и показывает только его ТЗ — в каждом пикселе карты записать координаты следующего пиксела и обозначить в данных окончание каждой линии. Изображения я взял небольшие, но тем не менее файлы анимации достаточно увесистые. Допустим что обрабатываемые изображения будет 7*7 пикселов, а карт трассировок всего четыре, тогда ТЗ генератора примерно будет выглядеть так, но на самом деле его алгоритм намного сложнее и работает на много быстрее — он ничего практически не считает и выдает большие объёмы данных автоматически, но об этом позже, а пока так чисто визуально

Здесь четыре слоя — четыре карты, линии трассировок разных карт расположены с интервалом 45 градусов. Сам код генератора пока может от 0-90 для любых размеров обрабатываемых изображений, но с ограничем высоты изображения — не менее трёх пикселов, так как я вместо блака проверки условий if вставил свою формулу от математической логики — так проще и быстрее. Можно было алгоритмом Брезенхема, но для поставленной задачи он абсолютно аутичен — медленнен. Ниже я разберу алгоритм своего генератора, но пока о алгоритме замены свёрточных нейросетей.
Алгоритм обрабатывающий изображение следует по пикселам согласно трассировкам по картам, где у него обозначен конец каждой линии, как только на изображении соотвествующий пиксел окрашен в искомы свет, алгоритм на своём «поляризованном» слое увеличивает вес этого пиксела и увеличивает вес пикселов линии пока не встретит пустой пиксел на изображении, или не кончится линия, после чего вес обнуляется. В реализации может быть немного иначе, но ТЗ алгоритма, читающего изображение, именно такое. Слои 1-4, это поляризованные слои алгоритма, 5 — изображение.
Вот три анимации того, как это визуализируется. Тут движение в обратном направлении, в противоположность алгоритму генерирующему карты.



Что такое свёрточная нейросеть в моём понимании — нечто определяющее и разделяющее данные о размерах линий, пересечениях и образуемых углах.
На примере обработки изображения с цифрой 8 можно выделить что имеются вершинообразующие пикселы, принадлежащие на разных слоях линиям с большим весом. Данные об углах в данной визуализации связываются с расстоянием между поляризованными слоями. Это примерно так, перечёркнуты вершинообразующие пикселы —

И то и другое легко вычисляется, так например коэфициент вершинообразования сложением весов пиксела на разных слоях, вычислить расстояние между полярными слоями — так же не сложно. Можно добавить методы вычисления дуг и другой метаинформации, но публикация не о перспективах и полном списке возможностей, а пояснительная.
Далее по генератору карт, тракотороподобному, но быстрому, изображение старое — поэтому в грязи и пыли ;).

Алгоритм-генератор карт записывает от пиксела входа в какой следующий пиксел следовать алгритму-читателю. Делает он это так, буд-то бы идёт от конца карты к началу и записывает в каждый пиксел сведения координат где только что был, НО, на самом деле всё не так, потому что такой метод требует больших затрат, и реализовано всё иначе — построчно: никаких углов, а вместах скоса алгоритм предполагает-координаты откуда мог попасть в текущий пиксел при таком-то наклоне линии. Тогда для всей карты ничего не надо считать абсолютно, так как скос всегда один — шаг вверх (по машинным координатам Y, где верх — это низ) и шаг вправо. А что-же тогда считать? Считать остаётся всего один правый столбец, для которого формула весьма тривиальна, привожу не для разложения, а для визуальной оценки
TracerX := RazmerX — (stepFrequent * y + znak *trunc(y / (stepsBig+1)));
, и самую нижнюю строку, вот тут вот намного сложнее, так как сляпал на скорую руку какую смог математическую логику, что намного проще чем мудрить с набором операторов if … else … и заниматься самовыносом мозгов самому себе, пусть лучше математика сама себя вывозит
TracerX := x — ( znaksign(trunc((1+sign(y-trunc(y/(stepsBig+1))(stepsBig+1)-steps)/2))) + stepFrequent * y + znak * trunc(y / (stepsBig+1)))+1;
.Сложность расчёта последней строки связана с вычислением положения изменённого шага скоса и возможно что формулу можно сделать изящней и несколько с иным смыслом, но я сильно не заморачивался, ведь это егенератор карт, этап пока что только наработка и отладка, а сами карты можно сохранять и использовать многократно.
Проект выложен тут
https://github.com/Andrei-Y/A-trace-map-generator
Папка для скачивания
Интерфейс программы, скрин с фоном стола редим отладки

Нажатие на Buton2 запускает проверяющий алгоритм, который следует по координатам записанным в полях ячеек таблица (поля разделены запятой), ноль в третьем поле — ничего не означает, единица — окончание линии трассировки.
Результат работы проверяющего алгоритма — записывает точки и ставить симовол X в ячейке где обрывается линия.

Пока всё. генератор не доделал до полной развертки от 0 до 180 градусов, так как отвлёкся на пояснительный материал. Генератор быстрый, почти молниеносный, но так-же может сломать мозг. Приведу ядро движка генератора, язык программирование FPC Lazarus.
repeat ////////////////////////////////////////////////////////////////////////////////////////////////// {%REGION 'Engine'} asm JMP p end; p1: StringGrid1.Cells[px^, py^] := IntToStr(pTracerX^) + ',' + IntToStr(pTracerY^) + ',' + '1'; //запись правого столбца p := @p3; goto p4; p2: x1:=x+1; y1:=y+1; StringGrid1.Cells[px^, py^] := IntToStr(px1^) + ',' + IntToStr(py1^) + ',' + '0'; //автоматическое забивание координат в местах скоса трассы p := @p3; goto p4; p3: x1:=x+1; y1:=y; StringGrid1.Cells[px^, py^] := IntToStr(px1^) + ',' + IntToStr(py1^) + ',' + '0'; //автоматическое забивание координат трассы на прямых участках p4: Dec(x); {%ENDREGION} until x < Biger;
Скажу сразу что на этом коде всякие блюстители этикета кода вывихнут себе окончательно себе мозги, так как этот код концептуальный, в нём идея, и её нужно понять прежде чем понять то, как это работает. На C или C++ при распараллеливании этот код будет ещё стремительнее, он и так ничего не делает кроме одного столбца и одной строки из всей карты. Алгоритм Брезенхема не предназначен для текущего ТЗ. Ну и собственно с обработкой изображения я ожидаю такого-же эффекта в сравнении с свёрточными нейросетями. Ядро движка немного пришлось изменит при переходе на интервал 0-90 градусов. Далее ещё будет немного. Код самого движка тут под спойлером, остальной код в папке по ссылке что ранее, если у кого-то будет желание почитать и проверить
Hidden text
repeat repeat asm JMP logic end; logic1: logic := @logic2; goto logic4; logic2: Edge := @Edge3; logic := @logic3; logic3: TracerY := y - steps1; TracerX := 0; if TracerY < 0 then begin TracerX := RazmerX - (stepFrequent * y + znak *trunc(y / (stepsBig+1))); if TracerX < 0 then TracerX := 0; TracerY := 0; end; logic4: steps := stepsBig; repeat if steps = 0 then begin Biger := x - stepRare+1; steps := stepsBig+1; end else begin Biger := x - stepFrequent + 1; end; if Biger < 0 then Biger := 0; repeat ////////////////////////////////////////////////////////////////////////////////////////////////// {%REGION 'Engine'} asm JMP p end; p1: StringGrid1.Cells[px^, py^] := IntToStr(pTracerX^) + ',' + IntToStr(pTracerY^) + ',' + '1'; //запись правого столбца p := @p3; goto p4; p2: x1:=x+1; y1:=y+1; StringGrid1.Cells[px^, py^] := IntToStr(px1^) + ',' + IntToStr(py1^) + ',' + '0'; //автоматическое забивание координат в местах скоса трассы p := @p3; goto p4; p3: x1:=x+1; y1:=y; StringGrid1.Cells[px^, py^] := IntToStr(px1^) + ',' + IntToStr(py1^) + ',' + '0'; //автоматическое забивание координат трассы на прямых участках p4: Dec(x); {%ENDREGION} until x < Biger; asm JMP Edge end; Edge1: //подсчёт шагов в первой строке - других расчётов для первой строки не требуется и знание этого значения нужно для последней строки Inc(steps1); goto Edge3; Edge2: //трассировку начальной ячейки каждого шага последней обрабатываемой строки кроме правого столбца if RazmerX > RazmerY then begin Dec(steps1); TracerX := x - ( znak*sign(trunc((1+sign(y-trunc(y/(stepsBig+1))*(stepsBig+1)-steps)/2))) + stepFrequent * y + znak * trunc(y / (stepsBig+1)))+1; if TracerX<1 then begin TracerY := y - steps1; TracerX := 0; if TracerY < 0 then TracerY := 0; end; end else begin Dec(steps1); TracerY := y - steps1; TracerX := 0; end; p := @p1; goto Edge4; Edge3: p := @p2; Edge4: Dec(steps); until x < 0; Inc(y); yf := y + 1; p := @p1; x := xRazmerX; until y > yRazmerY - 1; Edge := @Edge2; until y > yRazmerY;
Думаю что данный пояснительный материал вносит какую-то ясность о том, как мой алгоритм ничего не делая справляется с своим ТЗ. Ну и хоть какую-то по паланам замены свёртков. Всем спасибо за внимание, думаю и так понятно что быстрых результатов от меня ожидать не стоит, так как всё делаю в послерабочее время, которого совсем мало для подобных вещей.
Всем всего доброго и успехов.
ссылка на оригинал статьи https://habr.com/ru/articles/743758/
Добавить комментарий