Сегодня мы хотим поделиться серией примеров на Питоне для изучающих 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. В общем для оптимизации – непаханый край, как говорится.
Спасибо за внимание, и вот обещанная ссылочка на исходники.