- PVSM.RU - https://www.pvsm.ru -

Сегодня мы хотим поделиться серией примеров на Питоне для изучающих OpenCV на Raspberry Pi, а именно для двухкамерной платы StereoPi. Готовый код (плюс образ Raspbian) поможет пройти все шаги, начиная c захвата картинки и заканчивая получением карты глубин из захватываемого видео.
Сразу подчеркну, что эти примеры – для комфортного погружения в тему, а не для продакшн-решения. Если вы продвинутый юзер OpenCV и имели дело с малиной, то знаете, что для полноценной работы желательно кодить на сишечке, да еще и задействовать малиновый GPU. В конце статьи я чуть подробнее коснусь «бутылочных горлышек» питонового решения и производительности в целом.
В качестве железа у нас вот такой вот сетап:

Плата StereoPi, на борту Raspberry Pi Compute Module 3+. Подключены две самые простые камеры для Raspberry Pi версии V1 (на сенсоре ov5647).
Что установлено:
Процесс установки всего софта выходит за рамки этой статьи, и мы просто предлагаем скачать готовый образ 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, то может поломаться процесс взаимодействия Питона с камерой, и потребуется перезагрузка малины.
Резонный вопрос: зачем всё это, и почему не захватить сразу уменьшенную картинку и не загружать наш питон ее уменьшением?
Ответ: при прямом захвате на очень низких разрешениях в малиновом ядре пока есть проблемы (картинка ломается). Поэтому мы берем разрешение побольше, а затем картинку уменьшаем. Тут мы используем маленький трюк: картинка скейлится не питоном, а с помощью GPU, поэтому нагрузки на армовое ядро нет.
Это же вроде родной класс Picamera для работы с камерой, а тут он не используется. Так уже вышло, что в наших тестах работа с «приготовленным вручную» массивом numpy идет значительно быстрее (раза эдак в полтора), чем с использованием PiRGBArray. Это не значит, что PiRGBArray плохой, скорее всего это наши кривые руки.

Мы видим, что из 4 ядер проца загружено по сути только одно, и то на 70%. И это при том, что мы работаем с GUI, и у нас идет вывод картинок и карты глубин юзеру. Это означает, что имеется неплохой запас производительности, и тонкий тюнинг OpenCV с OpenMP и другими плюшками на C, а также «боевой» режим без GUI могут дать очень интересные результаты.
Максимальный достигнутый нами составил 17 FPS, при захвате с камеры 20 кадров в секунду. Самыми «отзывчивыми» в плане скорости параметрами в настройках карты глубин являются MinDisparity и NumOfDisparities. Это логично, так как они определяют количество «шагов», проделываемых внутри алгоритма поисковым окошком сравнения кадров. Вторым по отзывчивости является preFilterCap, он влияет, в частности, на «сглаженность» карты глубин.
На Compute Module 3+ Lite (новая серия, с железной «шапкой» на проце) показывает примерно такие результаты:

Как минимум его можно задействовать для андисторсии и ректификации картинок в реальном времени, ибо есть примеры (вот на WebGL [1] ), Питоновый Pi3d [2], а также проект Processing (примеры для малины [3]).
Есть еще интересная разработка Koichi Nakamura, под названием py-videocore [4]. В нашей с ним переписке он выразил идею, что для ускорения StereoBM можно использовать его ядро и сорсы OpenCV с поддержкой Cuda [5]. В общем для оптимизации – непаханый край, как говорится.
Спасибо за внимание, и вот обещанная ссылочка на исходники [6].
Автор: Евгений
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/diy/313693
Ссылки в тексте:
[1] вот на WebGL: https://jywarren.github.io/fisheyegl/example/#a=1&b=1&Fx=0.35&Fy=0.36&scale=1.087&x=1&y=1
[2] Pi3d: https://pi3d.github.io
[3] примеры для малины: https://pi.processing.org/tutorial/camera/#using-glsl-shaders-for-improved-performance
[4] py-videocore: https://github.com/nineties/py-videocore
[5] с поддержкой Cuda: https://docs.opencv.org/3.4/db/d8a/classcv_1_1cuda_1_1StereoBM.html
[6] обещанная ссылочка на исходники: https://github.com/realizator/stereopi-tutorial
[7] Источник: https://habr.com/ru/post/446872/?utm_source=habrahabr&utm_medium=rss&utm_campaign=446872
Нажмите здесь для печати.