Создание недорогой стереокамеры с использованием OpenCV

В этом посте мы узнаем, как создать нестандартную недорогую стереокамеру (используя пару веб-камер) и снимать с ее помощью 3D-видео с помощью OpenCV. Мы предоставляем код на Python.

В частности, вы узнаете следующее:

  1. Шаги создания и настройки стереокамеры
  2. Важность стереокалибровки и коррекции
  3. Шаги стереокалибровки и коррекции
  4. Как работают 3D-очки?
  5. Создание собственного 3D-видео

Пример 3D-видео. (Источник)

Нам всем нравятся 3D-фильмы и видео, подобные показанному выше. Чтобы испытать 3D-эффект, вам потребуются красно-голубые 3D-очки, подобные изображенному на рисунке 1. Как это работает? Как мы можем испытать 3D-эффект, когда экран просто плоский? Они сняты с помощью стереокамеры.

Рисунок 1 - красно-голубые 3D-очки
Рисунок 1 — красно-голубые 3D-очки

В предыдущем посте мы узнали о стереокамерах и о том, как они используются, чтобы компьютер мог воспринимать глубину. В этом посте мы научимся создавать нашу собственную стереокамеру и поймем, как ее можно использовать для создания 3D-видео.

Шаги по созданию настройки стереокамеры

Установка стереокамеры обычно содержит две идентичные камеры, расположенные на фиксированном расстоянии друг от друга. В стандартных стереокамерах промышленного класса используется идентичная пара камер.

Чтобы создать такой дома, нам потребуется следующее:

  1. Две веб-камеры USB (желательно одной модели).
  2. Жесткое основание для крепления фотоаппаратов (дерево, картон), Пенопласт ПВХ).
  3. Зажимы или изолента.

Можно проявить творческий подход в использовании различных компонентов для создания стереокамеры, но главное требование — держать камеры жестко фиксированными и параллельными.

Рисунок 2 - Изображение моей стереокамеры DIY (слева) и набора OpenCV AI с глубиной (OAK-D) (справа). Две камеры по бокам OAKD образуют стереокамеру. Источник изображения ДУБ-Д.
Рисунок 2 — Изображение моей стереокамеры DIY (слева) и набора OpenCV AI с глубиной (OAK-D) (справа). Две камеры по бокам OAKD образуют стереокамеру. Источник изображения OAK-D.

Многие люди создали и поделились своими настройками стереокамер, подобными моей, показанной на левом изображении на рис. 2, или той, которая была опубликована в этой публикации.

Как только мы исправим камеры и убедимся, что они правильно выровнены, мы закончили? Готовы ли мы создавать карты несоответствий и 3D-видео?

Важность стереокалибровки и ректификации

Чтобы понять важность стереокалибровки и стерео-ректификации, мы пытаемся создать карту несоответствия, используя изображения, снятые с нашей стереосистемы, без какой-либо калибровки или стерео-ректификации.

Рисунок 3 - Левое и правое изображения, снятые с помощью стереокамеры.
Рисунок 3 — Левое и правое изображения, снятые с помощью стереокамеры.

Рисунок 4 - Карта диспаратности, созданная с использованием правого и левого изображений без стереокалибровки
Рисунок 4 — Карта диспаратности, созданная с использованием правого и левого изображений без стереокалибровки

Мы заметили, что карта несоответствия, созданная с помощью некалиброванной стереокамеры, очень зашумленная и неточная. Почему так происходит?

Исходя из предыдущего поста, соответствующие ключевые точки должны иметь одинаковые координаты Y, чтобы упростить поиск соответствий точек. На рисунке 5, когда мы наносим совпадающие линии между несколькими соответствующими точками, мы замечаем, что линии не полностью горизонтальны.

Рисунок 5 - Отображение совпадений между соответствующими точками.
Рисунок 5 — Отображение совпадений между соответствующими точками.

Также замечено, что координаты Y соответствующих точек не равны. На рисунке 6 показаны пара стереоизображений с точечным соответствием и карта несоответствия, созданная с использованием этих изображений. Мы видим, что теперь карта диспаратности менее зашумлена по сравнению с предыдущей. В этом случае соответствующие ключевые точки имеют одинаковые координаты Y. Такой случай возможен только при параллельном расположении камер. Это особый случай геометрии с двумя проекциями, когда изображения параллельны и связаны только горизонтальным перемещением. Это важно, поскольку метод, используемый для создания карты диспаратности, ищет соответствие точек только по горизонтали.

