Сегодня мы хотим поделиться серией примеров на Питоне для изучающих OpenCV на Raspberry Pi, а именно для двухкамерной платы StereoPi. Готовый код (плюс образ Raspbian) поможет пройти все шаги, начиная c захвата картинки и заканчивая получением карты глубин из захватываемого видео.
Вводная
Сразу подчеркну, что эти примеры – для комфортного погружения в тему, а не для продакшн-решения. Если вы продвинутый юзер OpenCV и имели дело с малиной, то знаете, что для полноценной работы желательно кодить на сишечке, да еще и задействовать малиновый GPU. В конце статьи я чуть подробнее коснусь «бутылочных горлышек» питонового решения и производительности в целом.
С чем работаем
В качестве железа у нас вот такой вот сетап:
Плата StereoPi, на борту Raspberry Pi Compute Module 3+. Подключены две самые простые камеры для Raspberry Pi версии V1 (на сенсоре ov5647).
Что установлено:
- Raspbian Stretch (kernel 4.14.98-v7+)
- Python 3.5.3
- OpenCV 3.4.4 (pre-compiled, 'pip' from Python Wheels)
- Picamera 1.13
- StereoVision lib 1.0.3 (https://github.com/erget/StereoVision)
Процесс установки всего софта выходит за рамки этой статьи, и мы просто предлагаем скачать готовый образ Raspbian (ссылки на гитхаб в конце статьи).
Шаг первый: захват картинки
Для этого используется скрипт 1_test.py
Открываем консольку, переходим из домашней папки в папку с примерами:
cd stereopi-tutorial
Запускаем скрипт:
python 1_test.py
После запуска на экран выводится превьюшка нашей стереокартинки. Процесс можно прервать нажатием на кнопку Q. При этом будет сохранена последняя захваченная картинка, которая будет использована в одном из следующих скриптов для настройки карты глубин.
Этот скрипт позволяет вам убедиться, что все железо работает корректно, а также получить первую картинку для дальнейшего использования.
Вот как выглядит работа первого скрипта:
Шаг второй: собираем картинки для калибровки
Если говорить о сферическом коне в вакууме, то для получения карты глубин хорошего качества нам нужно иметь две абсолютно идентичные камеры, вертикальные и оптические оси которых расположены идеально параллельно, а горизонтальные оси совпадают. Но в реальном мире все камеры немножко отличаются, да и расположить их идеально не получается. Поэтому был придуман трюк с программной калибровкой. С помощью двух камер из реального мира делается большое количество снимков заранее известного объекта (у нас это картинка с шахматной доской), а затем специальный алгоритм высчитывает всю «неидеальность» и пытается поправить картинки так, чтобы они были близки к идеалу.
Данный скрипт делает первый этап работы, а именно помогает сделать серию фотографий для калибровки.
Перед каждой фотографией скрипт начинает 5-секундный обратный отсчет. Этого времени, как правило, хватает на то, чтобы переместить доску в новое положение, убедиться что на обеих камерах она не вылезает за края, и зафиксировать ее положение (чтобы не было смаза на фотографии). По умолчанию размер серии установлен в 30 фотографий.
Запуск:
python 2_chess_cycle.py
Процесс:
В итоге мы имеем серию фотографий в папке /scenes.
Режем картинки на пары
Третий скрипт 3_pairs_cut.py нарезает сделанные фотографии на «левые» и «правые» картинки и сохраняет их в папке /pairs. На самом деле мы могли бы исключить этот скрипт и делать резку на лету, но он очень полезен при дальнейших экспериментах. Например, вы можете сохранять нарезку из разных серий, использовать свои скрипты для работы с этими парами, либо вообще подсовывать в качестве пар картинки, сделанные на других стереокамерах.
Плюс перед резкой каждой картинки скрипт выводит ее изображение, что часто позволяет еще до следующего этапа калибровки увидеть неудачные фото и просто их удалить.
Запускаем скрипт:
python 3_pairs_cut.py
Короткое видео:
В готовом образе есть набор фотографий и нарезанных пар, которые мы использовали для наших экспериментов.
Калибровка
Скрипт 4_calibration.py затягивает к себе все пары с шахматными досками и вычисляет необходимые поправки для исправления картинок. В скрипте сделан автоматический отброс фотографий, на которых не найдена шахматная доска, так что в случае неудачных фотографий работа не останавливается. После того, как все 30 пар картинок загружены, начинается обсчет. У нас он занимает примерно полторы минуты. После завершения скрипт берет одну из стереопар, и на основе посчитанных параметров калибровки «исправляет» их, выводя на экран ректифицированную картинку. В этот момент можно оценить качество калибровки.
Запуск командой:
python 4_calibration.py
Скрипт калибровки в работе:
Настройка карты глубин
Скрипт 5_dm_tune.py загружает картинку, сделанную первым скриптом, и результаты калибровки. Далее выводится интерфейс, позволяющий менять настройки карты глубин и смотреть, что меняется. Совет: перед настройкой параметров сделайте кадр, в котором у вас будут одновременно объекты на разных расстояниях: вблизи (сантиметров 30-40), на средней дистанции (метр-два) и вдалеке. Это позволит вам подобрать параметры, при которых близкие объекты будут красного цвета, а дальние – темно-синего.
В образе лежит файл с нашими настройками карты глубин. Вы можете загрузить наши настройки в скрипте просто нажав кнопку “Load settings”
Запускаем:
python 5_dm_tune.py
Вот как выглядит процесс настройки:
Карта глубин в реальном времени
Последний скрипт 6_dm_video.py строит карту глубин по видео, используя результаты работы предыдущих скриптов (калибровку и настройку карты глубин).
Запуск:
python 6_dm_video.py
Собственно результат:
Надеемся, что наши скрипты окажутся полезными в ваших экспериментах!
На всякий случай добавлю, что во всех скриптах есть обработка нажатий клавиш, и прервать работу можно нажатием кнопки Q. Если останавливать «грубо», например Ctrl+C, то может поломаться процесс взаимодействия Питона с камерой, и потребуется перезагрузка малины.
Для продвинутых
- Первый скрипт в процессе работы выводит среднее время между захватами кадров, а по завершению – средний FPS. Это простой и удобный инструмент для подбора таких параметров изображения, при которых питон еще «не захлебывается». С его помощью мы и подобрали 1280x480 при 20 FPS, при которых видео отдается без задержек.
- Вы можете заметить, что мы захватываем стереопару в разрешении 1280x480, а затем скейлим ее до 640x240.
Резонный вопрос: зачем всё это, и почему не захватить сразу уменьшенную картинку и не загружать наш питон ее уменьшением?
Ответ: при прямом захвате на очень низких разрешениях в малиновом ядре пока есть проблемы (картинка ломается). Поэтому мы берем разрешение побольше, а затем картинку уменьшаем. Тут мы используем маленький трюк: картинка скейлится не питоном, а с помощью GPU, поэтому нагрузки на армовое ядро нет. - Зачем захват видео в формате BGRA, а не BGR?
Мы для уменьшения размера картинки используем ресурсы GPU, а родным для модуля ресайза является формат BGRA. Если мы вместо BGRA используем BGR, то будем иметь два недостатка. Первый – чуть ниже итоговый FPS (в наших тестах – процентов на 20). Второй — постоянный ворнинг в консоли «PiCameraAlfaStripping: using alpha-stripping to convert to non-alpha format; you may find equivalent alpha format faster”. Гугление оного и привело в раздел документации Picamera, в котором и рассказывается про эту хитрость. - А где PiRGBArray?
Это же вроде родной класс Picamera для работы с камерой, а тут он не используется. Так уже вышло, что в наших тестах работа с «приготовленным вручную» массивом numpy идет значительно быстрее (раза эдак в полтора), чем с использованием PiRGBArray. Это не значит, что PiRGBArray плохой, скорее всего это наши кривые руки. - Насколько загружен проц при расчете карты глубин?
Давайте отвечу картинкой:
Мы видим, что из 4 ядер проца загружено по сути только одно, и то на 70%. И это при том, что мы работаем с GUI, и у нас идет вывод картинок и карты глубин юзеру. Это означает, что имеется неплохой запас производительности, и тонкий тюнинг OpenCV с OpenMP и другими плюшками на C, а также «боевой» режим без GUI могут дать очень интересные результаты. - Какой максимальный FPS карты глубин получается при этих настройках?
Максимальный достигнутый нами составил 17 FPS, при захвате с камеры 20 кадров в секунду. Самыми «отзывчивыми» в плане скорости параметрами в настройках карты глубин являются MinDisparity и NumOfDisparities. Это логично, так как они определяют количество «шагов», проделываемых внутри алгоритма поисковым окошком сравнения кадров. Вторым по отзывчивости является preFilterCap, он влияет, в частности, на «сглаженность» карты глубин. - Что с температурой процессора?
На Compute Module 3+ Lite (новая серия, с железной «шапкой» на проце) показывает примерно такие результаты:
- Как использовать GPU?
Как минимум его можно задействовать для андисторсии и ректификации картинок в реальном времени, ибо есть примеры (вот на WebGL ), Питоновый Pi3d, а также проект Processing (примеры для малины).
Есть еще интересная разработка Koichi Nakamura, под названием py-videocore. В нашей с ним переписке он выразил идею, что для ускорения StereoBM можно использовать его ядро и сорсы OpenCV с поддержкой Cuda. В общем для оптимизации – непаханый край, как говорится.
Спасибо за внимание, и вот обещанная ссылочка на исходники.
Комментарии (22)
vektory79
05.04.2019 21:05Интересно, насколько реально присадить такое на квадрокоптер?
P.S. У самого опыта с квадрокоптерами нету, но ради такого...Realizator Автор
05.04.2019 21:18На коптер — реально, и есть планы это сделать. Собственно коптер, на который это будет потом ставиться, и снят на последнем видео, только у него лапы сложены. Делаться это будет не на питоне конечно, так как там задача жесткого реального времени. А эта статья для тех, кто только начинает осваивать тему — уж больно питон в этом плане прост и дружелюбен.
vektory79
05.04.2019 21:34А питания хватит? Все-таки pi последних моделей уж очень жручие, на мой взгляд.
Realizator Автор
06.04.2019 13:02Заявленное максимальное потребление малины 2,4 А. Тут пара мыслей есть. Во-первых, прожорливость всего остального силового оборудования на коптере намного выше (если мы говорим о большом коптере). Во-вторых, 2,4А подразумевается когда на малине нагружено все что можно, в частности полностью загружена видяха (рисует какую-нибудь 3D игрушку). В наших тестах, когда к малине подклбчено 2 камеры и она стримит стереопоток разрешения HD, суммарное потребление 0,7А при 5 вольтах. А если воткнуть мощный WiFi (Alfa AWUS 036NEH, 1 Ватт) — то вместе со свистком получается 0,9А.
vektory79
06.04.2019 14:26Ну если только транслировать на шлем — то да. Но имея такой мат.аппарат можно же идти дальше!
Например есть проблема определения препятствий. Особенно в воздухе. Это или очень дорого или очень ненадёжно. А тут можно определять даже весьма мелкие преграды прямо во всём кадре, корректируя движение.
Но тогда это ударит по питанию...Realizator Автор
07.04.2019 14:53С трансляцией мы уже наигрались. Задача стоит как раз помогать пилоту «не воткнуться» в препятствие, с перехватом управления у APM (Pixhawk) при неизбежном столкновении.
slovak
06.04.2019 00:53сорсы OpenCV с поддержкой Cuda
На малинке?Realizator Автор
06.04.2019 13:13<Бу!> Это инфа из раздела для продвинутых юзеров. Я подразумевал, что такой юзер полезет на гитхаб к Накамуре, посмотрит там краткое описание возможностей малиновой видяхи (например It has 12 quad processor units (QPU) which is a dual-issue 16 way (4 way pipelined and 4 way true) SIMD processor.), скачает даташит, и проведет аналогии с архитектурой этого видеоядра и мощных зверей от NVidia. </Бу!>
Красота идеи Накамуры в том, что можно выжать из малинового SoC ту производительность, которая от этого железа вообще не ожидается. И нестандартно использовать кудовские исходники OpenCV для получения нужных нам результатов.
Чтобы пояснить подход, давайте я приведу в качестве примера вторую идею. Для аппаратного получения карты глубин можно использовать DSP малинового SoC, предназначенного для сжатия видео в H264. При этом ядро ARM не будет нагружаться вообще. Для этого надо понимать как работает H264, знать что такое карта векторов смещения, ну и знать, что эту готовую карту SoC умеет отдавать в готовом виде.slovak
06.04.2019 23:11Да, знатно. А не практичнее ли взять тот же Jetson Nano?
По сути эксплуатация GPU на десктопах как раз так и начиналась, как Вы описываете. А потом уже куду подогнали.Realizator Автор
07.04.2019 14:48Джетсон нано — очень клевый свежак, он анонсирован месяц назад. Раньше обычный джетсон стоил 300 баксов, и кроватка для него 200 — дорого было. А сейчас Нано отличная железка за $100. Радиатор правда конского размера :-) СтереоПи первого поколения почти 3 года назад родилась, текущее — год назад. Но Джетсон Нано это просто другая плата, их помимо джетсона много. У малины сейчас самый главный козырь — нереальное количество примеров и активное сообщество с поддержкой. У джетсона — Куда, и с нейронками всё чудесно.
alex319
06.04.2019 07:45+1Спасибо, много полезного!
Мы видим, что из 4 ядер проца загружено по сути только одно, и то на 70%
— просто на всякий случай вопрос, OpenCV скомпилено под 4 ядра?Realizator Автор
06.04.2019 13:23Это боль OpenCV. Скомпилено оно вроде как с использованием OpenMP, но реализация такова, что профит ощущается только на очень больших разрешениях. Так что, по хорошему, надо лезть в код и внимательно тюнить распараллеливание под текущее железо.
Вообще авторы сборок на Python Wheels (готовые бинарники), товарищи из Raspberry, скомпилили всё очень грамотно в плане оптимизации под наборы команд малинового проца. Эти советы подробно расписаны у Adrian на pyimagesearch.com, и по ним эта компиляция и делалась.
Armavir
07.04.2019 14:48а как можно «скомпилить» под 4 ядра, если реализация однопоточная?
Realizator Автор
07.04.2019 14:49Есть и многопоточная. А для диспарити мест для распараллеливания громадьё, можно и свое распараллеливание написать.
Armavir
07.04.2019 17:33в с++ opencv есть две реализации stereoBM. Первая однопоточная, другая в ветки contrib, под cuda. В opencv априори однопоточность или cuda, даже opencl не реализованно корректно. Поэтому чтобы исполнять код на нескольких потоках, нужно исходники править и заново все пересобирать. А с учетом того, что реализация многих методов в opencv написана на simd, это вообще бессмысленно
Realizator Автор
08.04.2019 12:30+1В математике есть два подхода к решению задачи. Первый — доказать, что решение не существует. Второй — начти решение, пусть не всеобъемлющее и работающее лишь в определенной области и при определенном наборе параметров. Правило Лопиталя и многие другие вещи работают именно так. Мы сторонники второго подхода.
Да, готового решения нет. Скопипастить не получится. Заинклудить либу тоже. Надо напрягать мозг, и сильно. Планируем это делать.
prostodeb
06.04.2019 13:14А что за Линукс у вас такой интересный стоит?
Realizator Автор
06.04.2019 13:19О-о-о-о, это очень редкий зверь! Зовется Raspbian. Разрабатывался специально для экзотической, малодоступной, известной только специалистам платы Raspberry Pi. Про него практически ничего невозможно нагуглить, ну и тем более прочитать в разделе «Что установлено» в этой статье. Его должны хорошо помнить старики, специалисты по древним технологиям, которые нынче занимаются исключительно апдейтом граммофонов до уровня патефонов.
rPman
06.04.2019 19:19Про качество 3d-карты глубины, на кдпв, вся правая часть изображения покрыта дикими артефактами, с этим что то удалось сделать?
Realizator Автор
07.04.2019 14:51Мы не любим фейки, поэтому выложено реальное видео после реальной калибровки, и все исходники включая картинки.
Причина артефактов — после калибровки вокруг картинок появляются черные поля (см. самый конец видео с калибровкой). Артефакты тут как раз мэтчинг одной картинки с черными полями другой. Это всё фиксится либо перекалибровкой (в нашем сете есть картинки, на которых шахматная доска найдена, но некорректно), либо настройками с обрезкой черных зон.
В данном случае эти поля полезны для тех, кто пытается разобраться в теме калибровки и нюансах.
shadowlord
Прошу прощения за оффтоп, давно интересует — что стало с проектом виртурилки?
Realizator Автор
Ну сама виртурилка уже сошла с конвейера, так как железо сильно устарело. StereoPi это по сути виртурилка 2.0 и есть :-) Все наши прошлые наработки уже давно портированы на малину и другие железки, поэтому в наших проектах мы к аппаратной части сейчас не привязаны. Ну а так как Raspberry с двумя камерами не бывает — мы сделали свою. Такая вот история. Прямо сейчас делается тираж