Обнаружение и обработка дорожных знаков и пешеходов

Материал из Викиконспекты
Перейти к: навигация, поиск
Эта статья находится в разработке!

Обнаружение объектов является хорошо известной проблемой в области компьютерного зрения и глубокого обучения. Существует два компонента в модели обнаружения объекта, а именно, базовая нейронная сеть и нейронная сеть обнаружения. Во-первых, базовые нейтральные сети -это CNN, которые извлекают объекты из изображения, из объектов низкого уровня, таких как линии, ребра или круги, в объекты более высокого уровня, такие как лицо, человек, светофор или знак стоп и т. д. Несколько хорошо известных базовых нейронных сетей-это LeNet, InceptionNet(он же. GoogleNet), ResNet, VGG-Net, AlexNet, MobileNet и др. Эта превосходная статья обсуждает различия между этими базовыми нейтральными сетями ниже.

Затем нейронные сети обнаружения присоединяются к концу базовой нейронной сети и используются для одновременной идентификации нескольких объектов из одного изображения с помощью извлеченных признаков. Некоторые из популярных сетей обнаружения-SSD (Single Shot MultiBox Detector), R-CNN (регион с функциями CNN), более быстрый R-CNN и YOLO (вы смотрите только один раз) и т. д. В этой статье рассматриваются различия между этими нейронными сетями обнаружения.

Modeling training

Выделим несколько этапов связанных с модельным обучением.

  1. Сбор и маркировка изображений (20-30 мин)
  2. Выбор модели
  3. Трансферное обучение / модельное обучение (3-4 часа)
  4. Сохранить вывод модели в формате Edge TPU (5 мин)
  5. Запуск вывода модели на Raspberry Pi

Сбор и маркировка изображений

У нас есть 6 типов объектов, а именно: красный свет, зеленый свет, знак остановки, ограничение скорости 40 миль в час, ограничение скорости 25 миль в час и несколько фигурок Lego в качестве пешеходов.
Рис. 1 Установленные объекты
Таким образом, у нас будет около 50 подобных фотографий, с объектами размещенными случайным образом на каждом изображении. Затем пометим каждое изображение с помощью ограничительной рамки для каждого объекта на изображении. С помощью бесплатного инструмента, называемый labelImg (для Windows / Mac / Linux) это можно сделать довольно быстро и просто.


Выбор модели

На Raspberry Pi, так как мы имеем ограниченные вычислительные мощности, мы должны выбрать модель, которая работает относительно быстро и точно. После экспериментов с несколькими моделями, свой выбор остановим на MobileNet v2 SSD COCO как на модели с оптимальным балансом между скоростью и точностью.

Трансферное Обучение / Модельное Обучение

Для этого шага будем использовать Google Colab.

Подготовка данных для обучения:

repo_dir_path =  '/content/DeepPiCar'
% cd{ repo_dir_path} / models/ object_detection
# Convert train folder annotation xml files to a single csv file,
# generate the `label_map.pbtxt` file to `data/` directory as well.
!python code/xml_to_csv.py -i data/ images/ train - o data/ annotations/ train_labels.csv -l data/ annotations
# Convert test folder annotation xml files to a single csv.
!python code/xml_to_csv.py -i data/ images/ test -o data/ annotations/ test_labels.csv
# Generate `train.record`
!python code/generate_tfrecord.py --csv_input=data/annotations/train_labels.csv - - output_path= data/ annotations/ train.record -- 
img_path=data/images/train --label_map data/annotations/label_map.pbtxt
# Generate `test.record`
!python code/generate_tfrecord.py --csv_input=data/annotations/test_labels.csv - - output_path= data/ annotations/ test.record -- 
img_path=data/images/test --label_map data/annotations/label_map.pbtxt

Приведенный выше код преобразует xml-файлы меток, созданные инструментом LabelImg, в двоичный формат, так что TensorFlow может обрабатывать быстро.

Загружаем Pre-trained Model:

MODEL_FILE = MODEL + '.tar.gz'
DOWNLOAD_BASE = 'http://download.tensorflow.org/models/object_detection/'
DEST_DIR =  '/content/models/research/pretrained_model'
if not( os.path.exists( MODEL_FILE)):
    urllib.request.urlretrieve( DOWNLOAD_BASE +  MODEL_FILE, MODEL_FILE)
tar =  tarfile.open( MODEL_FILE)
tar.extractall()
tar.close()
os.remove( MODEL_FILE)
if(os.path.exists( DEST_DIR)):
    shutil.rmtree(DEST_DIR)
