Изучаем OpenCV на StereoPi: карта глубин по видео

hbxtpox_r6mswlkshq4w3cpovr4.gif

Сегодня мы хотим поделиться серией примеров на Питоне для изучающих OpenCV на Raspberry Pi, а именно для двухкамерной платы StereoPi. Готовый код (плюс образ Raspbian) поможет пройти все шаги, начиная c захвата картинки и заканчивая получением карты глубин из захватываемого видео.

Вводная


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

С чем работаем


В качестве железа у нас вот такой вот сетап:

orpd9ufeuctr0lbmsk0kfogroao.jpeg

Плата 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. Это простой и удобный инструмент для подбора таких параметров изображения, при которых питон еще «не захлебывается». С его помощью мы и подобрали 1280×480 при 20 FPS, при которых видео отдается без задержек.
  • Вы можете заметить, что мы захватываем стереопару в разрешении 1280×480, а затем скейлим ее до 640×240.

    Резонный вопрос: зачем всё это, и почему не захватить сразу уменьшенную картинку и не загружать наш питон ее уменьшением?

    Ответ: при прямом захвате на очень низких разрешениях в малиновом ядре пока есть проблемы (картинка ломается). Поэтому мы берем разрешение побольше, а затем картинку уменьшаем. Тут мы используем маленький трюк: картинка скейлится не питоном, а с помощью 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 плохой, скорее всего это наши кривые руки.

  • Насколько загружен проц при расчете карты глубин?
    Давайте отвечу картинкой:

    nnezefyxuiuxx7difz1xctii16w.jpeg

    Мы видим, что из 4 ядер проца загружено по сути только одно, и то на 70%. И это при том, что мы работаем с GUI, и у нас идет вывод картинок и карты глубин юзеру. Это означает, что имеется неплохой запас производительности, и тонкий тюнинг OpenCV с OpenMP и другими плюшками на C, а также «боевой» режим без GUI могут дать очень интересные результаты.

  • Какой максимальный FPS карты глубин получается при этих настройках?

    Максимальный достигнутый нами составил 17 FPS, при захвате с камеры 20 кадров в секунду. Самыми «отзывчивыми» в плане скорости параметрами в настройках карты глубин являются MinDisparity и NumOfDisparities. Это логично, так как они определяют количество «шагов», проделываемых внутри алгоритма поисковым окошком сравнения кадров. Вторым по отзывчивости является preFilterCap, он влияет, в частности, на «сглаженность» карты глубин.

  • Что с температурой процессора?

    На Compute Module 3+ Lite (новая серия, с железной «шапкой» на проце) показывает примерно такие результаты:

    bap7kwdbbhd0y2bmvebpqzimqpa.jpeg

  • Как использовать GPU?

    Как минимум его можно задействовать для андисторсии и ректификации картинок в реальном времени, ибо есть примеры (вот на WebGL), Питоновый Pi3d, а также проект Processing (примеры для малины).

    Есть еще интересная разработка Koichi Nakamura, под названием py-videocore. В нашей с ним переписке он выразил идею, что для ускорения StereoBM можно использовать его ядро и сорсы OpenCV с поддержкой Cuda. В общем для оптимизации — непаханый край, как говорится.


Спасибо за внимание, и вот обещанная ссылочка на исходники.

© Habrahabr.ru