Данная публикация служит пояснительным материалом к предыдущей, а так-же самостоятельной для тех, кто читает по данной теме мои публикации впервые.
Сначала о том, каким алгоритмом я планирую заменить в своих работах свёрточные нейросети. Чтобы это работало быстро - нужны карты трассировок. Линии трассировок на карте расположены параллельно под определённым углом на каждой карте - так и происходит условная поляризация. Генератор карт работает быстро и генерирует он карты трассировок направленных прямыми линиями, обрыв каждой линии он отмечает в данных. То-есть сначала запускатеся генератор карт и генерирует картинку, данная анимация существенно отличается от работы генератора и показывает только его ТЗ - в каждом пикселе карты записать координаты следующего пиксела и обозначить в данных окончание каждой линии. Изображения я взял небольшие, но тем не менее файлы анимации достаточно увесистые. Допустим что обрабатываемые изображения будет 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
Папка для скачивания
generator_0-90_deg3.zip
Интерфейс программы, скрин с фоном стола редим отладки
Нажатие на 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;
Думаю что данный пояснительный материал вносит какую-то ясность о том, как мой алгоритм ничего не делая справляется с своим ТЗ. Ну и хоть какую-то по паланам замены свёртков. Всем спасибо за внимание, думаю и так понятно что быстрых результатов от меня ожидать не стоит, так как всё делаю в послерабочее время, которого совсем мало для подобных вещей.
Всем всего доброго и успехов.