Рисунок 6 - Соответствие признаков для пары стереоизображений и сгенерированной карты несоответствия
Рисунок 6 — Соответствие признаков для пары стереоизображений и сгенерированной карты несоответствия

Потрясающе! Все, что нам нужно сделать, это выровнять наши камеры и сделать их полностью параллельными. Так можно ли настраивать камеры вручную методом проб и ошибок? Что ж, в качестве забавного занятия вы можете попробовать! Осторожно, спойлеры!! Настройка камер вручную для получения четкой карты несоответствия займет много времени. Более того, каждый раз, когда установка нарушается и камеры перемещаются, мы должны повторить этот процесс. Это трудоемкое и не идеальное решение.

Вместо того, чтобы физически настраивать камеры, мы делаем это на стороне программного обеспечения. Мы используем метод под названием «Ректификация стереоизображения». [1] Рисунок 7 поясняет процесс выравнивания стерео. Идея состоит в том, чтобы повторно спроектировать два изображения на общую плоскость, параллельную линии, проходящей через оптические центры. Это гарантирует, что соответствующие точки имеют одинаковую координату Y и связаны простым горизонтальным перемещением.

Рисунок 7 - Процесс стерео ректификации
Рисунок 7 — Процесс стерео ректификации

Основываясь на публикации об искажении объектива, мы знаем, что изображения, снятые камерой, страдают от искажения объектива. Следовательно, помимо стерео-ректификации, также важно получать не искаженные изображения. Итак, общий процесс выглядит следующим образом:

  1. Откалибруйте отдельные камеры, используя стандартный метод калибровки OpenCV, описанный в посте калибровки камеры.
  2. Определите преобразование между двумя камерами, используемыми в настройке стереокамеры.
  3. Используя параметры, полученные на предыдущих шагах, и метод stereoCalibrate, мы определяем преобразования, применяемые к обоим изображениям для стерео-ректификации.
  4. Наконец, отображение, необходимое для поиска пары неискаженного и выпрямленного стереоизображения, получается с помощью метода initUndistortRectifyMap.
  5. Это сопоставление применяется к исходным изображениям, чтобы получить пару исправленных неискаженных стереоизображений.

Чтобы выполнить эти шаги, мы снимаем изображения калибровочного шаблона. В следующем видео показаны различные изображения, снятые для калибровки стереокамеры DIY.

Давайте разберемся с кодом для калибровки и исправления.

Шаг 1: Индивидуальная калибровка правой и левой камер стереосистемы

Видео, показывающее процесс захвата изображения для стереокалибровки

Перед выполнением стереокалибровки мы выполняем индивидуальную калибровку обеих камер. Но зачем нам калибровать камеры по отдельности, если метод stereoCalibrate() также может выполнять калибровку каждой из двух камер? Поскольку существует множество параметров для вычисления (большое пространство параметров) и накопление ошибок на таких этапах, как обнаружение углов и приближение точек к целым числам. Это увеличивает шансы отклонения итерационного метода от правильного решения.Следовательно, мы рассчитываем параметры камеры индивидуально и используем метод stereoCalibrate() только для нахождения преобразования между парой стереокамер, основной матрицей и основной матрицей. Но как алгоритм узнает, что калибровку отдельных камер нужно пропустить? Для этого мы устанавливаем флаг CALIB_FIX_INTRINSIC и передаем его методу.

# Установите путь к изображениям, снятым левой и правой камерами
pathL = "./data/stereoL/"
pathR = "./data/stereoR/"

# Критерии прекращения доработки обнаруженных углов
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)


objp = np.zeros((9*6,3), np.float32)
objp[:,:2] = np.mgrid[0:9,0:6].T.reshape(-1,2)

img_ptsL = []
img_ptsR = []
obj_pts = []

