6. Скрипт 6_depth_map.py – карта глубин "с наскоку"

Назначение шестого скрипта

В этом скрипте мы пробуем быстро и легко построить карту глубин функцией StereoBM, на вход которой помимо стереопары подаются всего два параметра.

Скрипт делает следующее:

  • Загружает параметры для резки картинки на стереопару
  • Загружает указанную картинку для построения карты глубин
  • Строит карту глубин
  • Показывает результат пользователю
 
Запуск скрипта командой:

python 6_depth_map.py

Имя файла с картинкой, по которой строится карта глубин, зашито в коде скрипта, в следующей строчке:

imageToDisp = './scenes/photo.png'

Исходный код: https://github.com/realizator/3dberry-turorial/blob/master/6_depth_map.py

О подвохах

Результат, мягко говоря, совсем не тот, что ожидается. И очень часто пользователи, решившие просто поиграться с картой глубин, пытаются построить карту с несколькими вариантами параметров и заканчивают свое знакомство с этой технологией, оставшись при мнении "это всё ерунда и оно не работает".

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

Способы получения карты глубин

В OpenCV есть несколько алгоритмов построения карт глубин, или disparity map. В доках можно увидеть StereoBM и StereoSGBM (вот чтение на ночь). Нас интересует вариант StereoBM, так как в этой функции используется алгоритм реального времени. Вариант с StereoSGBM дает лучшее качество, но гораздо более требователен к вычислительным ресурсам.

Попробуем описать суть алгоритма в несколько предложений. У нас на входе есть два изображения одной и той-же сцены, полученных с камер, находящихся рядом друг с другом, оси которых параллельны. Объекты, находящиеся на дальнем плане, будут на левом и правом изображении находиться практически на однои и том-же месте. А вот изображение более близлежащих объектов уже будет отличаться - на левом изображении такие объекты будут находиться правее, а на правом - левее. Чем ближе объект к камере - тем сильнее будет отличаться его изображение на левой и правой картинках. Так вот, алгоритм пытается понять, насколько сильно изображение объекта смещено на левой и правой картинке. Расстояние смещения и называется disparity. Чем выше это значение - тем ближе объект к камере.

Функция StereoBM берет левую картинку и начинает построчно ее анализировать. На каждой строке алгоритм берет точку или их набор, и пытается найти похожий набор точек на этой-же строчке на правом изображении. После этого он считает, насколько эти наборы точек друг относительно друга смещены. Кстати в этом месте становится понятным, зачем нужна калибровка камер и ректификация изображений.

На вход функции StereoBM подается две картинки и два параметра -  ndisparities и SADWindowSize.

Первый параметр ndisparities указывает, насколько "далеко" алгоритм должен просмотреть строчку правой картинки в поисках нужного ему фрагмента. Если вы укажете ndisparities = 16 то это будет означать, что алгоритм будет "заглядывать" правее на 16 точек.

Параметр SADWindowSize это размер блока точек, который алгоритм будет искать. Если сделать этот параметр равным 1 - то будет идти попиксельный анализ картинки.

Первая причина плохого результата: алгоритм по умолчанию считает, что disparity может быть от 0 до ndisparities. Но случается так, что disparity бывает отрицательной (и это кстати наш случай), то-есть искать нужно "в обе стороны". Тут мы никак не можем повлиять на эту настройку, и алгоритм может просто не найти соответствий на левой и правой картинки кроме больших однотонных объектов.

Вторая причина плохого результата таится в неожиданном месте. Результат построения карты глубин - по сути массив, в ячейках которого находятся значения disparity. А они могут быть как отрицательными, так и положительными. Более того, максимальные и минимальные значения могут существенно отличаться. И если мы наш результат в чистом виде попросим показать в виде изображения с оттенками серого - то получаем мусор. Для корректного отображения нужно результат нормализовать, а именно - минимальное значение сделать равным 0, а максимальное 255. Тогда все данные корректно отобразятся. Дополнительно скажу, что в этом скрипте такая нормализация сделана. Этот блок в скрипте специально оставлен в "детальном" виде, чтобы можно было понять суть. В следующем скрипте нормализация сделана с помощью другой функции, скрывающей суть происходящего.