D point что это
Перейти к содержимому

D point что это

Шесть степеней свободы: 3D object detection и не только

В компьютерном зрении часто приходится работать с двумерными изображениями, и значительно реже — с 3D объектами. Из-за этого многие ML инженеры чувствуют себя неуверенно в этой области: много незнакомых слов, непонятно, куда тут применить старых друзей Resnet и Unet. Поэтому сегодня я хотел бы немного поговорить о 3D на примере задачи определения шести степеней свободы, что в каком-то виде синонимично 3D object detection. Я разберу одну из относительно свежих работ на эту тему с некоторыми отступлениями.

Кратко о задаче

Для начала давайте определимся, что такое шесть степеней свободы (6 DoF — degrees of freedom). Представим себе некоторый ригидный (неизменяемый, т.е. при трансформации все точки будут оставаться на той же дистанции друг от друга) объект в трехмерном мире. Чтобы описать его положение относительно наблюдателя понадобится 6 измерений: три будут отвечать за повороты по разным осям, а еще три — за смещение по соответствующим осям. Соответственно, имея эти шесть чисел, мы представляем, как объект расположен относительно какого-то базиса (например, точки, с которой ведется фотосъемка). Эта задача является классической для робототехники (где находится объект, который нужно схватить роборукой?), дополненной реальности (где нарисовать маску в MSQRD, ушки в Snapchat или кроссовки в Wanna Kicks?) , беспилотных автомобилей и других доменов.

Будем рассматривать статью MobilePose: Real-Time Pose Estimation for Unseen Objects with Weak Shape Supervision (Hou et al., 2020). Эта статья, написанная авторами из Google Research, предлагает надежный и, что немаловажно, быстрый пайплайн для решения задачи, будет уместно разобрать его по частям.

Пайплайн состоит из трех основных кусков:

Backbone достаточно классический, архитектура в виде песочных часов должна быть знакома каждому, кто хоть раз обучал Unet.

Выходы сети не выглядят инновационно. Detection, regression — все знакомые слова! Впрочем, насчет shape могут возникнуть вопросы. Но давайте отложим их на время.

Постпроцессинг может показаться загадочным для тех, кто не в теме. Что такое EPnP и почему оно превращает 2D точки в 3D bounding box?

3D для чайников

И здесь сразу нужно сделать важное отступление, которое поможет нам ответить на все эти вопросы. Давайте высокоуровнево посмотрим на некоторую математику 3D мира. Пусть есть некоторый набор 3D-точек X — матрица размером (n, 3), в которой n — количество точек. Как мы помним, шесть степеней свободы — это три поворота и три смещения, т.е. Rigid transformation. Если обозначить R матрицу поворота, а t — вектор переноса (rotation и translation соответственно), будет соблюдаться такое уравнение:

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

Но X’ — это все еще 3D-координаты. Потому стоит сказать, что еще существует некоторая проективная матрица P . Эта матрица характеризует то, как мы проецируем объект на двумерную плоскость, условно “рендерим” его. Эта матрица зависит от размера фотографии, фокусного расстояния, искажений, но в нашей задаче ее можно считать константной. Имя такую матрицу, можно получить 2D координаты точек, просто умножив ее на X’ :

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

Подзадача нахождения R и t , зная X , x и P , называется Perspective-n-Point. Т.е. мы знаем, как выглядит наш объект в 3D (это X ), знаем, на какое изображение он спроецирован ( P ) и где на этом изображении находятся его точки. Выглядит как задача оптимизации! Есть целое семейство алгоритмов, которые решают эту задачу, например, некоторые уже реализованы в OpenCV.

Кстати, к этой проблеме подходят и с другой стороны. Адепты deep learning могут найти множество статей, где используется специальный projection layer, который преобразует 2D и 3D точки друг в друга. Обычно, чтобы обучить такой слой, используют синтетические данные, т.к. 3D координаты из реального мира получать дорого и сложно. Пример такой статьи.

Где взять 3D точки?

Итак, нам нужен X — напомню, это набор 3D точек. Откуда его взять?

