Headtracking или как увидеть трехмерный чайник на двухмерном мониторе

:

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

Где глаза?


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

Что видят глаза?


Итак, позиции глаз у нас есть, теперь надо узнать, что же они видят. Кстати предполагается, что пользователь будет циклопом с одним рабочим глазом как раз между двумя реальными. Первая попытка сделать все легко и просто, прикрутив виртуальную камеру в OpenGL к тому месту, где должен быть глаз пользователя не увенчалась успехом, так как было совсем не реалистично.На видео можно посмотреть как это выглядело:

Чисто субъективно, плохо было то, что чайник сильно увеличивался при приближении. Стал я думать, что же тут не так и даже провел простенький эксперимент с подручными средствами:

Обратить внимание надо на размеры телефона относительно размеров отверстия. Получается, что при приближении наш чайник должен не увеличиваться, а, наоборот, уменьшаться (для случая, когда он за «окном»). Вот так это примерно выглядит:

Тут же кроется еще одна проблема, при взгляде сбоку, искажаются пропорции. Проблема заключается в том, что OpenGL рендерит нам картинку как-будто она перпендикулярна взгляду, но пользователь видит плоскость монитора под углом.

В результате, изображение получается искаженным.

Причем на картинке правильное изображение находится справа, а не слева. Если посмотреть на него справа от монитора (примерно там находилась голова пользователя), то оно будет правильным, а левое искаженным.

Как исправить увиденное?


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

Теперь надо сделать так, чтобы содержимое этой области растянулось на все окно. Для этого мы проецируем точки, соответствующие углам реального окна на мониторе в 3д пространстве на плоскость дисплея, куда рендерит картинку OpenGL (фактически перемножаем координаты этих точек на матрицу проецирования), находим гомографию (гомография — это такая матрица, которая позволяет установить соответствие точек одного изображению точкам другого, найти ее можно с помощью OpenCV или написать все ручками) между получившимся четырехугольником и прямоугольником окна на реальном дисплее и мапим наше отрендеренное изображение к прямоугольнику окна реального дисплея (опять же OpenCV нам в помощь).

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

Правда есть еще одна мелочь: если пользователь находится далеко от монитора, то с постоянным FOV получается, что полезная часть изображения (собственно то, что видно через монитор) получается довольно маленькой.

и финальная картинка имеет низкое разрешение:

Для борьбы с этим я пересчитываю FOV так, чтобы полезная картинка занимала чуть меньше, чем наибольшая возможная площадь:

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

Что получилось?


Результаты всего этого можно наблюдать на видео:

А теперь несколько комментариев, как оно работает с субъективной точки зрения и почему:

1) Самая большая проблема — маленькие углы обзора веб-камеры на ноутбуке. Поэтому довольно легко выйти за пределы видимости и изображение останавливается. Не приспособлена она для такого. Но радует то, что все достаточно быстро и в плане скорости проблем нет. Есть в природе более широкоугольные, но надо такую раздобыть.

2) Обмануть человеческий мозг не так то просто. Двумя глазами я точно вижу, что монитор плоский и без стерео такое 3д не проходит. Даже если закрыть один глаз, то мозг, все равно, знает, что это плоский монитор и никакого чайника там быть не может. Но есть один нюанс, если закрыть один глаз и постоянно представлять себе трехмерный чайник, то через какое-то время можно обмануть мозг.

3) Не все так просто с проекциями, как кажется на первый взгляд. Лично для меня было довольно странно, что объект должен уменьшаться при приближении если он находится за экраном. Кстати тут скрывается баг, чайник мой, все-таки, объемный и находился как раз в плоскости экрана, поэтому делать так, как делал я нельзя. Надо пересчитывать позицию каждой вершины чайника отдельно.

4) Попробую в следующем семестре сделать тоже самое, но с поддержкой стерео 3д и отдельной картинкой для каждого глаза. Посмотрим, поверит ли тогда мозг.

UPDATE:


По просьбам трудящихся, выкладываю исходники и бинарники. В бинарники включил все дллки, поэтому получилось 24 метра в архиве.

Еще, похоже, что я что-то там сломал в ходе экспериментов, чайник сейчас какой-то очень уж вытянутый (такое ощущение, что где-то поменялся знак при проецировании, поэтому чайник растягивается не в ту сторону), но, кому интересно, разберется.

И еще одна особенность, там будет два окна с чайником, надо одно переместить на другое (просто перетянуть), чтобы было правильно мне так для отладки было удобней).

Потерянная модель чайника может быть найдена тут и должен лежать тут: C:\_WORK\__3d_Models\