for i in tqdm(range(1,12)):
	imgL = cv2.imread(pathL+"img%d.png"%i)
	imgR = cv2.imread(pathR+"img%d.png"%i)
	imgL_gray = cv2.imread(pathL+"img%d.png"%i,0)
	imgR_gray = cv2.imread(pathR+"img%d.png"%i,0)

	outputL = imgL.copy()
	outputR = imgR.copy()

	retR, cornersR =  cv2.findChessboardCorners(outputR,(9,6),None)
	retL, cornersL = cv2.findChessboardCorners(outputL,(9,6),None)

	if retR and retL:
		obj_pts.append(objp)
		cv2.cornerSubPix(imgR_gray,cornersR,(11,11),(-1,-1),criteria)
		cv2.cornerSubPix(imgL_gray,cornersL,(11,11),(-1,-1),criteria)
		cv2.drawChessboardCorners(outputR,(9,6),cornersR,retR)
		cv2.drawChessboardCorners(outputL,(9,6),cornersL,retL)
		cv2.imshow('cornersR',outputR)
		cv2.imshow('cornersL',outputL)
		cv2.waitKey(0)

		img_ptsL.append(cornersL)
		img_ptsR.append(cornersR)


# Калибровка левой камеры
retL, mtxL, distL, rvecsL, tvecsL = cv2.calibrateCamera(obj_pts,img_ptsL,imgL_gray.shape[::-1],None,None)
hL,wL= imgL_gray.shape[:2]
new_mtxL, roiL= cv2.getOptimalNewCameraMatrix(mtxL,distL,(wL,hL),1,(wL,hL))

# Калибровка правой камеры
retR, mtxR, distR, rvecsR, tvecsR = cv2.calibrateCamera(obj_pts,img_ptsR,imgR_gray.shape[::-1],None,None)
hR,wR= imgR_gray.shape[:2]
new_mtxR, roiR= cv2.getOptimalNewCameraMatrix(mtxR,distR,(wR,hR),1,(wR,hR))

Шаг 2: Выполнение стереокалибровки с фиксированными внутренними параметрами

По мере калибровки камер мы передаем их методу stereoCalibrate() и устанавливаем флаг CALIB_FIX_INTRINSIC. Мы также передаем 3D-точки и соответствующие 2D-пиксельные координаты, захваченные на обоих изображениях. Метод вычисляет вращение и перенос между двумя камерами и матрицей Essential и Fundamental.

flags = 0
flags |= cv2.CALIB_FIX_INTRINSIC
# Здесь мы исправляем внутренние матрицы камеры, чтобы вычислялись только Rot, Trns, Emat и Fmat.
# Следовательно, внутренние параметры совпадают
criteria_stereo= (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)

# Этот шаг выполняется для преобразования между двумя камерами и вычисления матрицы Essential и Fundamenatl
retS, new_mtxL, distL, new_mtxR, distR, Rot, Trns, Emat, Fmat = cv2.stereoCalibrate(obj_pts, img_ptsL, img_ptsR, new_mtxL, distL, new_mtxR, distR, imgL_gray.shape[::-1], criteria_stereo, flags)

Шаг 3: Стерео ректификация

Теперь, используя внутренние свойства камеры, а также вращение и перемещение между камерами, мы можем применить стерео исправление. Стерео исправление применяет вращения, чтобы обе плоскости изображения камеры находились в одной плоскости. Наряду с матрицами вращения метод stereoRectify также возвращает матрицы проекции в новом координатном пространстве.

rectify_scale= 1
rect_l, rect_r, proj_mat_l, proj_mat_r, Q, roiL, roiR = cv2.stereoRectify(new_mtxL, distL, new_mtxR, distR, imgL_gray.shape[::-1], Rot, Trns, rectify_scale,(0,0))

Шаг 4. Вычислите отображение, необходимое для получения пары неискаженных выпрямленных стереоизображений

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

Left_Stereo_Map= cv2.initUndistortRectifyMap(new_mtxL, distL, rect_l, proj_mat_l,
                                             imgL_gray.shape[::-1], cv2.CV_16SC2)
Right_Stereo_Map= cv2.initUndistortRectifyMap(new_mtxR, distR, rect_r, proj_mat_r,
                                              imgR_gray.shape[::-1], cv2.CV_16SC2)

print("Saving paraeters ......")
cv_file = cv2.FileStorage("improved_params2.xml", cv2.FILE_STORAGE_WRITE)
cv_file.write("Left_Stereo_Map_x",Left_Stereo_Map[0])
cv_file.write("Left_Stereo_Map_y",Left_Stereo_Map[1])
cv_file.write("Right_Stereo_Map_x",Right_Stereo_Map[0])
cv_file.write("Right_Stereo_Map_y",Right_Stereo_Map[1])
cv_file.release()