Самый простой вариант — пытаться найти один и тот же объект на всех изображениях. Берем какую-то 3D CAD модель (готовую, рисуем с нуля, сканируем настоящий объект специальным сканером…) и используем его (точнее, какие-то его точки) в качестве X. Иными словами, делаем явное допущение «на фотографии находится именно такой объект» — на первый взгляд, это прямо-таки нагло, но практика показывает, что для оценки 6 DoF этого достаточно.

Более сложный подход — так называемые параметризированные модели. Образцовый пример — Basel Face. Исследователи из Университета Базеля отсканировали много лиц и при помощи PCA обучили такую модель, чтобы изменение малого числа ее параметров позволяло сгенерировать эти 3D лица. Таким образом, можно крутить малое количество ручек — главных компонент и получать довольно разные модели.

Параметризованная модель может быть и куда проще. Например, если мы ищем на фотографии 3D bounding box, в качестве базовой модели можно использовать куб, а для параметризации использовать его соотношения длины-ширины-высоты.

Если наша 3D модель параметризована, ее параметры можно подбирать разными итеративными методами и выбирать такую, на которой reprojection error будет меньше. Т.е. берем некоторую модель X , решаем PnP, получаем R и t и выбираем такой X , чтобы разность x и (X @ R.T + t) @ P была минимальна, для примера можно посмотреть на procrustes analysis.

Истинные диплернеры идут дальше и в каком-то виде выучивают или 3D модель, или ее параметры. Хороший пример — известная работа DensePose от Facebook Research, которая популяризовала подход с выучиванием dense карты координат. Т.е. модель предсказывает для каждого пикселя его относительное расположение на 3D модели. Дальше можно найти соответствия и получить для каждого пикселя некоторое приближение его 3D координаты.

В статье, которую мы изначально взялись разбирать, есть выход с таинственным названием shape. В зависимости от наличия grouth truth данных (об этом немного позже) авторы либо учат там сегментационную маску объекта (т.н. weak supervision, для улучшения сходимости), либо как раз карту координат объекта.

Также может возникнуть вопрос — координаты каких именно точек мы хотим найти? Ответ простой: на самом деле, нам все равно. 3D модель обычно состоит из тысяч вершин, мы можем выбрать подмножество по своему вкусу. Единственный более или менее важный критерий — приблизительная равноудаленность точек друг от друга; в случае неудачной выборки решение PnP становится нестабильным.

Где взять 2D точки?

Итак, с 3D объектом худо-бедно разобрались, давайте пойдем в более знакомую большинству CV инженеров область, т.е. вернемся в 2D и подумаем, где взять координаты на плоскости.

Для получения 2D координат точек (обычно эта задача называется keypoint detection) популярны два основных подхода: регрессия напрямую (последний слой сети выдает x, y для каждой точки) и heatmap-регрессия (последний слой выдает тепловую карту с пятном около точки). Первый подход может быть быстрее, т.к. необязательно выстраивать полную encoder-decoder архитектуру, второй обычно точнее и достаточно легко обучается (это почти та же сегментация).

изображение взято из Adaptive Wing Loss for Robust Face Alignment via Heatmap Regression

Авторы MobilePose не пошли ни по одному из этих путей и придумали интересный гибрид, вдохновившись современными “безанкорными” архитектурами для детекции вроде CenterNet. Помните, на схеме есть головы Detection и Regression? Так вот, Detection голова предсказывает центр объекта, а Regression — где вершины объекта находятся относительно этого центра.

В этом подходе есть изящный трюк. Я много писал о том, как выбрать 3D модель, а в этом случае все осознанно упрощается: в качестве модели используется простой параллелепипед, он же 3D bounding box! То есть X — это вершины бокса, а x — проекция этих вершин. Значит, достаточно знать соотношения сторон этого бокса (которые мы получаем из самой архитектуры детектора), и остальная магия ни к чему.

Особенности приготовления данных

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

Unlike 2D object detection, it is prohibitive to manually label data for 3D detection. Due to this difficulty of collecting sufficiently large amounts of labeled training data, such approaches are typically trained on real data that are highly correlated with the test data (e.g., same camera, same object instances, similar lighting conditions). As a result, one challenge of existing approaches is generalizing to test data that are significantly different from the training set.

