[Перевод] Intel® RealSense™. Работа с потоками необработанных данных
Разработчикам, которые интересуются возможностями, доступными при внедрении управления без помощи контроллеров в своих приложениях, достаточно ознакомиться с Intel RealSense SDK, сопутствующими примерами и ресурсами в Интернете. Если вы «погрузитесь» в это решение, то обнаружите широкий набор функций, позволяющих создавать совершенно новые, великолепные интерфейсы с использованием новых технологий.В этой статье мы поговорим о потоках различных необработанных данных, о доступе к ним и о способах их использования. За счет прямого доступа к необработанным данным мы не только сможем работать с метаданными, но и получим самый быстрый способ определять, что делает пользователь в реальном мире.В этой статье в качестве камеры Intel RealSense мы использовали трехмерную камеру Bell Cliff, которая выдает несколько потоков данных — от традиционного цветного изображения RGB до данных о глубине и картинки с инфракрасной камеры. Каждый из этих потоков ведет себя по-своему, но об этом мы подробнее поговорим ниже. Ознакомившись с этой статьей, вы узнаете, какие потоки доступны и когда работать с ними.Для понимания представленных материалов полезно (но необязательно) знать C++ для ознакомления с примерами кода и иметь общее представление о технологии Intel RealSense (или о ее более ранней версии — Intel® Perceptual Computing SDK).Почему это важноЕсли вас интересует только реализация простой системы распознавания жестов или лица, то в модулях алгоритмов Intel RealSense SDK вы найдете все необходимое, а о потоках необработанных данных можно не заботиться. Проблема возникнет, когда вам потребуется функциональность, отсутствующая в модулях алгоритмов в составе SDK. Приложение не будет работать без альтернативного решения.Итак, первый вопрос: «Что нужно вашему приложению и можно ли выполнить эти требования с помощью модулей алгоритмов Intel RealSense SDK?». Если нужен указатель на экране, отслеживающий перемещение руки, для этого может оказаться достаточно модуля отслеживания руки или пальцев. Чтобы быстро определить, соответствует ли доступная функциональность вашим нуждам, можно использовать примеры в составе SDK. Если таких возможностей недостаточно, то можно приступить к планированию использования потоков необработанных данных.Например, в настоящее время поддерживается обнаружение двухмерных жестов. Но что, если вам нужно обнаруживать жесты по набору трехмерных рук и определять дополнительную информацию по движению рук пользователя? Что, если нужно записать высокоскоростной поток жестов и сохранить их в виде последовательности, а не в виде снимка? Потребуется обойти систему распознавания пальцев и рук, образующую вычислительную нагрузку, и внедрить методику для динамического кодирования телеметрии в реальном времени. Вообще можно столкнуться с недостаточной функциональностью, может потребоваться более прямое решение для той или иной программной проблемы.
Еще пример: предположим, что вы создаете приложение, которое обнаруживает и распознает язык жестов и преобразует его в текст для передачи в телеконференции. Текущая функциональность Intel RealSense SDK поддерживает отслеживание рук и пальцев (но только в одиночных жестах) и не имеет целенаправленной поддержки распознавания языка жестов. Единственное решение в таких случаях — разработка собственной системы распознавания жестов, которая сможет быстро преобразовывать жесты в последовательность положений пальцев и рук, а с помощью системы шаблонов будет распознавать знаки и восстанавливать текст. Единственный доступный в настоящее время способ достижения этого результата — доступ к потоку необработанных данных с помощью высокоскоростной записи и преобразование значения на лету.
Возможность написания кода для восполнения пробела между существующей и нужной функциональностью крайне важна, и она обеспечивается в Intel RealSense SDK.Эта технология пока относительно нова, и разработчики еще только изучают ее возможности. Доступ к потокам необработанных данных расширяет возможные действия, а из таких усовершенствований рождаются новые решения.
Потоки Лучший способ узнать о потоках данных — самостоятельно ознакомиться с ними. Для этого нужно запустить пример Raw Streams, находящийся в папке bin установленного экземпляра Intel Realsense SDK.\Intel\RSSDK\bin\win32\raw_streams.exe
Пример снабжен полным исходным кодом и проектом, который нам очень пригодится. Если запустить исполняемый файл и нажать копку START при запуске приложения, вы получите цветной поток RGB, как показано на рис. 1.
Рисунок 1. Типичный цветной поток RGB
Помахав самому себе ручкой, нажмите кнопку STOP, откройте меню Depth и выберите 640×480x60. Снова нажмите кнопку START.
Рисунок 2. Отфильтрованный поток данных глубины с камеры Intel® RealSense™ 3D.
Как видно на рис. 2, это изображение значительно отличается от цветного потока RGB. Вы видите черно-белое изображение, представляющее расстояние каждого пикселя до камеры. Светлые пиксели ближе, а темные — дальше; черные либо считаются фоном, либо не распознаны достоверно.Двигаясь перед камерой, вы поймете, что камера может очень быстро принимать решения о действиях пользователя. Например, совершенно ясно, как выделить на сцене руки благодаря толстому черному контуру, отделяющему их от тела и головы, находящихся дальше от камеры.
Рисунок 3. Ночное видение. Камера Intel® RealSense™ 3D выдает поток необработанного видеоизображения, снятого в инфракрасном спектре
Последний тип потока может быть неизвестен прежним разработчикам Intel Perceptual Computing SDK, но на рис. 3 видно, что в меню IR можно получить с камеры изображение, снятое в инфракрасном диапазоне. Это поток необработанных данных, его скорость чтения намного превышает частоту обновления типовых мониторов.
Можно инициализировать все или любые из этих потоков для их одновременного чтения по мере потребностей приложения; для каждого потока можно выбрать требуемое разрешение и частоту обновления. Важно отметить, что итоговая кадровая скорость входящих потоков будет зависеть от доступной пропускной способности. Например, если попытаться инициализировать поток RGB при 60 кадрах в секунду, поток глубины при 120 кадрах в секунду и ИК-поток при 120 кадрах в секунду и передавать все эти потоки с единой синхронизацией, будет доступна лишь наименьшая скорость обновления (60 кадров в секунду), и только если с такой работой справится система.
Образец с необработанными потоками пригоден для начала работы, но не позволяет сочетать потоки, поэтому его следует использовать только для ознакомления с типами, разрешениями и скоростями обновления, доступными для вашей камеры. Помните, что пакет Intel RealSense SDK предназначен для работы с различными типами трехмерных камер, поэтому разрешение образца может быть недоступным на других камерах. Поэтому не следует жестко задавать разрешение в коде приложений.
Создание потоков и доступ к данным Просмотреть полный исходный код примера с необработанными потоками можно, открыв следующий проект в Visual Studio*.\Intel\RSSDK\sample\raw_streams\raw_streams_vs20XX.sln
В примере содержится простой пользовательский интерфейс и полный набор параметров, поэтому код не очень легко читается. Имеет смысл убирать добавочный код и оставлять только необходимые строки кода, служащие для того, чтобы создавать, обрабатывать и удалять поток, полученный с камеры. Ниже приведен код, представляющий собой «очищенную» версию приведенного выше проекта, но в этом коде сохранены все необходимые компоненты даже для простейших приложений Intel RealSense.
Первые две важные функции — это инициализация камеры Intel RealSense 3D и ее высвобождение по завершении работы программы. Это видно в приведенном ниже коде, а подробные сведения о вызываемых функциях будут приведены ниже.
int RSInit (void) { InitCommonControls (); g_session=PXCSession: CreateInstance (); if (! g_session) return 1; g_bConnected = false; g_RSThread = CreateThread (0,0, ThreadProc, g_pGlob→hWnd,0,0); Sleep (6000); if (g_bConnected==false) return 1; else return 0; } void RSClose (void) { g_bConnected = false; WaitForSingleObject (g_RSThread, INFINITE); } Здесь мы имеем функции верхнего уровня для любого приложения, предназначенного для необработанных данных: создание экземпляра сеанса и потока для выполнения кода, обрабатывающего поток, затем высвобождение потока с помощью глобального флага g_bConnected. Рекомендуется использовать потоки ЦП при работе с потоками данных, поскольку это даст возможность основному приложению работать с любой требуемой кадровой скоростью, независимо от кадровой скорости камеры. Кроме того, это помогает распределить нагрузку на ЦП среди нескольких ядер, благодаря чему повышается общая производительность приложения.В приведенном выше коде нас интересует только строка с функцией ThreadProc, которая содержит весь код, отвечающий за управление потоками. Перед переходом к подробностям отметим, что этот исходный код не является исчерпывающим, здесь для улучшения читаемости удалены глобальные объявления и второстепенные разделы. Сведения о глобальных объявлениях см. в первоначальном исходном коде образца проекта raw_streams.
static DWORD WINAPI ThreadProc (LPVOID arg) { CRITICAL_SECTION g_display_cs; InitializeCriticalSection (&g depthdataCS); HWND hwndDlg=(HWND)arg; ~ PopulateDevices (hwndDlg); PXCCapture: DeviceInfo dinfo=GetCheckedDevice (hwndDlg); PXCCapture: Device: StreamProfileSet profiles=GetProfileSet (hwndDlg); StreamSamples ((HWND)arg, &dinfo, &profiles, false, false, false, g_file ); ReleaseDeviceAndCaptureManager (); g_session→Release (); DeleteCriticalSection (&g_depthdataCS); return 0; } Для работы с потоком данных важно создать «главный раздел» в коде. Если не сделать это в многопоточной среде, то два потока теоретически смогут записывать данные в одну и ту же глобальную переменную одновременно, что нежелательно.Для тех, кто не знаком с многопоточностью, эта функция вызывается и не завершается, пока для главного потока (создавшего этот поток) для параметра g_bConnected не будет установлено значение false. Главный вызов функции здесь — StreamSamples, а остальной код выше и ниже служит лишь для входа и выхода. Первая интересующая нас функция — PopulateDevices, она практически идентична такой же функции в проекте raw_streams. Она заполняет список g_devices именами всех доступных устройств. Если вы используете камеру Intel RealSense 3D на ультрабуке, то есть вероятность, что у вас будет два устройства (второе — встроенная камера ультрабука). Обратите внимание на следующие строки.
static const int ID_DEVICEX=21000;
static const int NDEVICES_MAX=100;
int с = ID_DEVICEX;
g session→CreateImpl
g_device→QueryDeviceInfo (&dinfo);
Функция StreamProfileSet отвечает за сбор всех типов потоков и разрешений, которые нужно инициализировать, она может быть простой или сложной, исходя из потребностей. В целях совместимости с камерами настоятельно рекомендуется перечислить доступные разрешения и типы в списке вместо жесткого кодирования фиксированных настроек.
PXCCapture: Device: StreamProfileSet GetProfileSet (HWND hwndDlg)
{
PXCCapture: Device: StreamProfileSet profiles={};
if (! g_device) return profiles;
PXCCapture: DeviceInfо dinfo;
g_device→QueryDeviceInfo (&dinfo);
for (int s=0, mi=IDXM_DEVICE+l; s
Что делать с данными потока Итак, теперь вы знаете, как получить поток данных с камеры Intel RealSense 3D, и, наверное, интересуетесь, что делать с этими данными. Разумеется, можно просто вывести эти данные на экран и полюбоваться изображением, но вскоре потребуется преобразовать эти данные в полезную информацию и обработать ее в вашем приложении.Как не существует двух одинаковых снежинок, так и все реализации необработанных потоков данных будут различаться, но все же существует несколько общих подходов, помогающих наладить анализ данных. Чтобы сократить объем нового кода, мы будем использовать приведенный выше код в качестве шаблона для примеров, предлагаемых ниже.
Найти ближайшую точку Рекомендуется найти ближайшую к камере точку объекта, находящегося перед ней. При этом вы только что передали данные глубины из потока данных в глобальный массив в потоке ЦП. Можно создать вложенный цикл для проверки каждого значения в массиве. short bestvalue = 0; int bestx = 0; int besty = 0; for (int у = 0; у < (int)dinfo.height; y++) { for ( int x = 0; x < (int)dinfo.width; x++) { short thisvalue = g_depthdata[x][y]; if ( thisvalue > bestvalue) { bestvalue = thisvalue; bestx = x; besty = y; } } } Каждый раз при обнаружении более близкого значения оно заменяет текущее наилучшее значение, при этом записываются координаты по осям X и Y. К тому моменту, когда цикл обойдет каждый пиксель в данных глубины, в итоговых переменных BESTX и BESTY будут храниться координаты данных глубины для ближайшей к камере точки.Игнорировать объекты на заднем плане Может потребоваться идентифицировать формы объектов переднего плана, но приложение не должно путать их с объектами на заднем плане, например с сидящим пользователем или с проходящими мимо людьми. short newshape[dinfo.height][dinfo.width]; memcpy (newshape,0, sizeof (newshape)); for (int у = 0; у < (int)dinfo.height; y++) { for ( int x = 0; x < (int)dinfo.width; x++) { short thisvalue = g_depthdata[x][y]; if ( thisvalue>32000 && thisvalue<48000 ) { newshape[x][y] = thisvalue; } } } Если добавить условие при чтении каждого пикселя и передавать только те пиксели, которые находятся в пределах определенного диапазона, то из данных глубины можно извлекать объекты и передавать их во второй массив для дальнейшей обработки.Подсказки и советы Что следует делатьЕсли вы работаете с примерами впервые и используете ультрабук со встроенной камерой, то приложение может использовать эту встроенную камеру вместо камеры Intel RealSense. Убедитесь, что камера Intel RealSense правильно подключена и что ваше приложение использует устройство Intel® RealSense™ 3D camera. Для получения дополнительных сведений о том, как найти список устройств, см. ссылки на g_devices в этой статье. Всегда старайтесь использовать многопоточные вычисления в приложении Intel RealSense: в этом случае приложение не будет «привязано» к кадровой скорости потока камеры Intel RealSense 3D, а на многоядерных системах будет достигнута более высокая производительность. Чего не следует делатьНе задавайте жестко в коде параметры устройства или профиля при инициализации потоков, поскольку будущие камеры Intel RealSense 3D могут не поддерживать заданные вами параметры. Всегда следует перечислять доступные устройства и профили и использовать условия поиска, чтобы найти нужное. Избегайте ненужной передачи данных во вторичные массивы, поскольку при каждом таком цикле расходуется немало ресурсов ЦП и памяти. Старайтесь, чтобы анализ данных был как можно ближе к первоначальной операции чтения данных. Заключение Знание того, как получить поток необработанных данных с камеры Intel RealSense 3D, поможет расширить возможности этой технологии и создавать современные решения. Мы уже видели великолепные приложения с управлением без помощи рук, созданные первыми разработчиками в этой области, но это лишь малая часть всех возможностей новых технологий.Многие пользователи по-прежнему относятся к компьютерам как к устройствам, на которые следует активно воздействовать, чтобы они работали, но теперь компьютеры получили «зрение» и могут наблюдать за всеми нашими движениями. Не подсматривать, прошу заметить, а просто наблюдать, чтобы в нужный момент прийти на помощь. Согласно поговорке в стране слепых одноглазый станет королем. Разве неверно, что мы живем в мире, населенном «слепыми» компьютерами? Представьте, какая произойдет революция, если один из них в не столь отдаленном будущем «прозреет»? Будучи разработчиками, мы являемся архитекторами этой революции, вместе мы можем создать совершенно новую парадигму, в которой компьютеры видят своих операторов и стараются им помогать.Подробнее о RealSense на сайте IntelВсе о RealSense для разработчиковЗагрузка RealSense SDKФорум разработчиков RealSense