На всех ноутбуках и телефонах сейчас есть камера, поэтому можно с помощью tensoflow моделей анализировать положения головы и глаз.
Наверно всем известно что есть библиотека Tensorflow для нейронных сетей, она работает под языком Python и Javascript. Процесс свертки в нейронных сетях это достаточно тяжеловесное вычисление, которое хорошо параллелится и существуют версии не только для CPU, но и на CUDA под Python и WebGL/WebGPU под Javascript. Забавно, но если у вас не NVidia, то оффициальная сборка Tensorflow на языке Javascript будет на ПК будет работать быстрее, так как нет официальной сборки с поддержкой OpenGL. Но к счастью для всех TF 2.0 имеет модульную архитектуру, которая позволяет думать только о самой сути, а не языке, на котором это выполняется. Так же существуют конвертеры 1.0 -> 2.0, которые я пока сам особо не пробовал.
На данный момент есть две официальные модели для распознования лица: facemesh и blazeface. Первая больше подходит для мимики и масок, вторая шустрее и просто определяет квадрат и характерные точки, такие как глаза, уши, рот. Поэтому я взял легковесный вариант blazeface. Зачем лишняя информация? Вообще может быть можно существующую модель еще уменьшить, так как кроме положения глаз, мне больше ничего не нужно.
В браузере на данный момент существует 5 бэкендов: cpu, wasm, webgl, wasm-simd, webgpu. Первый СPU слишком медленный и его сейчас не стоит брать ни в каких случаях, последний два слишком инновационны и находятся на стадии пропосалов и работают под флагами, так что поддержка у конечных клиентов нулевая. Поэтому я выбирал из двух: WebGL и WASM.
По существующим бенчмаркам можно увидеть, что что для маленьких моделей WASM работает порой быстрее чем WebGL. Кроме этого, параллакс может использоваться с 3д сценами и запуская WASM бэкенд на них, я понял что WASM работает гораздо лучше, так как дискретные видеокарты ноутбуков, не вывозят одновременно нейросети и 3д сцены. Для этого я взял простую сцену с 75-ю гловами в .glb. Ссылка кликабельная и там WASM.
Если вы перешли по ссылке, вы наверно заметили, что браузер спросил разрешение на доступ к видеокамере. Возникает вопрос: что будет, если пользователь нажал нет? Разумно было бы ничего не грузить в таком случае и фоллбэчить на управление мышью\гироскопом. Я не нашел ESM версию tfjs-core и tfjs-converter, так что вместо динамического импорта, я остановился на довольно криповой конструкции с бибилиотекой fetchInject, где порядок загрузки модулей имеет значение.
fetchInject([ 'https://cdn.jsdelivr.net/npm/@tensorflow-models/blazeface@0.0.5/dist/blazeface.min.js'], fetchInject([ 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter@2.0.1/dist/tf-converter.min.js', 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@2.0.1/dist/tfjs-backend-wasm.wasm'], fetchInject([ 'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core@2.0.1/dist/tf-core.min.js']))).then(() => { tf.wasm.setWasmPath('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-wasm@2.0.1/dist/tf-backend-wasm.min.js'); //some other code}
Для того что бы найти положение взгляда, я беру среднее между двумя глазами. И легко понять что растояние до головы, будет пропорционально растоянию между глазами на видео, в силу подобия сторон треугольника. Данные о положении глаз, которые приходят из модели довольно зашумленные, поэтому прежде чем производить рачеты, я их сгладил с помощью скользящей средней EMA (Exponential Moving Average)
pos = (1 - smooth) * pos + smooth * nextPos;
Или записав по другому
pos *= 1 - smooth;pos += nextPos * smooth;
Таким образом мы получаем координаты x, y, z, в некой сферической системе координат с центром в камере. Причем x и y ограничены углом обзора камеры, а z это примерное растояние от головы до нее. На малых углах поворота , так что x и y можно считать за углы.
pushUpdate({ x: (eyes[0] + eyes[2]) / video.width - 1, y: 1 - (eyes[1] + eyes[3]) / video.width, z: defautDist / dist});
Фотограмметрия
Довольно забавный дата сет с публикации SIGGRAPH 2020 Immersive light field video with a layered mesh representation. Они делали картинки специально для того что бы можно было двигать камеру в некотором диапазоне, что идеально ложится под идею параллакса. Пример с параллаксом.
3д сцена тут создается послойно, и на каждый слой наложена текстура. Не менее забавно выглядит дейвайс, с помощью которого они создавали фотограмметрию. Они купили 47 дешевых экш камер Yi 4K за 10к рублей каждая, и разместили их на полусферу в форме икосаэдра в котором треугольная сетка утроена. После этого все разместили на штатив и камеры синхронизировали для сьемки.
Ссылки:
- My github: github.com/munrocket/parallax-effect
- Immersive light field video: augmentedperception.github.io/deepviewvideo
- WebGL community in telegram: t.me/webgl_ru