Synthetic data is a promising alternative for training such deep neural networks, capable of generating an almost unlimited amount of pre-labeled training data with little effort. Synthetic data comes with its own problems, however. Chief among these is the reality gap, that is, the fact that networks trained on synthetic data usually do not perform well on real data.

В рассматриваемой статье авторы сделали один из таких трюков: вместо того, чтобы бездумно рендерить весь мир, они сделали комбинацию реальных и синтетических данных. Для этого они взяли видео из AR-приложений (хорошо быть гуглом и иметь много данных из ARCore). В таких видео есть и знание о плоскостях, оценка 6 DoF, полученная при помощи визуальной одометрии, и оценка освещения. Это позволяет рендерить искусственные объекты не где попало, а только на плоских поверхностях, адаптируя освещенность, что значительно снижает reality gap между синтетическими и реальными данными. К сожалению, повторить этот трюк в домашних условиях кажется довольно сложным.

Все вместе

(Сделано из палок и скотча за полчаса)

Ура, мы галопом пробежались по всем ключевым концепциям пайплайна! Этого должно хватить, чтобы читатель смог собрать из open source компонентов, например, приложение, которое будет рисовать маску на лице (для этого можно даже не учить модели самостоятельно, готовых сетей для face alignment немало).

Конечно, это будет только прототип. При доведении такого приложения до продакшена возникнет еще много вопросов, например:

12.15 – Дружественные функции и классы

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

В подобных ситуациях есть два варианта:

  1. Использовать в коде отображения открытые функции класса хранилища. Однако у этого есть несколько потенциальных недостатков. Во-первых, эти открытые функции-члены должны быть определены, что требует времени и может загромождать интерфейс класса хранилища. Во-вторых, классу хранилища, возможно, придется предоставить для кода отображения функции, которые он не хочет делать доступными для кого-либо еще. Но невозможно сказать «эта функция предназначена для использования только классом отображения».
  2. В качестве альтернативы, используя дружественные классы и дружественные функции, вы можете предоставить своему коду отображения доступ к закрытым деталям класса хранилища. Это позволяет коду отображения напрямую обращаться ко всем закрытым членам и функциям класса хранилища, не давая при этом доступ кому-либо еще! В этом уроке мы подробнее рассмотрим, как это делается.

Дружественные функции

Дружественная функция – это функция, которая может получить доступ к закрытым членам класса, как если бы она была членом этого класса. Во всем остальном дружественная функция похожа на обычную функцию. Дружественная функция может быть либо обычной функцией, либо функцией-членом другого класса. Чтобы объявить дружественную функцию, просто используйте ключевое слово friend перед прототипом функции, которую вы хотите сделать другом класса. Не имеет значения, объявляете ли вы дружественную функцию в закрытом или открытом разделе класса.

Вот пример использования дружественной функции:

В этом примере мы объявили функцию с именем reset() , которая принимает объект класса Accumulator и устанавливает значение m_value равным 0. Поскольку reset() не является членом класса Accumulator , обычно reset() не будет иметь доступ к закрытым членам Accumulator . Однако, поскольку Accumulator специально объявил эту функцию reset() как друга класса, то ей предоставляется доступ к закрытым членам Accumulator .

Обратите внимание, что мы должны передать в reset() объект Accumulator . Это потому, что reset() не является функцией-членом. У нее нет указателя *this и нет объекта Accumulator для работы, если он не указан.

Вот еще один пример:

В этом примере мы объявляем функцию isEqual() другом класса Value . isEqual() принимает в качестве параметров два объекта Value . Поскольку isEqual() является другом класса Value , она может получить доступ к закрытым членам всех объектов Value . В этом случае она использует этот доступ для сравнения двух объектов и возвращает true , если они равны.

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

Несколько друзей

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

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