Как работают 3D-очки?

После того, как наша стереокамера DIY откалибрована, мы готовы создавать 3D-видео. Но прежде чем мы поймем, как снимать 3D-видео, важно понять, как работают 3D-очки.

Мы воспринимаем мир с помощью бинокулярного зрения. Наши глаза находятся в разных положениях. Следовательно, они собирают несколько иную информацию.

В чем разница между информацией, полученной нашим левым и правым глазом?

Проведем простой эксперимент! Вытяните руку вперед и возьмите в руку любой предмет. Теперь посмотрите на объект одним закрытым глазом. Через секунду повторите то же самое с другим глазом и продолжайте чередовать. Вы замечаете разницу в том, что видят оба ваших глаза?

Различия в основном заключаются в относительном горизонтальном положении предметов. Эти позиционные различия называются горизонтальными диспропорциями. В предыдущем посте мы рассчитали карту диспаратности, используя пару стереоизображений.

Сейчас, приблизьте объект к себе и повторите тот же эксперимент. Какие изменения вы заметили сейчас? Горизонтальные диспропорции, соответствующие объекту, увеличиваются. Следовательно, чем выше диспропорции для объекта, тем он ближе. Вот как мы используем стереопсис для восприятия глубины с помощью нашей системы бинокулярного зрения.

Мы можем смоделировать такое несоответствие, искусственно представляя два разных изображения отдельно каждому глазу, используя метод, называемый стереоскопией. Изначально для 3D-фильмов люди кодировали изображение каждого глаза с помощью фильтров красного и голубого цветов. Они использовали красно-голубые 3D-очки, чтобы каждое из двух изображений доходило до намеченного глаза. Это создавало иллюзию глубины. Стереоскопический эффект, создаваемый этим методом, называется анаглифным 3D. Следовательно, изображения называются анаглифическими изображениями, а очки — анаглифическими 3D-очками.

Создание собственного 3D-видео

Мы поняли, как пара стереоизображений преобразуется в анаглифическое изображение, чтобы создать иллюзию глубины при просмотре через анаглифные очки. Но как нам запечатлеть эти стереоизображения? Ага! Здесь мы используем стереокамеру, сделанную своими руками. Мы фиксируем стереоизображения с помощью нашей стереокамеры DIY и создаем анаглифическое изображение для каждой пары стереоизображений. Затем мы сохраняем все последовательные анаглифические изображения как видео. Вот как мы делаем 3D-видео!

Давайте погрузимся в код и создадим наше 3D-видео.

cv2.imshow("Left image before rectification", imgL)
cv2.imshow("Right image before rectification", imgR)

Left_nice= cv2.remap(imgL,Left_Stereo_Map[0],Left_Stereo_Map[1], cv2.INTER_LANCZOS4, cv2.BORDER_CONSTANT, 0)
Right_nice= cv2.remap(imgR,Right_Stereo_Map[0],Right_Stereo_Map[1], cv2.INTER_LANCZOS4, cv2.BORDER_CONSTANT, 0)

cv2.imshow("Left image after rectification", Left_nice)
cv2.imshow("Right image after rectification", Right_nice)
cv2.waitKey(0)

out = Right_nice.copy()
out[:,:,0] = Right_nice[:,:,0]
out[:,:,1] = Right_nice[:,:,1]
out[:,:,2] = Left_nice[:,:,2]

cv2.imshow("Output image", out)
cv2.waitKey(0)
3D-видео, созданное с использованием настраиваемой стереокамеры и кода, описанного выше

В первом посте «Введение в эпиполярную геометрию и стереозрение» обсуждались все фундаментальные концепции, связанные с геометрией двух представлений и стереозрением. Этот же пост был о создании недорогой стереокамеры, ее калибровке и использовании для создания собственных 3D-фильмов.

Литература

[1] C. Loop and Z. Zhang. Computing Rectifying Homographies for Stereo Vision. IEEE Conf. Computer Vision and Pattern Recognition, 1999.

По мотивам Making A Low-Cost Stereo Camera Using OpenCV

Print Friendly, PDF & Email

CC BY-NC 4.0 Создание недорогой стереокамеры с использованием OpenCV, опубликовано К ВВ, лицензия — Creative Commons Attribution-NonCommercial 4.0 International.


Респект и уважуха

Добавить комментарий