Я нахожусь в моем первом семестре Self Driving Car Nanodegree от Udacity, и я хочу поделиться своим опытом относительно последнего проекта семестра 1, то есть обнаружения и отслеживания транспортных средств. Полный код можно найти здесь.

Вступление

Основная цель этого проекта - применить концепции HOG и машинного обучения для обнаружения транспортного средства из видео на приборной панели. Подождите минуту? Машинное обучение и что тоже для обнаружения объектов в 2018 году? Звучит устаревшим, не правда ли? Конечно, реализации глубокого обучения, такие как YOLO и SSD, которые используют сверточную нейронную сеть, выделяются для этой цели, но если вы новичок в этой области, лучше начать с классического подхода. Итак, приступим !!

Сбор данных

Самая важная вещь для любой задачи машинного обучения - это помеченный набор данных, и здесь нам нужно иметь два набора данных: автомобили и изображения, не относящиеся к транспортным средствам. Изображения были взяты из уже имеющихся наборов данных, таких как GTI и KITTI Vision. Изображения имеют размер 64x64 и выглядят примерно так:

Извлечение функций

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

Хорошо, но все же как извлечь функции? Что ж, есть три хороших метода, если вы хотите извлечь элементы из изображений.

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

2. Пространственное разбиение - цветовые гистограммы, безусловно, круто, но если особенности изображения так важны, то почему мы не можем взять все функции, используя какую-то функцию numpy? Что ж, в этом вопросе вы, безусловно, правы. Мы можем извлечь всю информацию из изображения, сгладив его с помощью numpy.ravel (). Но подождите, давайте сделаем некоторые вычисления, размер изображения 64x64, и это трехканальное изображение, поэтому общее количество извлеченных функций составляет 12 288 !! Близко к 12 тыс. Функций на одном изображении - не лучшая идея! Итак, вот и появляется пространственная компоновка! Что, если я скажу, что изображение 64x64 дает ту же информацию, что и 16x16? Конечно, есть некоторая потеря информации, но все же мы можем извлечь из изображения хорошие черты! Так что, если я применю numpy.ravel () к изображению 16x16, я получу только 768 функций!

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

Просто обратите внимание на это. OpenCV HOG возвращает Hog Image и Feature Vectors, но длина image.ravel () не равна длине функционального вектора. Это связано с тем, что HOG внутренне выполняет некоторые вычисления и уменьшает избыточность данных и возвращает оптимизированные векторы признаков. Кроме того, большее количество линий, которые вы видите на изображении, означает, что оно вернет больше функций.

Создание набора данных и предварительная обработка данных

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

Давайте определимся с параметрами HOG для извлечения функций. После множества ударов и испытаний я решил сделать следующее:

  • Ориентации- 9
  • Ячейки в блоке - 2
  • Пикселей на ячейку - 16
  • Цветовое пространство - YUV

Круто, после прогона изображений через функцию HOG с этими параметрами конечный размер параметра составляет 972, что довольно круто!

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

i) Перемешивание данных
ii) Разделение набора данных на обучающий и тестовый набор
iii) Нормализация и масштабирование данных (соответствие и преобразование набора данных)

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

Классификатор обучения

Что ж, фичи извлечены, данные предобработаны! Что дальше? Ага, теперь очередь нашего классификатора. Выбор классификатора за вами, но есть из чего выбрать:

  • Машины опорных векторов
  • Наивный байесовский
  • Древо решений

Я решил использовать машины опорных векторов, потому что они хорошо совместимы с HOG. Теперь в SVM у нас есть SVC (классификатор опорных векторов), и здесь также у нас есть выбор с различными ядрами и разными значениями C и гаммы.

Я обучил свой классификатор как линейному, так и rbf-ядру. Для обучения линейному ядру потребовалось около 1,8 секунды с точностью тестирования 98,7%, в то время как ядру rbf потребовалось около 25 минут для обучения с точностью тестирования 98,3%. Я решил использовать LinearSVC с параметрами по умолчанию исключительно потому, что он занимал меньше времени и был более точным, чем ядро ​​rbf.

Скользящее окно

Круто, наш классификатор теперь хорошо обучен и в 99% случаев сможет правильно предсказывать транспортные средства и не транспортные средства. Итак, каков следующий шаг? Что ж, следующий шаг - применить классификатор к участкам вашего изображения, чтобы найти, где именно на изображении находится машина!

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

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

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

Тепловые карты

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

Хорошо, это было круто, это все? И да, и нет! Есть еще одна проблема, когда вы запускаете код еще на нескольких тестовых изображениях.

Как вы заметили, на нашем изображении обнаружено несколько ложных срабатываний, также обнаруживаются автомобили, идущие с левой полосы, так как же нам решить эту проблему? Прежде всего, нам нужно посмотреть, как вообще возникла эта проблема? Наш классификатор имел точность 98,7%. У нас было 470 окон. Таким образом, в результирующих окнах у нас будет около 6 окон, которые будут ложными срабатываниями. Эти окна могут появляться где угодно, если у вас есть более низкий порог на конечном изображении тепловой карты. Таким образом, чтобы решить проблему с выездом машины на другую полосу и некоторые ложные срабатывания, можно просто увеличить порог. В моем случае я установил порог на значение 4, но снова пороговое значение зависит от ряда факторов, используемого цветового пространства, точности SVM и так далее.

Усреднение

Что ж, мы почти закончили! Конвейер отлично работает с изображениями, но все же есть одна проблема, если вы запустите конвейер для изображений, поступающих из видеопотока. Окончательно обнаруженные коробки станут очень шаткими и не будут обеспечивать плавное восприятие, возможно, что коробка исчезнет в некоторых кадрах. Так какое же решение? Решение довольно интуитивно понятное, сохраняет все уточненные окна, обнаруженные в предыдущих 15 кадрах, и усредняет прямоугольники в текущем кадре. Также вам нужно настроить порог на более высокий уровень. Просто так конечные ограничивающие рамки выглядят менее шаткими и обеспечивают плавный переход. Я попробовал свой пайплайн на видео проекта, и результаты были примерно такими.

Некоторые советы и хитрости

  1. Если вы застряли с длительным временем обработки при запуске конвейера для видео, сначала попробуйте уменьшить окна.
  2. Если у вас по-прежнему длительное время обработки, попробуйте уменьшить количество извлекаемых функций.
  3. Если у вас есть тестовый набор из 10 000 изображений и размер вашей функции 8000, SVM не будет работать на должном уровне, даже если точность на тестовом наборе превышает 95%. Используйте SVM с ядром rbf или уменьшите количество функций.
  4. Если для запуска конвейера по-прежнему требуется много времени, попробуйте пропустить половину или две трети кадров. Это ускорится. Помните, что под пропуском кадров я подразумеваю пропуск обработки кадров и установку в уточненных окнах этого кадра прямоугольников, собранных из предыдущих 15 кадров, хранящихся в некоторой структуре данных.

Посмотреть окончательный видеовыход можно здесь.

Конечно, видео не идеальное, но я доволен окончательным результатом. Лагов было меньше, также не обнаруживались машины, идущие с другой стороны. Также. Я работаю над подходами к YOLO и SSD и скоро напишу о том, чему они научились. УРА!! 1-й семестр окончен, время для самоанализа и очень взволновано для семестра 2.