Цель этого проекта - создать надежный алгоритм поиска полосы движения с использованием передовых методов компьютерного зрения. Такой алгоритм должен хорошо работать с извилистыми дорогами, разными типами тротуара, переменными условиями освещения (день / ночь) и т. Д. Он основан на работе, проделанной в Проект 1: Поиск полос движения. Вот краткий обзор результата.

Трубопровод

  • Коррекция искажений
  • Преобразование перспективы с высоты птичьего полета
  • Градиент и пороговое значение цвета
  • Поиск переулка
  • Расчет радиуса кривой и смещения от центра полосы движения
  • Рисование полос на исходном видео

Коррекция искажений

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

Камера преобразует 3D-объекты в 2D-изображения. Точка отсчета в трехмерном пространстве (x, y, z) преобразуется в двухмерное пространство (x ’, y’). Для камеры-обскуры это преобразование может быть представлено матрицей камеры C:

y ~ C x

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

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

В opencv оба этих типа искажений могут быть представлены пятью коэффициентами:

Distcoeff = [k1, k2, p1, p2, k3]

Чтобы исправить искажение, я беру несколько изображений известной трехмерной формы (например, шахматной доски) и использую сопоставление точек из 3D - ›2D для вычисления правильной матрицы камеры. В случае шахматной доски это означает отображение отображений вида (1, 1, 0) - ›(1,1). Затем матрицу камеры можно использовать для неискажения любых новых изображений. Для этой цели мы используем методы cv2.calibrateCamera и cv2.undistort из opencv. Для получения подробной информации обратитесь к методу camera_calibration_params .

Вот пример исправления искажения в образце изображения.

Перспективное преобразование

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

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

Вот пример преобразования перспективы с высоты птичьего полета на образце изображения.

Градиентное пороговое значение

Обнаружение Canny Edge, используемое в Project 1, основано на операторе Sobel. Применение оператора Собеля к изображению - это способ получения производной изображения в направлении x или y. Операторы Собела 3x3 для направлений x и y следующие:

Я использовал их вместе с величиной и направлением градиента, чтобы сгенерировать новое двоичное изображение с пороговыми значениями градиента. Пожалуйста, обратитесь к методу gradient_thresholding для получения подробной информации.

Вот пример определения порога градиента на образце изображения.

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

Пороговое значение цвета

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

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

  • R из RGB. порог: (220, 255)
  • S от HLS. порог: (180, 255)
  • V из HSV. порог: (220, 255)
  • V из LUV. порог: (155, 255)

Вот пример установления порога цвета на образце изображения.

Очевидно, что пороговая обработка цветового канала очень эффективна для выделения из изображений только линий полос.

Комбинированный порог

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

Вот пример применения комбинированного определения порога цвета к образцу изображения.

Поиск переулка

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

Вот пример применения поиска полосы на образце изображения.

Вот как интерпретировать это изображение:

  • Красный: пиксели левой полосы
  • Синий: пиксели правой полосы
  • Зеленый: скользящие окна, используемые для поиска пикселей-кандидатов.
  • Желтый: квадратичные кривые, подогнанные к красным и синим пикселям, чтобы найти кривые левой и правой полосы соответственно.

Этот процесс поиска полосы и подбора кривой был повторен на всех кадрах видео.

Расчет радиуса кривой

Следующая формула используется для вычисления радиуса кривой для функции y = f (x).

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

Расчет смещения от центра полосы движения

Поскольку камера установлена ​​в центре автомобиля, смещение от центра полосы движения было рассчитано путем измерения расстояния между центром изображения и средней точкой линии, соединяющей две кривые полосы движения.

смещение центральной полосы = (центр изображения) - (середина линии, соединяющей две кривые полосы)

Для преобразования расстояния в метрах из пикселей использовалась следующая шкала:

ym_per_pix = 30/720 # meters per pixel in y dimension
xm_per_pix = 3.7/700 # meters per pixel in x dimension

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

Рисование полос на исходном видео

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

Вот пример рисования линий полос движения на образце изображения.

Сглаживание

Чтобы обеспечить плавный вывод из метода поиска полос, я создал класс LaneDetector, который выполнял следующие функции:

  • Найдите полосы движения на изображении
  • Если найденные полосы движения отклоняются от ранее найденных полос движения более чем на пороговое значение, отбросьте их и продолжайте использовать предыдущие полосы движения (отклонение выбросов).
  • В противном случае усредните результаты из текущих и предыдущих 20 кадров видео, чтобы произвести прогноз для линий полос на текущем временном шаге. Значение 20 было выбрано после того, как оно показало лучший компромисс между точным обнаружением полос движения и обеспечением плавного вывода. Обратитесь к методу verify в классе LaneDetector для получения подробной информации.
  • Нарисуйте линии полос и добавьте радиус кривой, смещение центральной полосы к исходному изображению

Пожалуйста, обратитесь к классу LaneDetector за подробностями.

Результат

Это результат применения вышеуказанного конвейера к полному видео.

В целом я очень доволен результатами. Однако есть возможности для дальнейшего улучшения.

Дальнейшие улучшения

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

Я попробовал некоторые из этих улучшений здесь. Полную информацию о реализации можно найти здесь.