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


  1. shadowlord
    05.04.2019 16:40

    Прошу прощения за оффтоп, давно интересует — что стало с проектом виртурилки?


    1. Realizator Автор
      05.04.2019 16:42

      Ну сама виртурилка уже сошла с конвейера, так как железо сильно устарело. StereoPi это по сути виртурилка 2.0 и есть :-) Все наши прошлые наработки уже давно портированы на малину и другие железки, поэтому в наших проектах мы к аппаратной части сейчас не привязаны. Ну а так как Raspberry с двумя камерами не бывает — мы сделали свою. Такая вот история. Прямо сейчас делается тираж


  1. vektory79
    05.04.2019 21:05

    Интересно, насколько реально присадить такое на квадрокоптер?
    P.S. У самого опыта с квадрокоптерами нету, но ради такого...


    1. Realizator Автор
      05.04.2019 21:18

      На коптер — реально, и есть планы это сделать. Собственно коптер, на который это будет потом ставиться, и снят на последнем видео, только у него лапы сложены. Делаться это будет не на питоне конечно, так как там задача жесткого реального времени. А эта статья для тех, кто только начинает осваивать тему — уж больно питон в этом плане прост и дружелюбен.


      1. vektory79
        05.04.2019 21:34

        А питания хватит? Все-таки pi последних моделей уж очень жручие, на мой взгляд.


        1. Realizator Автор
          06.04.2019 13:02

          Заявленное максимальное потребление малины 2,4 А. Тут пара мыслей есть. Во-первых, прожорливость всего остального силового оборудования на коптере намного выше (если мы говорим о большом коптере). Во-вторых, 2,4А подразумевается когда на малине нагружено все что можно, в частности полностью загружена видяха (рисует какую-нибудь 3D игрушку). В наших тестах, когда к малине подклбчено 2 камеры и она стримит стереопоток разрешения HD, суммарное потребление 0,7А при 5 вольтах. А если воткнуть мощный WiFi (Alfa AWUS 036NEH, 1 Ватт) — то вместе со свистком получается 0,9А.


          1. vektory79
            06.04.2019 14:26

            Ну если только транслировать на шлем — то да. Но имея такой мат.аппарат можно же идти дальше!
            Например есть проблема определения препятствий. Особенно в воздухе. Это или очень дорого или очень ненадёжно. А тут можно определять даже весьма мелкие преграды прямо во всём кадре, корректируя движение.
            Но тогда это ударит по питанию...


            1. Realizator Автор
              07.04.2019 14:53

              С трансляцией мы уже наигрались. Задача стоит как раз помогать пилоту «не воткнуться» в препятствие, с перехватом управления у APM (Pixhawk) при неизбежном столкновении.


  1. slovak
    06.04.2019 00:53

    сорсы OpenCV с поддержкой Cuda
    На малинке?


    1. 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 умеет отдавать в готовом виде.


      1. slovak
        06.04.2019 23:11

        Да, знатно. А не практичнее ли взять тот же Jetson Nano?
        По сути эксплуатация GPU на десктопах как раз так и начиналась, как Вы описываете. А потом уже куду подогнали.


        1. Realizator Автор
          07.04.2019 14:48

          Джетсон нано — очень клевый свежак, он анонсирован месяц назад. Раньше обычный джетсон стоил 300 баксов, и кроватка для него 200 — дорого было. А сейчас Нано отличная железка за $100. Радиатор правда конского размера :-) СтереоПи первого поколения почти 3 года назад родилась, текущее — год назад. Но Джетсон Нано это просто другая плата, их помимо джетсона много. У малины сейчас самый главный козырь — нереальное количество примеров и активное сообщество с поддержкой. У джетсона — Куда, и с нейронками всё чудесно.


  1. alex319
    06.04.2019 07:45
    +1

    Спасибо, много полезного!

    Мы видим, что из 4 ядер проца загружено по сути только одно, и то на 70%
    — просто на всякий случай вопрос, OpenCV скомпилено под 4 ядра?


    1. Realizator Автор
      06.04.2019 13:23

      Это боль OpenCV. Скомпилено оно вроде как с использованием OpenMP, но реализация такова, что профит ощущается только на очень больших разрешениях. Так что, по хорошему, надо лезть в код и внимательно тюнить распараллеливание под текущее железо.
      Вообще авторы сборок на Python Wheels (готовые бинарники), товарищи из Raspberry, скомпилили всё очень грамотно в плане оптимизации под наборы команд малинового проца. Эти советы подробно расписаны у Adrian на pyimagesearch.com, и по ним эта компиляция и делалась.


    1. Armavir
      07.04.2019 14:48

      а как можно «скомпилить» под 4 ядра, если реализация однопоточная?


      1. Realizator Автор
        07.04.2019 14:49

        Есть и многопоточная. А для диспарити мест для распараллеливания громадьё, можно и свое распараллеливание написать.


        1. Armavir
          07.04.2019 17:33

          в с++ opencv есть две реализации stereoBM. Первая однопоточная, другая в ветки contrib, под cuda. В opencv априори однопоточность или cuda, даже opencl не реализованно корректно. Поэтому чтобы исполнять код на нескольких потоках, нужно исходники править и заново все пересобирать. А с учетом того, что реализация многих методов в opencv написана на simd, это вообще бессмысленно


          1. Realizator Автор
            08.04.2019 12:30
            +1

            В математике есть два подхода к решению задачи. Первый — доказать, что решение не существует. Второй — начти решение, пусть не всеобъемлющее и работающее лишь в определенной области и при определенном наборе параметров. Правило Лопиталя и многие другие вещи работают именно так. Мы сторонники второго подхода.
            Да, готового решения нет. Скопипастить не получится. Заинклудить либу тоже. Надо напрягать мозг, и сильно. Планируем это делать.


  1. prostodeb
    06.04.2019 13:14

    А что за Линукс у вас такой интересный стоит?


    1. Realizator Автор
      06.04.2019 13:19

      О-о-о-о, это очень редкий зверь! Зовется Raspbian. Разрабатывался специально для экзотической, малодоступной, известной только специалистам платы Raspberry Pi. Про него практически ничего невозможно нагуглить, ну и тем более прочитать в разделе «Что установлено» в этой статье. Его должны хорошо помнить старики, специалисты по древним технологиям, которые нынче занимаются исключительно апдейтом граммофонов до уровня патефонов.


  1. rPman
    06.04.2019 19:19

    Про качество 3d-карты глубины, на кдпв, вся правая часть изображения покрыта дикими артефактами, с этим что то удалось сделать?


    1. Realizator Автор
      07.04.2019 14:51

      Мы не любим фейки, поэтому выложено реальное видео после реальной калибровки, и все исходники включая картинки.
      Причина артефактов — после калибровки вокруг картинок появляются черные поля (см. самый конец видео с калибровкой). Артефакты тут как раз мэтчинг одной картинки с черными полями другой. Это всё фиксится либо перекалибровкой (в нашем сете есть картинки, на которых шахматная доска найдена, но некорректно), либо настройками с обрезкой черных зон.
      В данном случае эти поля полезны для тех, кто пытается разобраться в теме калибровки и нюансах.