Распознавание дороги посредством семантической сегментации

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

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

    Но обо всем по порядку и для начала немного матчасти.

    Сегментация


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

    Вот неплохая статья, описывающая примитивные подходы.

    Семантическая сегментация


    Семантическая сегментация — разбиение изображения на объекты с определением типов этих объектов.

    Выглядит это как-то так:



    Результаты очень впечатляющие, посмотрим чего стоит воплотить это в реальной жизни.

    U-net


    Самая известная нейросеть, изначально разработанная для медицины.
    Первоисточник

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

    В интернете много статей, как готовить данные и обучать U-net сети:


    Однако, готовой U-net сети, чтобы быстро взять и поэкспериментировать, я не нашел.

    E-net


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


    Данные


    Самые популярные датасеты, для сегментации улиц (на них изначально обучали E-net):


    На этих же датасетах сейчас обучают и U-net.

    Выбор реализации


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

    Так что выбор был сделан в сторону наименьшего сопротивления.

    Использование очень простое:
    (Более всего беспокоит то, что сеть обучена на картинках размером 1024x512 — это во-первых больше, чем отдает камера на Raspberry, во-вторых требуемая производительность для обработки такого объема данных несколько смущает. В итоге, главная проблема окажется именно в этом).

    Читаем нейросеть из файлов (в одном сама модель, в другом имена классов, в третьем — цвета).

    def load_segment_model():
        try:
            classes = None
            with open(PiConf.SEGMENT_CLASSES) as f:
                classes = f.read().strip().split("\n")
            colors = None
            with open(PiConf.SEGMENT_COLORS) as f:
                colors= f.read().strip().split("\n")
            colors = [np.array(c.split(",")).astype("int") for c in colors]
            colors = np.array(colors, dtype="uint8")
            print("[INFO] loading model...")
            net = cv2.dnn.readNet(PiConf.SEGMENT_MODEL)
            return net, classes, colors
        except Exception as e:
            logging.exception("Cannot load segment model")
        return None, None, None
    

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

    def segment_image(image_path, seg_net, seg_classes, seg_colors):
    
        image0 = cv2.imread(image_path)
        image = cv2.resize(image0, (1024, 512),interpolation=cv2.INTER_NEAREST)
        blob = cv2.dnn.blobFromImage(image, 1 / 255.0, (1024, 512), 0, swapRB=True, crop=False)
    
        seg_net.setInput(blob)
        start = time.time()
        output = seg_net.forward()
        end = time.time()
    
        print("[INFO] inference took {:.4f} seconds".format(end - start))
    
        (numClasses, height, width) = output.shape[1:4]
    
        classMap = np.argmax(output[0], axis=0)
    
        mask = seg_colors[classMap]
    
        mask = cv2.resize(mask, (image0.shape[1], image0.shape[0]),interpolation=cv2.INTER_NEAREST)
        classMap = cv2.resize(classMap, (image0.shape[1], image0.shape[0]), interpolation=cv2.INTER_NEAREST)
    
        gmask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
        gmask = cv2.resize(gmask, (128, 64), interpolation=cv2.INTER_NEAREST)
        gmask = gmask[0:64,32:96]
    
        output = ((0.6 * image0) + (0.4 * mask)).astype("uint8")
        return output, gmask
    

    Проверка


    Берем готовые картинки с танка и натравливаем на них сегментирующую нейросеть.

    1



    Дорогой признана только левая часть тротуара.

    Сжимаем картинку и берем из нее центр размером 64x64:
    (Такой размер ожидается нейросетью, которая принимает решение о смене направления)



    Нейросеть направления (по факту — классификатор) командует взять влево. Не очень правильно, но терпимо.

    2



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



    Классификатор предлагает ехать прямо.

    3

    Ситуция, когда робот оказался посреди тротуара.



    Дорога распознана практически идеально.



    Классификатор командает брать вправо (чтобы найти кромку дороги в следующий раз).

    Применение


    Поколдовав немного над прошивкой танка, заменил цветовой детектор дороги на сегментирующую нейросеть.

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

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



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

    Ссылки


    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 26

      0
      Чисто теоретически, может быть записывать в память уже хорошо распознанные участки и не пытаться их распознать, а распознавать только «новое»?
        0
        А как отличить «хорошо распознанное» от «нового»?
          0
          То, что уже на прошлых кадрах было понято как дорога и препятствие. Не могу сформулировать мысль теоретически, попробую привести сильно упрощённо на конкретном примере:
          — Робот едет со скоростью 10 см/с.
          — Робот «видит» на 1 метр вперёд.
          — Робот распознал, что 50 сантиметров перед ним — точно (вероятность выше определённого порога) является ровной дороги.
          — Через секунду он фотографирует пространство впереди (глубиной 1 метр), но пытается распознать не всю фотографию, а только то пространство, которое не является точно распознанным (в данном примере — 40 сантиметров).
            +1

            Предлагаете распознавать частично? Все равно большее значение имеет разрешение картинки.

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

            Насчёт быстродейсвия — все сегментационные сети достаточно медленные, тем более на проце. Можно попробовать intel movidus stick, думаю, на нем получится пара fps, а то и больше. Либо попробовать раза в 4 уменьшить размер входа нейросети.
            А насчёт разницы между u- и e-net, думаю, для такого прототипа она вообще не принципиальна. Можно опять же поискать модификации с меньшим количеством слоев. Я когда-то решал задачу сегментации с помощью Segnet, находил годную cpu-реализацию (чуть больше информации тут )
              0
              Интересно, изучу
          0

          Танк маленький, камера слишком низко находится. Размер танка не соответствует размеру дороги.

            0
            Как это мешает?
              0

              Обученная модель подойдет только для такого размера машин.

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

                  Так делают все, кто обучает автопилоты?
                  Это все равно что обычный автомобиль обучать по не размеченному плацу или аэродрому. Чему он там обучится? Не выезжать за пределы плаца или аэродрома?
                  К тому же, обучать танк ездить только по дорогам — плохая идея :)

                    0
                    Пока только по тротуару, дальше — по мере роста потребностей.
                      0

                      У ENet есть одна неприятная особенность, связанная с обучением на Cityscapes — на всех изображениях присутствует фрагмент капота Mercedes, отчего, можно заметить, сеть продолжает выделять на всех Ваших примерах полукруг как background внизу кадра.

                        0
                        Да, сомнительно вот здесь же нет
                          0

                          Ну как бы есть же, чем иначе объяснить влечение сети оставлять пиксели снизу, но не слева, например?


                            +1
                            Может быть и ваша правда, я списывал это на неоднородность асфальта.
            0
            Я для своих экспериментов просто использовал вот этот проект github.com/kwotsin/TensorFlow-ENet
            Все работает из коробки с CamVid, при этом разрешение картинки можно задать в настройках (я потратил минут 20 на все), можно скачать предобученную модель.
            Можно использовть свой датасет.

            Но, если хотите высокий FPS возьмите модель Road Segmentation из openVino и используйте ее, она хорошо обучена и очень быстрая (у меня на одном ядре I7 работает в реалтайме, правда с Inference Engine). Не знаю, работает ли IE на вашей платформе, но если работает, то 3-4 fps должна выдать. Эта модель больше подходит для вашей задачи. Крмое этого, можете в модели уменьшить число классов до 2-х, этим вы сократите расходы времени CPU на обработку не нужных вам feature maps и тогда сеть будет работать быстрее. В репозитории OpenVino Zoo есть и варианты с int8 моделями, которые на CPU работают еще быстрее.
              0
              интересно, спасибо. Этот проект не попался изначально.
                0

                Все же автор использует Raspberry, где ARM CPU. OpenVINO подойдёт разве что для запуска на Movidius (ну или попробовать OpenCV оттуда, вдруг забыли какие-то флаги оптимизации выставить). Поэтому вариант с уменьшением размеров картинки в несколько раз, как по мне, пока наиболее перспективный.

                  0
                  К сожалению, Интел так документирует свои продукты, как будто преследует цель, чтобы ими никто не пользовался. Так что про Road Segmentation сразу не понял.
                  0
                  Пользуясь случаем хочу спросить насчет предобученных моделей Intel Zoo. У меня наблюдалось некоторое ускорение int8 моделей по сравнению с fp32. Там есть какие-то оптимизации для CPU вроде SIMD или у меня наблюдался эффект ускорения, связанный с уменьшением модели и более эффективным использованием кэша? На каких датасетах эти модели обучали? Можно ли взять модель, обучить на своем, а потом при помощи Model Optimizer сделать оптимизированную модель и запустить на IE?
                    0

                    Я бы сказал, что ускорение даже должно быть. То, что вес модели меньше в 4 раза — это хорошо, но не главное.
                    Модели обучаются в FP32 режиме — для создания INT8 веса квантизуют, с учетом активаций нейронов (назвали калибровкой). Потребуется 1-2 тысячи картинок-примеров из вашей задачи, чтобы собрать статистики.

                0
                Просто для информации (упоминался DonkeyCar) — Amazon запустил проект/сообщество AWS DeepRacer. Примерно похожая задача. Только трассы предопределённые.
                  0

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

                Only users with full accounts can post comments. Log in, please.