os.rename( MODEL, DEST_DIR)
fine_tune_checkpoint = os.path.join( DEST_DIR, "model.ckpt")
print( fine_tune_checkpoint)

Приведенный выше код загрузит предварительно обученные файлы модели для ssd_mobilenet_v2_quantized_300x300_coco_2019_01_03 модель и мы будем только использовать model.ckpt файл.

Тренируем модель:

num_steps = 2000
num_eval_steps = 50
model_dir =  '/content/gdrive/My Drive/Colab Notebooks/TransferLearning/Training'
pipeline_file =  'ssd_mobilenet_v2_quantized_300x300_coco.config'
!python /content/models/research/object_detection/model_main.py \
    - - pipeline_config_path= { pipeline_fname} \
    --model_dir='{model_dir}' \
    --alsologtostderr \
    --num_train_steps={num_steps} \
    --num_eval_steps={num_eval_steps}

Этот шаг занимает 3-4 часа, в зависимости от количества шагов, которые вы тренируете (они же эпохи или num_steps). После того, как тренировка будет завершена, вы увидите кучу файлов в model_dir. Мы ищем самое последнее model.ckpt-xxxx.meta файл.Во время обучения мы можем отслеживать прогрессирование потерь и точности с помощью TensorBoard.

Тестируем обученную модель:
Рис. 2 Опознанные объекты

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

Сохраняем вывод модели в формате Edge TPU:

После того, как модель обучена, мы должны экспортировать модель meta файл в диаграмму вывода в формате Google ProtoBuf, а затем в формат, который Edge TPU accelerator может понимать и обрабатывать.

Планирование и управление движением

Теперь, когда DeepPiCar может обнаружить и определить, какие объекты находятся перед ним, нам все еще нужно сказать ему, что с ними делать, т. е. управление движением. Существует два подхода к управлению движением, т. е. основанный на правилах и сквозной.

  1. Подход, основанный на правилах значит, нам нужно точно сказать машине, что делать, когда она сталкивается с каждым объектом. Например, скажите автомобилю остановиться, если он видит красный свет или пешехода, или ехать медленнее, если он видит более низкий знак ограничения скорости и т. д. Это сродни тому, что мы сделали в части 4, где мы рассказали автомобилю, как перемещаться по полосе движения с помощью набора кодов/правил.
  2. Сквозной подход просто кормит автомобиль большим количеством видеоматериалов хороших водителей, и автомобиль, через глубокое обучение, выясняет сам по себе, что он должен остановиться перед красными огнями и пешеходами или замедлиться, когда ограничение скорости падает.

В статье рассматривается первый подход.

Правила довольно просты: если ни один объект не обнаружен, то привод на последнем известном ограничении скорости. Если какой-то объект обнаружен, этот объект изменит скорость автомобиля или ограничение скорости.

Во-первых, определяем базовый класс, TrafficObject, который представляет собой любые дорожные знаки или пешеходов, которые могут быть обнаружены на дороге. Он содержит метод, set_car_state(car_state). car_stateСловарь содержит две переменные, а именно: speed, и speed_limit, который будет изменен этим методом. Он также имеет вспомогательный метод, is_close_by(), который проверяет, находится ли обнаруженный объект достаточно близко. (Ну, поскольку наша единственная камера не может определить расстояние, приближаем расстояние с высотой объекта. Чтобы точно определить расстояние, нам понадобится лидар или его маленький кузен, ультразвуковой датчик или система камер стереовидения, как в Тесле.)

class TrafficObject(object):
    def set_car_state( self, car_state):
        pass
    @staticmethod
    def is_close_by( obj, frame_height, min_height_pct= 0,05):
        # default: if a sign is 10% of the height of frame
        obj_height = obj.bounding_box[ 1] [1] - obj.bounding_box[0][1]
        return obj_height /  frame_height >>  min_height_pct

Реализация для красного света и пешехода тривиальны, просто устанавливают скорость автомобиля 0.

class RedTrafficLight(TrafficObject):
    def set_car_state( self, car_state):
        logging.debug('red light: stopping car')
        car_state['speed'] =  0
class Pedestrian(TrafficObject):
    def set_car_state( self, car_state):
        logging.debug('pedestrian: stopping car')
        car_state['speed'] =  0

Как 25 миль в час, так и 40 миль в час ограничения скорости могут использовать только один класс SpeedLimit, который принимает speed_limit в качестве параметра инициализации. Когда знак обнаружен, просто установливаем ограничение скорости автомобиля до соответствующего предела.


