«Поляризация» машинному зрению вместо свёрточных нейросетей и чем отличается мой генератор карт от алгоритма Брезенхема

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Данная публикация служит пояснительным материалом к предыдущей, а так-же самостоятельной для тех, кто читает по данной теме мои публикации впервые.

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

Думаю что данный пояснительный материал вносит какую-то ясность о том, как мой алгоритм ничего не делая справляется с своим ТЗ. Ну и хоть какую-то по паланам замены свёртков. Всем спасибо за внимание, думаю и так понятно что быстрых результатов от меня ожидать не стоит, так как всё делаю в послерабочее время, которого совсем мало для подобных вещей.

Всем всего доброго и успехов.

Источник: https://habr.com/ru/articles/743758/


Интересные статьи

Интересные статьи

История про «восстание машин» давно знакома всем любителям научной фантастики, но после взрывного роста возможностей нейросетевых языковых моделей (вроде ChatGPT) об этом риске заговорили и вполне сер...
Яндекс.Маркет принял на работу робота-силача, который может перевозить до 800 кг.
Недавно я обратил внимание на одно заблуждение, связанное с генераторами, а точнее — с тем, как они позволяют экономить память. Такое ощущение, что многие воспринимают генераторы как инструмент, кот...
В августе Московский метрополитен запустил тестирование виртуальной «Тройки» на Московском транспорте. Суть технологии в том, что для оплаты транспорта вы можете использовать смартфон на Android, в пл...
Введение Цель этого проекта — создание клона движка DOOM, использующего ресурсы, выпущенные вместе с Ultimate DOOM (версия со Steam). Он будет представлен в виде туториала — я не хочу добив...