Автор: Ромакин Д.В.
Привет всем читателям Habrahabr! В продолжение первой статьи я хочу поделиться с вами как я решил проблему нахождения ребер графа. Но перед этим я хотел бы напомнить про описание системы, чтобы не возникло путаницы в последовательности.
Программа разбита в несколько этапов:
- На вход поступает изображение.
- Запускается скрипт для подготовки данных (удаление шумов и инвертирование цветов в 1 канал).
- Поиск вершин VertexSearch. На изображении с помощью каскадов Хаара ищем вершины графа, затем запускаем фильтрацию, которая позволяет уменьшить количество ошибок от каскадов.
- Поиск ребер EdgeSearch. Определяем координаты пересечения ребер и вершин.
- Алгоритм Tracker. Находим ребра и какие вершины эти ребра соединяют.
- Составляется удобный формат для дальнейшей визуализации.
Для того, чтобы определить какие вершины графа соединены между собой, необходимо знать:
- Область начала ребра (линии)
- Область пересечения ребра (линии) с другими
Были протестированы 2 алгоритма для поиска области начал ребра и области пересечения ребра с другими:
Ни один из предложенных алгоритмов не подходит, поэтому был создан свой алгоритм с названием Tracker, который "пробегается" по пикселям ребер графа на изображении.
Tracker работает в 3 этапа:
-
Поиск области начала ребер;
-
Проход по пикселям ребер графа на изображении;
-
Определение принадлежит ли ребро данной вершине.
В данном случае используется метод вспомогательных прямоугольников, пересечение с ребрами которых позволяет найти область начала ребра, а впоследствии как область начала движения для Tracker.
- На вход алгоритму Canny подаем обработанное изображение;
- С помощью свертки соединяем 2 линии после алгоритма Canny;
- Уменьшаем размер линии до малого числа пискселей, чтобы не было разрыва;
- То множество пикселей, которое попало в область дополнительных прямоугольников - удаляется, сохраняя множества пикселей, которые попали на пересечения самых больших для каждой вершины прямоугольников и ребер графа;
- Белая часть - это множество пикселей пересечения, поэтому вычисляем центральный пиксель пересечения для каждой области;
- Получаем области (окружности) начала ребра, центрами которых являются центральные пиксели;
- После удаления в 5 пукте находим ребра графа (множество пикселей), по которым будет передвигаться Tracker.
P.s. Дополнительные прямоугольники строятся на основе выделенной области Хаара.
Ниже можно увидеть окончательный результат 7 шагов:

Для того, чтобы подтвердить, что ребро принадлежит данным двум вершинам, необходимо, чтобы Tracker "пробежался" от одной вершины к другой, хотя бы один раз.
Движение основано на пересечении ребра графа (траекторией движения Tracker) с окружностью, которая строится, используя координаты центрального пикселя.
- Используя координаты центрального пикселя области начала ребра (линии), строится красная окружность;
- Находится пересечение красной окружности с пикселями ребра (дороги) Tracker;
- Множество пикселей пересечения приводится к одному пикселю пересечения, который является центральным пикселем для новой окружности;
- Рисуется новая окружность, центром которой является новый центральный пиксель пересечения;
- Циклически выполняем 2-4, до тех пор, пока не встретим:
- Другую точку начала линии;
- Последнюю область (последний пиксель) ребра (окончание дороги).
| Движение Tracker | Траектория движения Tracker |
|---|---|
![]() |
![]() |
Включается алгоритм Tracker Shoot, который проверяет соединено ли ребро с вершиной графа (красным прямоугольником Хаара). Более подробно в главе про Tracker Shoot.
Данный алгоритм начинает работать после встречи одно из условий 5 этапа алгоритма движения Tracker. Рассмотрим работу на примере.
| Tracker Shoot | Tracker Shoot Zoom |
|---|---|
![]() |
![]() |
Слева на картинке изображен Tracker Shoot, а справа - увеличенная картинка Tracker Shoot.
- При выходе на 5 этапе из алгоритма движения Tracker в обоих случаях сохраняются координаты центральных пикселей данной окружности и предыдущей (если встретили другую точку начала - белая и красная окружности, если последнюю область ребра - 2 последние красные окружности).
- Используя выражение прямой
и имея 2 пары координат центральных пикселей, находим угловой коэффициент
для данного ребра.
- Используя координаты ближайших вершин прямоугольника относительно координат последнего центрального пикселя (белой или красной окружности в зависимости от выхода на 5 этапе) получаем границы значений углового коэффициента, при которых данное ребро соединено с вершиной графа.
- Если угловой коэффициент
лежит в границах значений, то ребро соединяет 2 вершины, иначе - ребро огибает вершину.
P.s. Если коэффициент находится в допустимой области, то картинка будет такая же как сверху.
- При разрыве линии алгоритм движения Tracker может остановиться, т.к. в провал попадет точка пересечения окружности с траекторией Tracker;
- При пересечении линий алгоритм не работает;
- Определение точек начала линии иногда дает плохой результат, если какая-либо линия проходит рядом с областью вершины (пример ниже);
| Original image -> Preprocessing | Vertex Search with action | Find Start | Tracker | Result |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
- Для корректной работы этапа подготовки необходимо указывать индекс для фотографии, которая поступает на вход;
- Отсутсвие многопоточности, для поиска вершин и Tracker.
| Original image -> Preprocessing | Vertex Search | Find Start | Tracker | Result |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
| Original image -> Preprocessing | Vertex Search with action | Find Start | Tracker | Result |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
В двух статьях были рассмотрены все этапы оцифровки графов, проблемы и результаты работы. Надеюсь читатели нашли в них что-то полезное. Еще я надеюсь на то, что удалось показать, что данная задача вполне решаемая.
- https://www.pyimagesearch.com/2017/08/21/deep-learning-with-opencv/
- https://www.pyimagesearch.com/category/object-detection/
- https://habr.com/ru/post/309508/
- https://habr.com/ru/post/312450/
- https://www.asozykin.ru/courses/nnpython-intro
- https://proglib.io/p/neural-nets-guide/
Весь актуальный код можно найти в репозитории и потестировать, если будет желание. Мой Github







