class SpeedLimit( TrafficObject):
    def __init__(self, speed_limit):
        self.speed_limit = speed_limit
    def set_car_state( self, car_state):
        logging.debug('speed limit: set limit to %d' %  self.speed_limit)
        car_state['speed_limit'] =  self.speed_limit

Реализация зеленого света еще проще, так как он ничего не делает, но печать зеленого света обнаруживается (код не показан).

После того, как мы определили поведение для каждого дорожного знака, нам нужен класс, чтобы связать их вместе, который является ObjectsOnRoadProcessor класс. Этот класс сначала загружает обученную модель для Edge TPU, затем обнаруживает объекты в живом видео с моделью и, наконец, вызывает каждый объект трафика для изменения скорости и ограничения скорости автомобиля.

class ObjectsOnRoadProcessor(object):
    """
 This class 1) detects what objects (namely traffic signs and people) are on the road
 and 2) controls the car navigation (speed/steering) accordingly
    """
    def __init__(self,
                 car= None,
                 speed_limit=40,
                 model= '/home/pi/DeepPiCar/models/object_detection/data/model_result/road_signs_quantized_edgetpu.tflite',
                 label= '/home/pi/DeepPiCar/models/object_detection/data/model_result/road_sign_labels.txt',
                 width= 640,
                 height= 480):
        # model: This MUST be a tflite model that was specifically compiled for Edge TPU.
        # https://coral.withgoogle.com/web-compiler/
        logging.info('Creating a ObjectsOnRoadProcessor...')
        self.width =  width
        self.height =  height
        # initialize car
        self.car =  car
        self.speed_limit = speed_limit
        self.speed = speed_limit
        # initialize TensorFlow models
        with open(label, 'r') as f:
            pairs = (l.strip().split(maxsplit= 1) for l in f.readlines())
            self.labels =  dict((int(k), v) for k, v in )
        # initial edge TPU engine
        logging.info('Initialize Edge TPU with model %s...' %  model)
        self.engine = edgetpu.detection.engine.DetectionEngine(model)
        self.min_confidence = 0.30
        self.num_of_objects = 3
        logging.info('Initialize Edge TPU with model done.')
        self.traffic_objects = {0: GreenTrafficLight(),
                                1: Person(),
                                2: RedTrafficLight(),
                                3: SpeedLimit(25),
                                4: SpeedLimit(40),
                                5: StopSign()}
    def process_objects_on_road(self, frame):
        # Main entry point of the Road Object Handler
        objects, final_frame = self.detect_objects(frame)
        self.control_car(objects)
        return final_frame
    def control_car(self, objects):
        logging.debug('Control car...')
        car_state = {"speed": self.speed_limit, "speed_limit": self.speed_limit}
        if len(objects) == 0:
            logging.debug('No objects detected, drive at speed limit of %s.' % self.speed_limit)
        contain_stop_sign = False
        for obj in objects:
            obj_label = self.labels[obj.label_id]
            processor = self.traffic_objects[obj.label_id]
            if processor.is_close_by(obj, self.height):
                processor.set_car_state(car_state)
            else:
                logging.debug("[%s] object detected, but it is too far, ignoring. " % obj_label)
            if obj_label == 'Stop':
                contain_stop_sign = True
        if not contain_stop_sign:
            self.traffic_objects[5].clear()
        self.resume_driving(car_state)
    def resume_driving(self, car_state):
        old_speed = self.speed
        self.speed_limit = car_state['speed_limit']
        self.speed = car_state['speed']
        if self.speed == 0:
            self.set_speed(0)
        else:
            self.set_speed(self.speed_limit)
        logging.debug('Current Speed = %d, New Speed = %d' % (old_speed, self.speed))
        if self.speed == 0:
            logging.debug('full stop for 1 seconds')
            time.sleep(1)
    def set_speed(self, speed):
        # Use this setter, so we can test this class without a car attached
        self.speed = speed
        if self.car is not None:
            logging.debug("Actually setting car speed to %d" % speed)
            self.car.back_wheels.speed = speed

Обратите внимание, что каждый объект TrafficObject просто изменяет speed и speed_limit в car_state объекте, но на самом деле не меняет скорость автомобиля. Это ObjectsOnRoadProcessor изменяет фактическую скорость автомобиля, после обнаружения и обработки всех дорожных знаков и пешеходов.

Полный исходный код интереса находится на Github DeepPiCar.

Итоги

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

Источники информации

Эта статья находится в разработке!