Разработчикам, которые интересуются возможностями, доступными при внедрении управления без помощи контроллеров в своих приложениях, достаточно ознакомиться с 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.
IntelRSSDKbinwin32raw_streams.exe
Пример снабжен полным исходным кодом и проектом, который нам очень пригодится. Если запустить исполняемый файл и нажать копку START при запуске приложения, вы получите цветной поток RGB, как показано на рис. 1.
Рисунок 1. Типичный цветной поток RGB
Помахав самому себе ручкой, нажмите кнопку STOP, откройте меню Depth и выберите 640x480x60. Снова нажмите кнопку 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*.
IntelRSSDKsampleraw_streamsraw_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<PXCCapture>(g_devices[c], &g_ capture); g_device=g_capture->CreateDevice((c-ID_DEVICEX)%NDEVICES_MAX);
Код, константы и глобальные функции скопированы из первоначального кода, их можно еще более сократить. Важнейшие вызовы здесь — Createlmpl и CreateDevice. В результате указатель камеры Intel RealSense 3D теперь хранится в g_device.
При наличии действующего указателя на устройство остальной код инициализации работает без проблем. Функция StreamProfileSet является оболочкой для этого кода.
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<PXCCapture::STREAM_LIMIT;s++)
{
PXCCapture::StreamType st=PXCCapture::StreamTypeFromIndex(s);
if (!(dinfo.streams&st)) continue;
int id=ID_STREAMlX+s*NPROFILES_MAX;
int nprofiles=g_device->QueryStreamProfileSetllum(st);
for (int p=0;p<nprofiles;p++)
{
if ( st==PXCCapture::StreamType::STREAM_TYPE_COLOR) continue;
if ( st==PXCCapture::StreamType::STREAM_TYPE_IR) continue;
if ( st==PXCCapture::StreamType::STREAM_TYPE_DEPTH && p==2)
{
PXCCapture::Device::StreamProfileSet profilesl={};
g_device->QueryStreamProfileSet(stj p, Sprofilesl);
profiles[st]=profilesl[st];
}
}
mi++;
}
return profiles;
}
QueryStreamProfileSet возвращает значительный объем кода, в котором нам нужны доступные потоки для одиночного потока глубины и возврат профиля. Можно написать собственные условия для поиска нужных потоков, будь то с определенным разрешением или с определенной кадровой скоростью, если есть условия отката, чтобы приложение могло работать с потоком подходящего формата.
Итоговая функция и центральный блок кода для доступа к данным потока — StreamSamples. Если убрать код безопасности и комментарии, код будет выглядеть так.
void StreamSamples(HWND hwndDlg, PXCCapture::DeviceInfо *dinfo, PXCCapture::Device::StreamProfileSet *profiles, bool synced, bool isRecord, bool isPlayback, pxcCHAR *file)
{
PXCSenseManager *pp=g_session->CreateSenseManager();
pp->QueryCaptureManager()->FilterByDeviceInfo(dinfo);
for (PXCCapture::StreamType st=PXCCapture::STREAM_TYPE_COLOR;st!=PXCCapture::STREAM_TYPE_ANY;st++)
{
PXCCapture::Device::StreamProfile &profile=(*profiles)[st];
if ([profile.imagelnfo.format) continue;
pp->EnableStream(st,profile.imagelnfo.width, profile.imagelnfo.height, profile.frameRate.max);
}
pp->QueryCaptureManager()->FilterByStreamProfiles(profiles);
MyHandler handler(hwndDlg);
if (pp->Init(&handler)>=PXC_STATUS_NO_ERROR)
{
pp->QueryCaptUreManager()->QueryDevice()->SetMirrorMode(PXCCapture: :Device: :MirrorMode: :MIRROR_MODE_DISABLED);
g_bConnected = true;
for (int nframes=0;g_bConnected==true;nframes++)
{
pxcStatus sts2=pp->AcquireFrame(synced);
if (sts2<PXC_STATUS_N0_ERR0R && sts2!=PXC_STATUS_DEVICE_LOST) break;
if (sts>=PXC_STATUS_NO_ERROR)
{
PXCCapture::Sample *sample = (PXCCapture::Sample*)pp->QuerySample();
short invalids[l];
invalids[0] = pp->QueryCaptureManager()->QueryDevice()->QueryDepthSaturationValue();
invalids [1] = pp->QueryCaptureManager()->QueryDevice()->QueryDepthl_owConfidenceValue();
PXCImage::ImageInfo dinfo=sample->depth->QueryInfo(); PXCImage::ImageData ddata;
if (sample->depth->AcquireAccess( PXCImage::ACCESS_READ, PXCImage::PIXEL_FORMAT_DEPTH,
&ddata)>=PXC_STATUS_NO_ERROR)
{
EnterCriticalSection(&g_depthdataCS);
memset ( g_depthdata, 0, sizeof(g_depthdata));
short *dpixels=(short*)ddata.planes[0];
int dpitch = ddata.pitches[0]/sizeof(short);
for (int у = 0; у < (int)dinfо.height; y++)
{
for (int x = 0; x < (int)dinfo.width; x++)
{
short d = dpixels[y*dpitch+x];
if (d == invalids[0] || d == invalids[l]) continue; g_depthdata[x][y] = d; } }
LeaveCriticalSection(&g_depthdataCS); g_bDepthdatafilled = true;
}
sample->depth->ReleaseAccess(&ddata);
} pp->ReleaseFrame();
} }
pp->Close(); pp->Release();
}
На первый взгляд, здесь многовато кода, но, если разобраться, вы увидите, что это просто несколько вызовов настройки, условный цикл и заключительная чистка перед возвращением к функции ThreadProc, вызвавшей этот код. Основная используемая переменная называется pp, это указатель диспетчера Intel RealSense SDK для наших основных действий. Примечание. Как было сказано выше, для повышения удобочитаемости кода из него удалено отслеживание ошибок, но на практике не следует создавать код, исходящий из того, что все без исключения вызовы Intel RealSense SDK будут успешными.
Первый фрагмент кода, который будет включать интересующие нас потоки, выглядит так.
pp->EnableStream(st,profile.imagelnfo.width, profile.imagelnfo.height, profile.frameRate.max);
Этот простой запрос включает тип потока с определенным разрешением и кадровой скоростью и отдает камере команду приготовиться к отправке нам этих необработанных данных. Следующая важная строка активирует диспетчер, чтобы он мог начать процесс получения данных.
MyHandler handler(hwndDlg);
if (pp->Init(&handler)>=PXC_STATUS_NO_ERROR)
Класс MyHandler определен в исходном проекте raw_streams и происходит от класса PXCSenseManager: Handler. В случае успеха вы узнаете, что камера включена и передает поток данных.
Теперь нужно запустить условный цикл, который будет работать до тех пор, пока какое-либо внешнее воздействие не изменит условие цикла. В этом цикле мы будем получать данные потока последовательно по одному кадру. Для этого используется команда AcquireFrame.
for (int nframes=0;g_bConnected==true;nframes++)
{
pxcStatus sts2=pp->AcquireFrame(synced);
Пока g_bConnected имеет значение true, мы будем делать это как можно быстрее в отдельном потоке, созданном для этой цели. Для получения фактических данных требуется еще несколько строк кода.
PXCCapture::Sample *sample = (PXCCapture::Sample*)pp->QuerySample();
short invalids[l];
invalids[0] = pp->QueryCaptureManager()->QueryDevice()->QueryDepthSaturationValue();
invalids [1] = pp->QueryCaptureManager()->QueryDevice()->QueryDepthl_owConfidenceValue();
PXCImage::ImageInfo dinfo=sample->depth->QueryInfo(); PXCImage::ImageData ddata;
if (sample->depth->AcquireAccess( PXCImage::ACCESS_READ, PXCImage::PIXEL_FORMAT_DEPTH,
&ddata)>=PXC_STATUS_NO_ERROR)
Первая команда получает от диспетчера образец указателя и использует его для получения указателя на фактические данные в памяти с помощью последней команды AcquireAccess. Код выполняет два запроса, чтобы узнать у диспетчера, какие значения соответствуют «насыщенному» пикселю и «недостоверному» пикселю. Оба этих условия могут возникнуть при получении данных глубины с камеры. Их следует игнорировать при интерпретации возвращенных данных. Основной результат этого кода: структура данных ddata теперь заполнена информацией, которая позволит нам получить прямой доступ к данным глубины (в этом примере). Изменив соответствующие параметры, можно получить доступ к данным потоков COLOR и IR, если они включены.
На этом фрагмент кода, относящийся к Intel RealSense SDK, завершен (от первого вызова инициализации до получения указателя на данные потока). Остальной код будет несколько привычнее для разработчиков, обладающих опытом создания программ для обработки изображений.
EnterCriticalSection(&g_depthdataCS);
memset ( g_depthdata, 0, sizeof(g_depthdata) );
short *dpixels=(short*)ddata.planes[0];
int dpitch = ddata.pitches[0]/sizeof(short);
for (int у = 0; у < (int)dinfо.height; y++)
{
for (int x = 0; x < (int)dinfo.width; x++)
{
short d = dpixels[y*dpitch+x];
if (d == invalids[0] || d == invalids[l]) continue;
g_depthdata[x][y] = d; }
}
LeaveCriticalSection(&g_depthdataCS);
Обратите внимание, что важный объект сеанса, созданный ранее, используется для блокировки нашего потока, чтобы никакой другой поток не мог получить доступ к нашим глобальным переменным. Это делается для того, чтобы можно было написать глобальный массив и быть уверенным в том, что на работу не будет влиять код из другой части приложения. Если проследить вложенные циклы, вы увидите, что после блокировки потока мы очищаем глобальный массив g_depthdata и заполняем его значениями из упомянутой выше структуры ddata, которая содержит указатель на данные глубины. Во вложенных циклах мы также сравниваем значение пикселей глубины с двумя недопустимыми значениями, которые мы задали раньше с помощью вызовов QueryDepthSaturationValue и QueryDepthLowConf idenceValue.
После переноса данных в глобальный массив поток ЦП может получить следующий кадр из поточных данных, а главный поток ЦП может приступить к анализу этих данных и к принятию решений. Можно даже создать новый рабочий поток для выполнения этого анализа, что даст возможность приложению работать в трех потоках и эффективнее использовать ресурсы многоядерной архитектуры.
Что делать с данными потока
Итак, теперь вы знаете, как получить поток данных с камеры 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
Автор: saul