Сколько нужно нейронов, чтобы распознать сводку моста?

    История началась, когда я переехал жить на остров Декабристов в Санкт-Петербурге. Ночью, когда мосты развели, этот остров вместе с Васильевским полностью изолирован от большой земли. Мосты при этом нередко сводят досрочно, иногда на час раньше опубликованного расписания, но оперативной информации об этом нигде нет.


    После второго "опоздания" на мосты, я задумался об источниках информации о досрочной сводке мостов. Одним из пришедших в голову вариантов была информация с публичных веб-камер. Вооружившись этими данными и остаточными знаниями со специализации по ML от МФТИ и Яндекса, я решил попробовать решить задачу "в лоб".


    0, Дворцовый

    Во-первых — камеры


    С веб-камерами в Петербурге сейчас не густо, живых направленных на мосты камер у меня получилось найти только две: от vpiter.com и РГГМУ. Несколько лет назад были камеры от Skylink, но сейчас они не доступны. С другой стороны, даже информация по одному только Дворцовому мосту с vpiter.com может быть полезна. И она оказалась полезнее, чем я ожидал — товарищ-парамедик рассказал, что его экипаж "скорой" в том числе благодаря оперативной информации о мостах спас "плюс" двух петербуржцев и одного шведа за неделю.


    Ещё камеры имеют свойство отваливаться, отдавать видеопоток поток в мерзком формате flv, но всё это очень несложно обходится готовыми кубиками. Буквально в две строчки shell-скрипта из видеопотока получается набор кадров, поступающих на классификацию раз в 5 секунд:


    while true; do
        curl --connect-timeout $t --speed-limit $x --speed-time $y http://url/to | \
        ffmpeg -loglevel warning -r 10 -i /dev/stdin -vsync 1 -r 0.2 -f image2 $(date +%s).%06d.jpeg
    done

    Правда, пока никакой классификации нет. Сначала в "сосисочную машинку" нужно положить размеченные данные, поэтому оставим скрипт работать по ночам на неделю, и опционально последуем мантре #ВПитереПить, проверив, что картинки грузятся.


    x = io.imread(fname)

    1, frame


    Во-вторых — обработка изображений


    Так или иначе, раскидав руками и методом деления пополам фотографии по папкам UP, MOVING, DOWN я получил размеченную выборку. Эндрю Ын в своём курсе предлагал хорошую эвристику "если вы на картинке можете отличить объект A от объекта Б, то и у нейронной сети есть шанс". Назовём это эмпирическое правило наивным тестом Ына.


    Первым делом кажется разумным обрезать картинку, чтоб на ней была только секция разводного моста. Телебашня — это красиво, но не выглядит практично. Напишем первую строчку кода, имеющую хоть какое-то отношение к обработке изображений:


    lambda x: x[40:360, 110:630]

    2, bridge


    Я краем уха слышал, что настоящие специалисты берут OpenCV, извлекают фичи и получают пристойное качество. Но, начав читать документацию к OpenCV, мне стало печально — довольно быстро я понял, что в установленный лимит "сделать прототип за пару вечеров" я с OpenCV не уложусь. Но в используемой для чтения jpeg-ов библиотеке skimage по слову feature тоже кое-что находилось. Чем отличается разведённый мост от сведённого? Контуром на фоне неба. Ну так и возьмём skimage.feature.canny, записав себе в блокнот задачу почитать после прототипа о том, как устроен оператор Кэнни.


    lambdax x: feature.canny(color.rgb2gray(x[40:360, 110:630]))

    3, canny


    Едущий над заштрихованной водой троллейбус выглядит довольно красиво. Быть может, скучая и по этой красоте, mkot сожалеет, что переехал из Петербурга Но данная картинка плохо проходит наивный тест Ына — она визуально выглядит шумной. Придётся прочитать документацию дальше первого аргумента функции. Кажется логичным, что если границ слишком много, то можно размазать картинку предлагаемым фильтром Гаусса. Значение по-умолчанию — 1, попробуем его увеличить.


    lambdax x: feature.canny(color.rgb2gray(x[40:360, 110:630]), sigma=2)

    4, sigma


    Это уже больше походит на данные, чем на штрихи простым карандашом. Но есть другая проблема, у этой картинки 166400 пикселей, а кадров за ночь собирается пару-тройку тысяч, т.к. место на диске ноутбука не бесконечное. Наверняка, если взять эти бинарные пиксели как-есть, классификатор просто переобучится. Применим ещё раз метод "в лоб" — сожмём её в 20 раз.


    lambda x: transform.downscale_local_mean(feature.canny(color.rgb2gray(x[40:360, 110:630]), sigma=2), (20, 20))

    5, features


    Это всё ещё похоже на мосты, но и изображение теперь всего 16x26, 416 пикселей. Имея несколько тысяч кадров на таком множестве недо-фич уже не очень страшно учиться и заниматься кросс-валидацией. Теперь неплохо бы выбрать топологию нейронной сети. Когда-то Сергей Михайлович Добровольский, читавший нам лекции по мат. анализу, шутил, что для предсказания исхода выборов президента США достаточно одного нейрона. Кажется, мост — не намного более сложная конструкция. Я попробовал обучить модель логистической регрессии. Как и ожидалось, мост устроен не намного сложнее выборов, и модель даёт вполне пристойное качество с двумя-тремя девятками на всяких разных метриках. Хотя такой результат и выглядит подозрительно (наверняка в данных всё плохо с мультиколлинеарностью). Приятным побочным эффектом является то, что модель предсказывает вероятность класса, а не сам класс. Это позволяет нарисовать забавный график, как процесс разводки Дворцового моста выглядит для "нейрона" робота в реальном времени.


    6, разводка


    Остаётся прикрутить к этой конструкцию push-уведомления и какой-нибудь интерфейс, позволяющий посмотреть на мост глазами, если классификатор дал сбой. Первое оказалось сделать проще всего с помощью Telegram-бота, который отправляет уведомления в канал @SpbBridge. Второе — из костыликов, bootstrap и jquery сделать веб-мордочку с прямым эфиром.


    Зачем я это всё написал?


    Хотел напомнить, что у каждой проблемы есть простое, всем понятное неправильное решение, которое тем не менее может быть практичным.


    И ещё, пока я писал этот текст, дождь, кажется, смыл в Неву камеру, которая смотрела на Дворцовый мост вместе с сервером vpiter.tv, о чём робот оперативно и сообщил.


    7, nomorecam


    Я буду рад, если вы во имя отказоустойчивости захотите поделиться своей веб-камерой, которая смотрит на какой-нибудь разводной мост. Вдруг, например, вы работаете в СПб ГКУ «ГМЦ».


    P.S.: А вот и версия с несколько большим числом нейронов, но более автоматическим извлечением фич.

    Метки:
    Поделиться публикацией
    Комментарии 52
    • +1
      Я достаточно далек от нейронных сете, но все же:
      Что показывает синий график? Ошибку? А как изменяется синий график при сведении и разведении моста?
      Может быть применив какую-либо фильтрацию удастся определить, что мост начал сводиться или разводиться? Это частично позволит решить проблему глюков видеокамеры. Ведь зная предыдущее состояние моста (а если синий график зеркально отражается относительно вертикальной оси при сведении/разведении моста то можно и без этого) и текущее направление движения, а так же среднее время сведения моста + запас можно с 90% предсказать текущее положение (открыт или закрыт)
      • 0
        Синий график показывает вероятность третьего класса «мост в движении». Потом я отказался от данного класса, т.к. в интерфейсе его всё равно не существует. Мусор с камеры оказалось проще всего отслеживать по баннеру, т.к. обычно мусор связан с развалившимся видеопотоком.
        По-честному ещё стоит, конечно, отслеживать, что камеру не повернули, и что она смотрит в тот же ракурс, на котором бот обучался.

        Вопрос про запас и вероятность я не понял. Глюки видеокамеры обычно выражаются либо в нескольких мусорных кадрах либо в полной её недоступности.
      • 0
        Очень интересный пост.
        У меня пара вопросов:
        1. Информационный робот всё время работал на вашем ноутбуке? Не было желания перенести его на выделенный сервер?
        2. Видно ли мост Александра Невского на фоне неба? Просто фото на «веб-мордочке» довольно мыльное, я мост не сразу нашел, чего уж про робота говорить)
        • 0
          1. Нет, на linode живёт робот, который пишет в канал, на ноутбуке жила только часть подготовки данных. Сбор данных жил, конечно, на кусочке отдельной железки с достаточным местом на диске, но и там пришлось думать о том, чтоб весь диск не забить видеопотоком :-)
          2. Плохо, но обычно видно. Если очень интерено, могу поделиться датасетом.
          • 0
            2. Если нет желания смотреть датасет, то качество на мосту Александра Невского можно посмотреть ещё в истории Telegram канала.
          • 0
            Очень интересная статья, спасибо!

            Я тоже далёк от нейронных сетей, поэтому интересно — не будет ли эффективнее не «изучать» картинку как таковую, а сличать картинки в периоде 1-3-5-10 минут (или сколько там уходит на сведение моста) и определять что-то вроде «на протяжении нескольких часов картинка была одна, а сейчас произошло изменение, скорее всего, мост свели»?
            • 0
              Изменение яркости по времени суток, по погоде, дождь, туман, ветер, фонари, засвет, блики, автотранспорт, птицы. Картинка вряд ли будет статичной долгий период времени.
              • 0
                Всё что надо — это определить это дерево контуров, описывающих «изображение похожее на мост». Далее определить соответствующее множество сопряженности векторов относящихся к «изображению похожему на мост».
                • 0
                  Про яркость у меня есть смешная картинка «Рассвет над мостом Александра Невского». Когда я разбирался с качеством камеры, которая смотрела на этот мост, я заметил, что она имеет два режима: ночной и дневной. Эти два режима хорошо видны на следующей картинке:

                  Я взял RGB значения цвета неба над мостом в разные моменты времени за всю ночь и нарисовал их точками на диаграмме. Хорошо видно, что цвет неба имеет разрыв — в какой-то момент он меняется скачком. Кажется, этот момент соответствует выключению городского освещения :-)
              • +1
                Безотносительно распознавания изображений: мосты сводят раньше при окончании фактической навигации, т.е. когда все суда, которые хотели пройти, прошли. Переговоры между судами и диспетчерами мостов ведутся по радиоканалу. Я сейчас не вспомню на какой частоте, но можно купить «речную» рацию и слушать на одном из первых каналов (опять же, точно не помню, но скорее всего 2, 3 или 5 канал). Последнее судно сообщает, что оно было последним и мост можно сводить.
                Возможно, оперативную информацию можно узнавать в Мостотресте. К середине дня они уже должны представлять сколько судов пойдет ночью.
                • 0
                  Да, в Мостотресте по телефону теоретически что-то можно узнать. По крайней мере на сайте телефон есть.
                  С рацией есть тонкость — если мосты не разводят из-за непогоды, то бот об этом может принять решение и сказать, «что-то мост не развели по расписанию», а в радиоэфире будет, наверное, тишина.
                • 0
                  Вместо размывания по Гауссу, я бы применил Median.
                  • 0
                    Гаусс лежал готовый в коробке и оказался достаточно хорош :-)
                    • +1
                      Median тоже не экзотика
                      • 0
                        Я хотел сказать, что Гаусс в коробке с `feature.canny`. А из-за каких свойств взяли бы именно median?
                        • +1
                          Очень просто: Гаусс размывает всё подряд, а Median давит мелкий мусор, пытаясь оставлять имеющиеся четкие переходы (местами, правда, он их сам дорисовывает).
                  • 0
                    Чувствую, что камера познала Хабраэффект… Жаль парамедиков(а точнее людей, которых никто, возможно, не спасёт), снова остались без инфы
                    • +2

                      Вообще, удивительно что никто ещё не сделал сервис, показывающий текущее состояние мостов. Ведь это давняя и насущная проблема.

                      • –1
                        Технологии крутые, но применение скорее как хороший пример
                        • 0
                          Хотя не, кроме мостов это могут быть шлагбаумы
                        • 0
                          На мой взгяд проще разведенность моста отслеживать по наличию и отсутствию потока машин — к слову машина дает перемещающуюся яркую точку — а движущиеся объекты могут быть отслежены без применения ненйронных сетей
                          • 0
                            А если потока машин нет и мост не разведен и мчится кораблик? Страшно если ваше предложение используют.
                            • 0
                              см ответ ниже (а также, между прочим, как ни странно, поток через питерские мосты всегда есть, особенно в предразводное время )
                              • 0
                                сомнительное утверждение. Особенно после сведения, особенно, если свели раньше срока. Проедет пара машин которые ждут и все.
                            • 0
                              Практически это мне кажется более сложной задачей, т.к. надо смотреть на динамику изображений и анализировать, наверное, каждый кадр (а не каждый 50-ый). Если есть интерес, могу предложить неразмеченный датасет с видео Литейного моста, там как раз камера смотрит на участок дороги сбоку, а разводной секции не видно.
                              • 0
                                Во-первых, я уже делал один проект про опасное сближение автомобилей (фиксация видеорегистратором) — поэтому в теме
                                Во-вторых при заданной камере движение объектов задано по кадру (кораблики по мосту не едут)
                                В-третьих, задача фиксация движения давно решена в системах безапасности — на всякий случай даю ссылку http://www.codeproject.com/Articles/10248/Motion-Detection-Algorithms
                                • +4
                                  > я уже делал один проект про опасное сближение автомобилей
                                  Вот поэтому вы и предлагаете нелепый вариант, про обнаружение автомобилей, т.к. вы это уже делали.

                                  Однако это выглядит нелогичным и ненужным усложнением. Определение по картинке — поднят ли мост, или опущен выглядит гораздо проще, чем определение — есть ли автомобили, и автомобили ли это. Тем более, машин может не быть ВООБЩЕ, а мост стоять (особенно, в предразводное время, т.к. все кому надо уже проехали, остались те, кто рассчитывает на авось).
                                  • 0
                                    Так и напрашивается об'единение этих двух методов. Ибо, толку мне от того, что мост сведён, если его через 10 минут разводить будут и уже входы/выходы заблокированы. Или же мост полностью перекрыт, но сведён (как было, когда его ремонтировали).
                                    • 0
                                      если его через 10 минут разводить будут

                                      Обычно въезд на мост закрывают аккурат по расписанию. Польза робота только в ночной сводке.


                                      перекрыт, но сведён (как было, когда его ремонтировали)

                                      Об этом обычно есть оперативные данные, которые навигаторы публикуют. Вот, например, сейчас ремонтируют Тучков мост и на мосту в Яндекс.Картах нарисовано одностороннее движение:


                                      • 0
                                        Вчера, к примеру, в полдень Дворцовый разводили на пару часов. Правда там и без него не проехать было.
                                        • 0

                                          Да, обещали развести с 11-30 до 13-30. В 11-30 и развели, но где-то в 12-40 уже начали сводить, а потом камера выключилась.

                                      • 0
                                        Так ведь задача сводится к определению — раздвинут ли мост, или сдвинут, а не заблокированы ли входы/выходы. Для этого, естественно, нужно иметь камеру в другом месте и решать задачку посложнее.
                                    • 0
                                      Я ни с одним из этих утверждений не спорил. Более того, именно так я и планировал решать задачу с Литейным мостом, если не получится найти более пристойного источника данных, чем следы от стоп-сигналов :)

                                      Со следами от стоп-сигналов вот ещё какая беда есть: если машина ошибается (а я не слишком высокого мнения о своих умениях в задачах распознавания), то по контуру моста человек может перепроверить машину. По следам стоп-сигналов это сложнее.
                                • 0
                                  Очень интересная статья и красиво подобранный материал. Сразу вспомнил как с друзьями составляли карту открытия и закрытия движения по мостам для максимально быстрого «объезда» ночью по максимальному количеству.
                                  • +1
                                    Где код?
                                    • 0

                                      В статье, там примерно четыре строчки кода :-) Вся содержательная часть — это преобразование картинки в вектор фич, которыми потом кормится классификатор с дефолтными параметрами. Но несложно продублировать:


                                      def load_dvorcovy_vpiter_admiral(fname):
                                          feat = feature.canny(color.rgb2gray(io.imread(fname)[40:360, 110:630]), sigma=2)
                                          feat = transform.downscale_local_mean(feat, (20, 20))
                                          assert feat.shape == (16, 26)
                                          return feat.reshape((np.prod(feat.shape), ))
                                    • 0
                                      • А всё-таки хотелось бы увидеть какие-нибудь циферки, оценивающие точность модели. LogLoss, accuracy, ROC_AUC, и т. д,
                                      • Если поделитесь размеченными изображениями(больше — лучше) готов поиграться со свёрточными сетями, и рассказать, что из них для этой задачи можно получить.(ну и код, понятно, выложить)
                                      • +1
                                        Автор указал, что у него «на всяких разных метриках» (подозреваю, что precision) получается 0,99. Картинка статичная и на самом деле остальные метрики не очень то нужны.

                                        Интересно поиграться с нейронными сетями было бы в случае, если была бы размеченная база из многих мостов (желательно с разными конфигурациями). Но при этом не забывайте, что один из немногих плюсов свёрточных нейросетей для компьютерного зрения — это устойчивость к translation, rotation и shifting. Если у вас примерно одинаковые картинки с примерно одинакового ракурса — нет особенного смысла их использовать.
                                        • +1
                                          С Дворцовым мостом всё было ровно так, как я написал, первая же попытка дала пристойный классификатор, который показывал 0.99+ по F-мере. Там выборка была не сбалансированная, собранная за рабочую неделю и у меня, если честно, она уже потерялась. :-)

                                          С мостом Александра Невского с наскоку не получилось, там качество картинки на голову хуже. Там я составлял выборку следующим образом: захватывал видеопоток с 0:00 до 8:00, нарезал его на блоки по 5 минут, размечал блоки на UP/DOWN/MOV, где в MOV попадали два блока соответствующие началу и концу движения моста. Проще говоря, факт разводки порождал такую последовательность блоков …, down, down, down, mov, mov, up, up, up, …. После этого из всех блоков одного класса я брал ~1000 кадров за кажду ночь и превращал их в фичи.
                                          Качество оценивал разделяя выборку по времени, первые 20 дней в train, 5 в test и 5 в валидационную выборку. По валидации настраивал кроп. Полагаю, разный кроп даёт заметно разные результаты, т.к. рядом с мостом стоит фонарь, который светит прямо в камеру, и из-за этого пороги у `canny` могут получаться бестолковые. Наверное, вместо кропа можно было точно так же пороги подбирать. Когда делал кросс-валидацию по дням, оценка качества получалась завышенной. С одной стороны это понятно (мы учимся на данных «из будущего»), с другой — не очень (чем данные «из будущего» отличаются-то). Этот процесс тоже дал F-меру 0.99+.

                                          Блоки mov я для обучения текущей модели не использовал, а использовал их только для рисования графика «процесс разводки глазами классификатора», чтоб визуально оценить скорость реакции модели на изменение моста. По этому графику я настраивал размер «кворума». Робот отсылает сообщение в telegram только тогда, когда в последних N кадрах было больше двух третей голосов за одно из двух состояний. Этим действием я сглаживал возможный шум в момент переходного процесса — спамящий о своей неуверенности робот мне довольно быстро показался весьма неприятным.

                                          Про датасеты. Размеченных руками у меня сейчас только 3 Gb jpeg-ов Александра Невского, с 2016-06-19 по 2016-07-24. Можно породить ещё разметку классификатором для Ал.Не. и Дворцового за последние две недели, там flv-шки и их сильно больше: 28G с Дворвоцого и 53G с Александра Невского. Есть ещё поворотная камера, которая смотрит на мост лейтенанта Шмидта (две задачи по цене одной — понять, что сейчас на камере нужный мост и понять его состояние) и Литейный, по которому машины ездят, а разводной секци не видно. Что из этого интересно?
                                          • 0
                                            Лучше всего и побольше. :) Вы выложите, что есть размеченного на данный момент. 3GB jpeg-ов о мосте Александра Невского — с этим однозначно можно работать.
                                      • 0
                                        Так у вас тут статичная картинка, зачем вам тут вообще машинное обучение? На вашем питоне у вас за 10 минут бы получился отличный rule-based с 100% точностью.
                                        • +1
                                          Чтобы не тратить время на составление правил :-)
                                          Машины должны работать. Люди должны думать.
                                          • +1

                                            Есть мнение, что применив skimage.feature.canny, вы уже сделали за машину большую часть работы)

                                        • 0
                                          Может как-то брать инфу у кораблей стоящих на рейде в Рыбацком. По рации или ещё как?
                                          • +2
                                            Переделайте в скрипте домен с vpiter.tv на 195.154.163.4

                                            Я вряд ли буду его продлевать в этом году.
                                            • 0
                                              Спасибо за информацию, я vpiter.tv в коде для читаемости оставлял, ещё думал, почему же в вёрстке IP-адрес напрямую зашит.

                                              Кстати, нет планов у вас робота моего забанить по какой-либо причине? :-) Я писал о нём на info at vpiter.com, но ответа там никакого не получил, и решил, «если баннер не вырезать и ссылку оставить, то, наверное, пока ок, а там видно будет».
                                              • +1
                                                Там немного странная ситуация в компании. Писать на указанный вами адрес бесполезно.

                                                Лучше сразу мне в личку.

                                                Банить робота не планирую. Кстати, существует более простой способ определить, работает ли какая-нибудь камера или нет. Если надо, предоставлю вам доступ к такой информации.
                                            • 0
                                              Хорошая статья.
                                              И главное — хорошая демонстрация — быстро разработанному «на коленке» вполне себе нормальному сервису… который возможно будет жить не один год и приносить реальную помощь людям.

                                              А критика — интересна как идеии на другие решения и апгрейд существующего.
                                              • +3
                                                Нейронные сети это, конечно, стильно, модно, молодёжно, но в данном конкретном случае не проще было бы сделать преобразование Хафа от картинки и посмотреть отношение интенсивностей в неких областях, соответсвующих прямым с определённым углом наклона?

                                                image
                                                • +1
                                                  стильно, модно, молодёжно

                                                  Так речь как раз о том, что можно обойтись простейшей линейной моделью, которая "нейронная сеть" лишь формально (один нейрон).


                                                  преобразование Хафа

                                                  Я о таком слове попросту не знал, никогда не занимался обработкой изображений, в этом и был посыл статьи — можно сделать ненулевую пользу почти не разбираясь, абсолютно "на коленке" :-)


                                                  Но да, когда прикручивал мост Александра Невского, я думал об извлечении более сложных фич, собирался начать как раз с угла наклона — но тот же способ с canny(rgb2blue(crop(raw))) успел дать пристойные результаты попросту на большем количестве данных раньше, чем я в режиме "по вечерам" успел разобраться в вопросе получше.

                                                • 0
                                                  Проще было бы не веб-камеры анализирывать, а скрин-шоты яндекс-карт.

                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                  Интересные публикации