Прототип Lane Departure Warning или как напомнить водителю о том, что жить ему ...

:


Почитал я немного про автовыставку в Детройте, про то, что Lane Departure Warning становится все более и более популярным и решил, что надо бы поделиться своим опытом изготовления прототипа этой системы из нехитрых компонентов в виде веб-камеры, Питона, OpenCV и пары дней усердной медитации:)

Историю создания прототипа можно почитать и посмотреть под катом… (там картинки, много...)

Предисловие


Началось все с того, что мне надо было выбрать финальные проекты для двух курсов: «Автоэлектроника» и «Обработка изображений». Как человек ленивый, я не мог не скрестить оба проекта и тем самым сэкономить время, а если учесть, что нам за пару лекций до этого как раз рассказывали про Lane Departure Warning (честно говоря, я до сих пор не знаю адекватного русского, а главное короткого названия этой системы, поэтому буду использовать сокращение LDW), то мне захотелось попробовать сделать такую штуку самостоятельно.

Немного общей информации


Итак, что же это за зверь такой по имени «Lane Departure Warning». Начнем с того, что во время движения автомобиль чаще всего находится в своей полосе и не должен из нее выезжать, но на деле же водитель может отвлекаться от управления автомобилем и машина может начать перемещаться на соседнюю полосу, что может привести к особенно печальным последствиям в случае перемещения на встречку. По статистике National Highway Traffic Safety Administration (NHTSA) 40-60% всех аварий на трассах в США прямо или косвенно связаны с тем, что автомобиль покидает свою полосу. Для предотвращения этого люди придумали отслеживать позицию автомобиля в полосе и в случае, если он начинает уходить с нее, подавать сигнал водителю или применять более активные действия: поворачивать руль или применять тормоза на колесах противоположной стороны, чтобы не дать автомобилю покинуть свою полосу. На самом деле, такие системы не являются чем-то совсем уж новым, японцы еще лет 10 назад начали экспериментировать с ними на некоторых моделях. Причем различные компании использовали для реализации достаточно разные подходы: обычные камеры смотрящие вперед или назад, а также инфракрасные камеры смотрящие вниз.

Реализация


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

Для обработки видеопотока традиционно была выбрана библиотека OpenCV, а для реализации алгоритма Python.
Чуть-чуть про Питон

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

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

Но только до тех пор, пока на дороге не появляется тень, а она появляется весьма и весьма часто. Тогда получается вот так:

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

Дабы избавиться от этой проблемы пришла ко мне мысль работать с цветовым пространством HSV, мол там цвет от яркости особо не зависит, тогда тень и автоэкспозиция влиять не должны. С одной стороны мысль достаточно разумная, только вот при крайних значениях яркости, в канале тона появляется довольно много шума. В результате это выглядит так:

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

и так еще два монитора рядом:)
Попытка вторая — виртуальные сенсоры

После неудачной первой попытки понял я, что так дело не пойдет, но смотреть где-то готовый алгоритм не хотелось принципиально, хотелось все свое. Правда один раз я таки немного считерил и подсмотрел одну картинку на первой странице выдачи гугла:
image
Из этой картинки родилась идея использования нескольких «виртуальных сенсоров», которые пытаются отследить не всю полосу целиков, а ее участок в какой-то выделенной горизонтальной области.

Тут на изображении горизонтальные красные полосы как раз и показывают рабочие области этих сенсоров. Работают все они независимо, что позволяет фильтровать их результаты относительно друг друга. Т.е. если все показывают, что полоса слева, а один справа, его результаты можно выбросить, даже если этот сенсор уверен на все 100%, что полоса там.

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

0) Сначала мы обрезаем изображение только до той области, где видна дорога. Это позволяет улучшить производительность и не смущать алгоритм отражениями на капоте и небом.
1) Затем мы применяем Canny для всего изображения. Это позволяет нам забыть об автоэкспозиции и иногда о тенях.
2) Затем для каждого сенсора:
2.1) ищем все замкнутые регионы (участки, ограниченные с обеих сторон границами)
2.2) ищем те регионы у которых ширина равна ширине полосы
2.3) если таких регионов несколько (из-за теней и подобных проблем), то считаем средний цвет региона и его расстояние от нашей цветовой модели разделительной полосы и выбираем наиболее похожий.

Вот так выглядят результаты для каждого сенсора (черный — разделительная полоса, белый — дорога):

Модель разделительной полосы

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

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

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

Собственно это почти конец, осталось только сообщить водителю о том, что там происходит. Для этого я показываю стрелочки, которые направлены в сторону перемещения машины:

Тестирование


Итак, самое интересное — тесты. Для начала посмотрим на то, как разработанный прототип справится с предварительно записанным видео:

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

Ну а теперь главное — тесты на реальной трассе:

А вот и обещанное видео на котором можно услышать звук наезда на катафоты, которые расположены на разделительной полосе (очень кстати полезная штука, причем не только для тестов):

Конец


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

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

И еще раз скажу, что Питон — классная штука для прототипирования, это я говорю как человек, который уже довольно много лет пишет на C++ (да и 2 дня на разработку такой штуки с нуля без знания языка подтвержают это, на Си бы я так не успел).

PS: Извиняюсь за то, что не продолжил прошлую тему с 3хмерным чайником, как оказалось, 3д монитор хорош для 3д, но никак не подходит для работы с 2д, поэтому пришлось его поменять, но я надеюсь эта тема не хуже:)

PS2: Забыл про исходники, они живут на гитхабе