История началась, когда я переехал жить на остров Декабристов в Санкт-Петербурге. Ночью, когда мосты развели, этот остров вместе с Васильевским полностью изолирован от большой земли. Мосты при этом нередко сводят досрочно, иногда на час раньше опубликованного расписания, но оперативной информации об этом нигде нет.
После второго "опоздания" на мосты, я задумался об источниках информации о досрочной сводке мостов. Одним из пришедших в голову вариантов была информация с публичных веб-камер. Вооружившись этими данными и остаточными знаниями со специализации по ML от МФТИ и Яндекса, я решил попробовать решить задачу "в лоб".
Во-первых — камеры
С веб-камерами в Петербурге сейчас не густо, живых направленных на мосты камер у меня получилось найти только две: от 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)
Во-вторых — обработка изображений
Так или иначе, раскидав руками и методом деления пополам фотографии по папкам UP, MOVING, DOWN я получил размеченную выборку. Эндрю Ын в своём курсе предлагал хорошую эвристику "если вы на картинке можете отличить объект A от объекта Б, то и у нейронной сети есть шанс". Назовём это эмпирическое правило наивным тестом Ына.
Первым делом кажется разумным обрезать картинку, чтоб на ней была только секция разводного моста. Телебашня — это красиво, но не выглядит практично. Напишем первую строчку кода, имеющую хоть какое-то отношение к обработке изображений:
lambda x: x[40:360, 110:630]
Я краем уха слышал, что настоящие специалисты берут OpenCV, извлекают фичи и получают пристойное качество. Но, начав читать документацию к OpenCV, мне стало печально — довольно быстро я понял, что в установленный лимит "сделать прототип за пару вечеров" я с OpenCV не уложусь. Но в используемой для чтения jpeg-ов библиотеке skimage по слову feature
тоже кое-что находилось. Чем отличается разведённый мост от сведённого? Контуром на фоне неба. Ну так и возьмём skimage.feature.canny
, записав себе в блокнот задачу почитать после прототипа о том, как устроен оператор Кэнни.
lambdax x: feature.canny(color.rgb2gray(x[40:360, 110:630]))
Едущий над заштрихованной водой троллейбус выглядит довольно красиво. Быть может, скучая и по этой красоте, mkot сожалеет, что переехал из Петербурга Но данная картинка плохо проходит наивный тест Ына — она визуально выглядит шумной. Придётся прочитать документацию дальше первого аргумента функции. Кажется логичным, что если границ слишком много, то можно размазать картинку предлагаемым фильтром Гаусса. Значение по-умолчанию — 1
, попробуем его увеличить.
lambdax x: feature.canny(color.rgb2gray(x[40:360, 110:630]), sigma=2)
Это уже больше походит на данные, чем на штрихи простым карандашом. Но есть другая проблема, у этой картинки 166400 пикселей, а кадров за ночь собирается пару-тройку тысяч, т.к. место на диске ноутбука не бесконечное. Наверняка, если взять эти бинарные пиксели как-есть, классификатор просто переобучится. Применим ещё раз метод "в лоб" — сожмём её в 20 раз.
lambda x: transform.downscale_local_mean(feature.canny(color.rgb2gray(x[40:360, 110:630]), sigma=2), (20, 20))
Это всё ещё похоже на мосты, но и изображение теперь всего 16x26, 416 пикселей. Имея несколько тысяч кадров на таком множестве недо-фич уже не очень страшно учиться и заниматься кросс-валидацией. Теперь неплохо бы выбрать топологию нейронной сети. Когда-то Сергей Михайлович Добровольский, читавший нам лекции по мат. анализу, шутил, что для предсказания исхода выборов президента США достаточно одного нейрона. Кажется, мост — не намного более сложная конструкция. Я попробовал обучить модель логистической регрессии. Как и ожидалось, мост устроен не намного сложнее выборов, и модель даёт вполне пристойное качество с двумя-тремя девятками на всяких разных метриках. Хотя такой результат и выглядит подозрительно (наверняка в данных всё плохо с мультиколлинеарностью). Приятным побочным эффектом является то, что модель предсказывает вероятность класса, а не сам класс. Это позволяет нарисовать забавный график, как процесс разводки Дворцового моста выглядит для "нейрона" робота в реальном времени.
Остаётся прикрутить к этой конструкцию push-уведомления и какой-нибудь интерфейс, позволяющий посмотреть на мост глазами, если классификатор дал сбой. Первое оказалось сделать проще всего с помощью Telegram-бота, который отправляет уведомления в канал @SpbBridge. Второе — из костыликов, bootstrap и jquery сделать веб-мордочку с прямым эфиром.
Зачем я это всё написал?
Хотел напомнить, что у каждой проблемы есть простое, всем понятное неправильное решение, которое тем не менее может быть практичным.
И ещё, пока я писал этот текст, дождь, кажется, смыл в Неву камеру, которая смотрела на Дворцовый мост вместе с сервером vpiter.tv, о чём робот оперативно и сообщил.
Я буду рад, если вы во имя отказоустойчивости захотите поделиться своей веб-камерой, которая смотрит на какой-нибудь разводной мост. Вдруг, например, вы работаете в СПб ГКУ «ГМЦ».
P.S.: А вот и версия с несколько большим числом нейронов, но более автоматическим извлечением фич.
Комментарии (52)
SnikeMK
31.07.2016 17:07Очень интересный пост.
У меня пара вопросов:
1. Информационный робот всё время работал на вашем ноутбуке? Не было желания перенести его на выделенный сервер?
2. Видно ли мост Александра Невского на фоне неба? Просто фото на «веб-мордочке» довольно мыльное, я мост не сразу нашел, чего уж про робота говорить)darkk
31.07.2016 17:251. Нет, на linode живёт робот, который пишет в канал, на ноутбуке жила только часть подготовки данных. Сбор данных жил, конечно, на кусочке отдельной железки с достаточным местом на диске, но и там пришлось думать о том, чтоб весь диск не забить видеопотоком :-)
2. Плохо, но обычно видно. Если очень интерено, могу поделиться датасетом.
darkk
31.07.2016 17:462. Если нет желания смотреть датасет, то качество на мосту Александра Невского можно посмотреть ещё в истории Telegram канала.
prohodil_mimo
31.07.2016 17:52Очень интересная статья, спасибо!
Я тоже далёк от нейронных сетей, поэтому интересно — не будет ли эффективнее не «изучать» картинку как таковую, а сличать картинки в периоде 1-3-5-10 минут (или сколько там уходит на сведение моста) и определять что-то вроде «на протяжении нескольких часов картинка была одна, а сейчас произошло изменение, скорее всего, мост свели»?akirsanov
31.07.2016 18:18Изменение яркости по времени суток, по погоде, дождь, туман, ветер, фонари, засвет, блики, автотранспорт, птицы. Картинка вряд ли будет статичной долгий период времени.
babylon
31.07.2016 19:17Всё что надо — это определить это дерево контуров, описывающих «изображение похожее на мост». Далее определить соответствующее множество сопряженности векторов относящихся к «изображению похожему на мост».
darkk
01.08.2016 00:32Про яркость у меня есть смешная картинка «Рассвет над мостом Александра Невского». Когда я разбирался с качеством камеры, которая смотрела на этот мост, я заметил, что она имеет два режима: ночной и дневной. Эти два режима хорошо видны на следующей картинке:
Я взял RGB значения цвета неба над мостом в разные моменты времени за всю ночь и нарисовал их точками на диаграмме. Хорошо видно, что цвет неба имеет разрыв — в какой-то момент он меняется скачком. Кажется, этот момент соответствует выключению городского освещения :-)
fralik
31.07.2016 18:17+1Безотносительно распознавания изображений: мосты сводят раньше при окончании фактической навигации, т.е. когда все суда, которые хотели пройти, прошли. Переговоры между судами и диспетчерами мостов ведутся по радиоканалу. Я сейчас не вспомню на какой частоте, но можно купить «речную» рацию и слушать на одном из первых каналов (опять же, точно не помню, но скорее всего 2, 3 или 5 канал). Последнее судно сообщает, что оно было последним и мост можно сводить.
Возможно, оперативную информацию можно узнавать в Мостотресте. К середине дня они уже должны представлять сколько судов пойдет ночью.darkk
01.08.2016 00:35Да, в Мостотресте по телефону теоретически что-то можно узнать. По крайней мере на сайте телефон есть.
С рацией есть тонкость — если мосты не разводят из-за непогоды, то бот об этом может принять решение и сказать, «что-то мост не развели по расписанию», а в радиоэфире будет, наверное, тишина.
Moskus
31.07.2016 19:41Вместо размывания по Гауссу, я бы применил Median.
darkk
01.08.2016 00:39Гаусс лежал готовый в коробке и оказался достаточно хорош :-)
Moskus
01.08.2016 00:45+1Median тоже не экзотика
darkk
01.08.2016 00:48Я хотел сказать, что Гаусс в коробке с `feature.canny`. А из-за каких свойств взяли бы именно median?
Moskus
01.08.2016 01:31+1Очень просто: Гаусс размывает всё подряд, а Median давит мелкий мусор, пытаясь оставлять имеющиеся четкие переходы (местами, правда, он их сам дорисовывает).
TimsTims
31.07.2016 22:20Чувствую, что камера познала Хабраэффект… Жаль парамедиков(а точнее людей, которых никто, возможно, не спасёт), снова остались без инфы
encyclopedist
31.07.2016 22:32+2Вообще, удивительно что никто ещё не сделал сервис, показывающий текущее состояние мостов. Ведь это давняя и насущная проблема.
sbnur
31.07.2016 23:03На мой взгяд проще разведенность моста отслеживать по наличию и отсутствию потока машин — к слову машина дает перемещающуюся яркую точку — а движущиеся объекты могут быть отслежены без применения ненйронных сетей
babylon
31.07.2016 23:21А если потока машин нет и мост не разведен и мчится кораблик? Страшно если ваше предложение используют.
darkk
01.08.2016 00:43Практически это мне кажется более сложной задачей, т.к. надо смотреть на динамику изображений и анализировать, наверное, каждый кадр (а не каждый 50-ый). Если есть интерес, могу предложить неразмеченный датасет с видео Литейного моста, там как раз камера смотрит на участок дороги сбоку, а разводной секции не видно.
sbnur
01.08.2016 08:03Во-первых, я уже делал один проект про опасное сближение автомобилей (фиксация видеорегистратором) — поэтому в теме
Во-вторых при заданной камере движение объектов задано по кадру (кораблики по мосту не едут)
В-третьих, задача фиксация движения давно решена в системах безапасности — на всякий случай даю ссылку http://www.codeproject.com/Articles/10248/Motion-Detection-AlgorithmsTimsTims
01.08.2016 10:02+4> я уже делал один проект про опасное сближение автомобилей
Вот поэтому вы и предлагаете нелепый вариант, про обнаружение автомобилей, т.к. вы это уже делали.
Однако это выглядит нелогичным и ненужным усложнением. Определение по картинке — поднят ли мост, или опущен выглядит гораздо проще, чем определение — есть ли автомобили, и автомобили ли это. Тем более, машин может не быть ВООБЩЕ, а мост стоять (особенно, в предразводное время, т.к. все кому надо уже проехали, остались те, кто рассчитывает на авось).vlivyur
01.08.2016 13:15Так и напрашивается об'единение этих двух методов. Ибо, толку мне от того, что мост сведён, если его через 10 минут разводить будут и уже входы/выходы заблокированы. Или же мост полностью перекрыт, но сведён (как было, когда его ремонтировали).
darkk
01.08.2016 13:33если его через 10 минут разводить будут
Обычно въезд на мост закрывают аккурат по расписанию. Польза робота только в ночной сводке.
перекрыт, но сведён (как было, когда его ремонтировали)
Об этом обычно есть оперативные данные, которые навигаторы публикуют. Вот, например, сейчас ремонтируют Тучков мост и на мосту в Яндекс.Картах нарисовано одностороннее движение:
TimsTims
02.08.2016 10:35Так ведь задача сводится к определению — раздвинут ли мост, или сдвинут, а не заблокированы ли входы/выходы. Для этого, естественно, нужно иметь камеру в другом месте и решать задачку посложнее.
darkk
01.08.2016 11:52Я ни с одним из этих утверждений не спорил. Более того, именно так я и планировал решать задачу с Литейным мостом, если не получится найти более пристойного источника данных, чем следы от стоп-сигналов :)
Со следами от стоп-сигналов вот ещё какая беда есть: если машина ошибается (а я не слишком высокого мнения о своих умениях в задачах распознавания), то по контуру моста человек может перепроверить машину. По следам стоп-сигналов это сложнее.
Terran37
01.08.2016 03:55Очень интересная статья и красиво подобранный материал. Сразу вспомнил как с друзьями составляли карту открытия и закрытия движения по мостам для максимально быстрого «объезда» ночью по максимальному количеству.
merl1n
01.08.2016 07:39+1Где код?
darkk
01.08.2016 11:55В статье, там примерно четыре строчки кода :-) Вся содержательная часть — это преобразование картинки в вектор фич, которыми потом кормится классификатор с дефолтными параметрами. Но несложно продублировать:
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), ))
ternaus
01.08.2016 10:00- А всё-таки хотелось бы увидеть какие-нибудь циферки, оценивающие точность модели. LogLoss, accuracy, ROC_AUC, и т. д,
- Если поделитесь размеченными изображениями(больше — лучше) готов поиграться со свёрточными сетями, и рассказать, что из них для этой задачи можно получить.(ну и код, понятно, выложить)
YNechaev
01.08.2016 12:20+1Автор указал, что у него «на всяких разных метриках» (подозреваю, что precision) получается 0,99. Картинка статичная и на самом деле остальные метрики не очень то нужны.
Интересно поиграться с нейронными сетями было бы в случае, если была бы размеченная база из многих мостов (желательно с разными конфигурациями). Но при этом не забывайте, что один из немногих плюсов свёрточных нейросетей для компьютерного зрения — это устойчивость к translation, rotation и shifting. Если у вас примерно одинаковые картинки с примерно одинакового ракурса — нет особенного смысла их использовать.
darkk
01.08.2016 12:30+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 с Александра Невского. Есть ещё поворотная камера, которая смотрит на мост лейтенанта Шмидта (две задачи по цене одной — понять, что сейчас на камере нужный мост и понять его состояние) и Литейный, по которому машины ездят, а разводной секци не видно. Что из этого интересно?ternaus
02.08.2016 01:38Лучше всего и побольше. :) Вы выложите, что есть размеченного на данный момент. 3GB jpeg-ов о мосте Александра Невского — с этим однозначно можно работать.
darkk
02.08.2016 13:39Есть вот такой сборник jpeg-ов: http://darkk.net.ru/garbage/dataset-for-mittov.tar.torrent Kaiser сказал, что вечером может посидить.
"Всего и побольше" — мне выкладывать некуда пока.
YNechaev
01.08.2016 11:55Так у вас тут статичная картинка, зачем вам тут вообще машинное обучение? На вашем питоне у вас за 10 минут бы получился отличный rule-based с 100% точностью.
stDistarik
01.08.2016 11:56Может как-то брать инфу у кораблей стоящих на рейде в Рыбацком. По рации или ещё как?
Delics
01.08.2016 12:31+2Переделайте в скрипте домен с vpiter.tv на 195.154.163.4
Я вряд ли буду его продлевать в этом году.darkk
01.08.2016 12:39Спасибо за информацию, я
vpiter.tv
в коде для читаемости оставлял, ещё думал, почему же в вёрстке IP-адрес напрямую зашит.
Кстати, нет планов у вас робота моего забанить по какой-либо причине? :-) Я писал о нём на info at vpiter.com, но ответа там никакого не получил, и решил, «если баннер не вырезать и ссылку оставить, то, наверное, пока ок, а там видно будет».Delics
01.08.2016 13:38+1Там немного странная ситуация в компании. Писать на указанный вами адрес бесполезно.
Лучше сразу мне в личку.
Банить робота не планирую. Кстати, существует более простой способ определить, работает ли какая-нибудь камера или нет. Если надо, предоставлю вам доступ к такой информации.
Sly_tom_cat
01.08.2016 12:49Хорошая статья.
И главное — хорошая демонстрация — быстро разработанному «на коленке» вполне себе нормальному сервису… который возможно будет жить не один год и приносить реальную помощь людям.
А критика — интересна как идеии на другие решения и апгрейд существующего.
pvvv
01.08.2016 13:05+3Нейронные сети это, конечно, стильно, модно, молодёжно, но в данном конкретном случае не проще было бы сделать преобразование Хафа от картинки и посмотреть отношение интенсивностей в неких областях, соответсвующих прямым с определённым углом наклона?
darkk
01.08.2016 13:27+1стильно, модно, молодёжно
Так речь как раз о том, что можно обойтись простейшей линейной моделью, которая "нейронная сеть" лишь формально (один нейрон).
преобразование Хафа
Я о таком слове попросту не знал, никогда не занимался обработкой изображений, в этом и был посыл статьи — можно сделать ненулевую пользу почти не разбираясь, абсолютно "на коленке" :-)
Но да, когда прикручивал мост Александра Невского, я думал об извлечении более сложных фич, собирался начать как раз с угла наклона — но тот же способ с
canny(rgb2blue(crop(raw)))
успел дать пристойные результаты попросту на большем количестве данных раньше, чем я в режиме "по вечерам" успел разобраться в вопросе получше.
BigW
Я достаточно далек от нейронных сете, но все же:
Что показывает синий график? Ошибку? А как изменяется синий график при сведении и разведении моста?
Может быть применив какую-либо фильтрацию удастся определить, что мост начал сводиться или разводиться? Это частично позволит решить проблему глюков видеокамеры. Ведь зная предыдущее состояние моста (а если синий график зеркально отражается относительно вертикальной оси при сведении/разведении моста то можно и без этого) и текущее направление движения, а так же среднее время сведения моста + запас можно с 90% предсказать текущее положение (открыт или закрыт)
darkk
Синий график показывает вероятность третьего класса «мост в движении». Потом я отказался от данного класса, т.к. в интерфейсе его всё равно не существует. Мусор с камеры оказалось проще всего отслеживать по баннеру, т.к. обычно мусор связан с развалившимся видеопотоком.
По-честному ещё стоит, конечно, отслеживать, что камеру не повернули, и что она смотрит в тот же ракурс, на котором бот обучался.
Вопрос про запас и вероятность я не понял. Глюки видеокамеры обычно выражаются либо в нескольких мусорных кадрах либо в полной её недоступности.