Это прототип класса, который сообщает компилятору, что в будущем мы собираемся определить класс под названием Humidity . Без этой строки компилятор при синтаксическом анализе прототипа для printWeather() внутри класса Temperature сообщил бы нам, что не знает, что такое Humidity . Прототипы классов выполняют ту же роль, что и прототипы функций – они сообщают компилятору, как что-то выглядит, чтобы его можно было использовать сейчас и определить позже. Однако, в отличие от функций, классы не имеют возвращаемых типов или параметров, поэтому прототипы классов всегда представляют собой просто class ClassName , где ClassName – это имя класса.

Дружественные классы

Также целый класс можно сделать другом другого класса. Это дает всем членам дружественного класса доступ к закрытым членам другого класса. Вот пример:

Поскольку класс Display является другом Storage , любой из членов Display , использующих объект класса Storage , может напрямую обращаться к закрытым членам Storage . Эта программа дает следующий результат:

Несколько дополнительных замечаний о дружественных классах. Во-первых, даже несмотря на то, что Display является другом Storage , Display не имеет прямого доступа к указателю *this объектов Storage . Во-вторых, то, что Display является другом Storage , не означает, что Storage также является другом Display . Если вы хотите, чтобы два класса дружили друг с другом, они оба должны объявить друг друга друзьями. Наконец, если класс A является другом B , а B – другом C , это не означает, что A является другом C .

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

Дружественные функции-члены

Вместо того чтобы делать другом весь класс, вы можете сделать другом только одну функцию-член. Это делается аналогично тому, как сделать дружественной обычную функцию, за исключением использования имени функции-члена с включенным префиксом ИмяКласса:: (например, Display::displayItem ).

Однако на самом деле это может быть немного сложнее, чем ожидалось. Давайте, преобразуем предыдущий пример, чтобы сделать Display::displayItem дружественной функцией-членом. Вы можете попробовать сделать что-то вроде этого:

Однако оказывается, что это не сработает. Чтобы сделать функцию-член другом, компилятор должен увидеть полное определение класса функции-члена (а не только предварительное объявление). Поскольку класс Storage еще не видел полного определения класса Display , компилятор выдаст ошибку в тот момент, когда мы попытаемся сделать функцию-член другом.

К счастью, это легко решить, просто поставив определение класса Display перед определением класса Storage .

Однако теперь у нас есть другая проблема. Поскольку функция-член Display::displayItem() использует Storage в качестве ссылочного параметра, а мы только что переместили определение Storage ниже определения Display , компилятор будет жаловаться, что не знает, что такое Storage . Мы не можем исправить это, изменив порядок определения, потому что тогда мы отменим предыдущее исправление.

К счастью, это тоже можно исправить, выполнив пару простых шагов. Во-первых, мы можем добавить класс Storage в качестве предварительного объявления. Во-вторых, мы можем переместить определение Display::displayItem() из класса на место после полного определения класса Storage .

Вот как это выглядит:

Теперь всё будет скомпилировано правильно: предварительного объявления класса Storage достаточно, чтобы удовлетворить объявление Display::displayItem() , полное определение Display удовлетворяет объявлению Display::displayItem() как друга Storage , а полное определение класса Storage достаточно, чтобы удовлетворить определение функции-члена Display::displayItem() . Если это немного сбивает с толку, смотрите комментарии в программе.

Если это похоже на боль – это так. К счастью, этот танец с бубном необходим только потому, что мы пытаемся сделать всё в одном файле. Лучшее решение – поместить определение каждого класса в отдельный заголовочный файл, а определения функций-членов в соответствующие файлы .cpp . Таким образом, все определения классов сразу же были бы видны в файлах .cpp , и не нужно было бы переупорядочивать классы и функции!

Резюме

Дружественная функция или класс – это функция или класс, которые могут получить доступ к закрытым членам другого класса, как если бы они были членами этого класса. Это позволяет дружественной функции или классу тесно работать с другим классом, не заставляя другой класс открывать свои закрытые члены (например, через функции доступа).

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

Обратите внимание, что для того, чтобы сделать конкретную функцию-член другом, необходимо сначала увидеть полное определение класса функции-члена.

Небольшой тест

Вопрос 1

