Слабым местом было распознавание самого дорожного полотна из-за переменчивости цветовых оттенков, из-за чего нейросеть, принимающая решения, выдавала странные результаты. В комментариях к той статье рекомендовали обратить внимание на семантическую сегментацию. Тема оказалась перспективной и применение сегментирующей нейросети принесло свои плюсы, но и минусы, куда же без них.
Но обо всем по порядку и для начала немного матчасти.
Сегментация
Сегментация — процесс выделения некоторых частей на изображении. Простейший и самый очевидный вид сегментации — по цвету. Однако, используя этот метод, невозможно понять, что и где изображено на картинке.
Вот неплохая статья, описывающая примитивные подходы.
Семантическая сегментация
Семантическая сегментация — разбиение изображения на объекты с определением типов этих объектов.
Выглядит это как-то так:
Результаты очень впечатляющие, посмотрим чего стоит воплотить это в реальной жизни.
U-net
Самая известная нейросеть, изначально разработанная для медицины.
Первоисточник
Народ быстро сообразил, что подход можно использовать на все случаи жизни.
В интернете много статей, как готовить данные и обучать U-net сети:
Однако, готовой U-net сети, чтобы быстро взять и поэкспериментировать, я не нашел.
E-net
Более молодая и менее известная сеть. Разработана как раз для распознавания городских улиц.
Данные
Самые популярные датасеты, для сегментации улиц (на них изначально обучали E-net):
- CityScapes — Немецкие и швейцарские города
- CamVid — улицы Кембриджа в Англии
- SUN — Принстонский набор датасетов для разных видов сцен
На этих же датасетах сейчас обучают и 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 картинки такого размера не переварить.
Похоже, все таки придется заняться обучением специализированной нейросети.
Ссылки
Комментарии (24)
nomhoi
27.06.2019 17:15Танк маленький, камера слишком низко находится. Размер танка не соответствует размеру дороги.
Stantin Автор
27.06.2019 17:20Как это мешает?
nomhoi
27.06.2019 17:28Обученная модель подойдет только для такого размера машин.
Stantin Автор
27.06.2019 17:45Модели обычно обучают с большой вариативностью по размеру, положению, цвету итд.
Так что низкую высоту камеры эта сеть должна решать (что она в общем-то и делает).nomhoi
27.06.2019 18:00Модели обычно обучают с большой вариативностью по размеру, положению, цвету итд.
Так делают все, кто обучает автопилоты?
Это все равно что обычный автомобиль обучать по не размеченному плацу или аэродрому. Чему он там обучится? Не выезжать за пределы плаца или аэродрома?
К тому же, обучать танк ездить только по дорогам — плохая идея :)Stantin Автор
27.06.2019 18:10Пока только по тротуару, дальше — по мере роста потребностей.
dkurt
27.06.2019 23:23У ENet есть одна неприятная особенность, связанная с обучением на Cityscapes — на всех изображениях присутствует фрагмент капота Mercedes, отчего, можно заметить, сеть продолжает выделять на всех Ваших примерах полукруг как background внизу кадра.
xaoc80
27.06.2019 19:05Я для своих экспериментов просто использовал вот этот проект 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 работают еще быстрее.dkurt
27.06.2019 23:17Все же автор использует Raspberry, где ARM CPU. OpenVINO подойдёт разве что для запуска на Movidius (ну или попробовать OpenCV оттуда, вдруг забыли какие-то флаги оптимизации выставить). Поэтому вариант с уменьшением размеров картинки в несколько раз, как по мне, пока наиболее перспективный.
Stantin Автор
27.06.2019 23:44К сожалению, Интел так документирует свои продукты, как будто преследует цель, чтобы ими никто не пользовался. Так что про Road Segmentation сразу не понял.
dkurt
29.06.2019 00:08+1На всякий случай,
Описания: https://github.com/opencv/open_model_zoo
Сами модели: https://download.01.org/opencv/2019/open_model_zoo/R1/models_bin/
xaoc80
28.06.2019 00:27Пользуясь случаем хочу спросить насчет предобученных моделей Intel Zoo. У меня наблюдалось некоторое ускорение int8 моделей по сравнению с fp32. Там есть какие-то оптимизации для CPU вроде SIMD или у меня наблюдался эффект ускорения, связанный с уменьшением модели и более эффективным использованием кэша? На каких датасетах эти модели обучали? Можно ли взять модель, обучить на своем, а потом при помощи Model Optimizer сделать оптимизированную модель и запустить на IE?
dkurt
29.06.2019 00:03Я бы сказал, что ускорение даже должно быть. То, что вес модели меньше в 4 раза — это хорошо, но не главное.
Модели обучаются в FP32 режиме — для создания INT8 веса квантизуют, с учетом активаций нейронов (назвали калибровкой). Потребуется 1-2 тысячи картинок-примеров из вашей задачи, чтобы собрать статистики.
Germanjon
Чисто теоретически, может быть записывать в память уже хорошо распознанные участки и не пытаться их распознать, а распознавать только «новое»?
Stantin Автор
А как отличить «хорошо распознанное» от «нового»?
Germanjon
То, что уже на прошлых кадрах было понято как дорога и препятствие. Не могу сформулировать мысль теоретически, попробую привести сильно упрощённо на конкретном примере:
— Робот едет со скоростью 10 см/с.
— Робот «видит» на 1 метр вперёд.
— Робот распознал, что 50 сантиметров перед ним — точно (вероятность выше определённого порога) является ровной дороги.
— Через секунду он фотографирует пространство впереди (глубиной 1 метр), но пытается распознать не всю фотографию, а только то пространство, которое не является точно распознанным (в данном примере — 40 сантиметров).
Stantin Автор
Предлагаете распознавать частично? Все равно большее значение имеет разрешение картинки.
d1ss4pp34r
Прощу прощения, что ответом на сообщение, комментарий оставить видимо карма не позволяет.
Насчёт быстродейсвия — все сегментационные сети достаточно медленные, тем более на проце. Можно попробовать intel movidus stick, думаю, на нем получится пара fps, а то и больше. Либо попробовать раза в 4 уменьшить размер входа нейросети.
А насчёт разницы между u- и e-net, думаю, для такого прототипа она вообще не принципиальна. Можно опять же поискать модификации с меньшим количеством слоев. Я когда-то решал задачу сегментации с помощью Segnet, находил годную cpu-реализацию (чуть больше информации тут )
Stantin Автор
Интересно, изучу