В геометрии точка – это позиция в пространстве. Мы можем определить точку в трехмерном пространстве как набор координат x, y и z. Например, Point(2.0, 1.0, 0.0) будет точкой в ​​пространстве с координатами x = 2.0, y = 1.0 и z = 0.0.

В физике вектор – это величина, которая имеет величину (длину) и направление (но не положение). Мы можем определить вектор в трехмерном пространстве как значения x, y и z, представляющие направление вектора вдоль осей x, y и z (длина может быть получена из них). Например, Vector(2.0, 0.0, 0.0) будет вектором, представляющим направление вдоль (только) положительной оси x, с длиной 2.0.

Вектор можно применить к точке, чтобы переместить точку в новое положение. Это делается путем добавления направления вектора к положению точки, чтобы получить новое положение. Например, Point(2.0, 1.0, 0.0) + Vector(2.0, 0.0, 0.0) даст точку (4.0, 1.0, 0.0).

Точки и векторы часто используются в компьютерной графике (точки представляют вершины фигуры, а векторы представляют движение фигуры).

Учитывая следующую программу:

1a) Сделайте Point3d дружественным классом класса Vector3d и реализуйте функцию Point3d::moveByVector() .

1b) Вместо того, чтобы делать класс Point3d другом класса Vector3d , сделайте функцию-член Point3d::moveByVector другом класса Vector3d .

Что такое поинты и как их можно заработать

Что такое поинты? Они представляют собой игровую валюту: один point приравнивается к 1 рублю российского происхождения. Данной валютой можно совершать покупки какой-либо услуги. Также разрешен ее вывод, а именно больше пятидесяти поинтов за один раз.

Бывают два типа поинтов:

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

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

что такое поинты

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

Как заработать point на свой кошелек?

Если говорить об игровой валюте (поинтах), то можно твердо сказать, что она является гибкой. Это значит, что ее запросто можно вывести на такие кошельки, как «Яндекс.Деньги» и «Вебмани». Что такое point, можно узнать, играя в различные онлайн-казино. А как играть, мы расскажем далее в статье.

Так, например, имеется вероятность получить их, играя на Лиге. Можно выигрывать различные турниры, призом которых и является данная валюта. После получения ее можно вывести либо играть дальше. Между прочим, есть возможность даже неплохо зарабатывать, если быть отличным игроком «Профессиональной Лиги». Но существуют и много других источников по заработку поинтов, на которых тоже есть вероятность неплохо пополнить свой электронный кошелек.

об игровой валюте поинтах

Как играть на эту валюту?

Продолжаем рассказ о том, что такое поинты и как на них играть. В качестве примера возьмем сервис под названием «MyMix». Здесь всего лишь необходимо присоединиться к миксу со ставками. Кстати, тут можно сделать микс и на свой вкус, отказавшись от тех, которые не по душе.

А разобраться, как играть на поинты на «Миксе», не сложно. Для этого нужно:

  1. Найти микс без каких-либо игроков.
  2. Нажать «Настройка микса».
  3. И в разделе «Игра на деньги» выбрать нужный размер ставки.
  4. Далее сохранить изменение.

На что тратить поинты

Если подвести итог из вышесказанного по поводу использования поинтов и вообще понятия, что такое поинты, то можно сказать, что тратятся они на то, что захочет обладатель этой электронной купюры. Ею могут оплачиваться различные услуги и самое главное — их можно вывести на собственный электронный кошелек в «Яндекс.Деньги» или же WebMoney.

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

Существует мудрое высказывание: «Деньги любят счёт». Это значит, их нужно рационально использовать, вести постоянный контроль и направлять их в нужное русло, а не выкидывать на ветер.

что такое поинты

Проблема азартных игр

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

что такое point

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

Метод получения бесплатных поинтов

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

Обычно его размер не меньше 1000 поинтов, но многие интернет-ресурсы выплачивают гораздо больше. Кроме того, существуют временные рамки, которые определяют начало/конец заработка и вывода игровой валюты. Так, после начисления поинтов на свой кошелек объявляется перерыв на 1 час, в течение которого нельзя заработать и вывести их в рамках одного сайта.

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *