Вы задумали создать простое приложение с потоком цветного изображения, использующее камеру Intel RealSense и Intel RealSense SDK, или просто собираетесь использовать поток цветного изображения в одном из приложений? Вам нужно понятное приложение, простое в отслеживании его работы, действующее напрямую, без огромного объема дополнительного кода, отвлекающего от того, чему вы хотите научиться? В этом случае вам повезло, поскольку именно такого результата я попытался добиться здесь: я старался создать простой, но эффективный пример приложения и документ, описывающий использование камеры и SDK Intel RealSense.
Этот пример был написан с помощью Intel RealSense SDK R4 и Visual Studio* на языке C#, он был протестирован в выпуске R5. Для него требуется камера Intel RealSense F200.
Структура проекта
В этом простом приложении я попытался отделить функциональность Intel RealSense SDK от кода уровня графического пользовательского интерфейса Windows* Form, чтобы разработчикам было удобнее сосредоточиться на функциональности SDK, относящейся к поточной передаче изображения. Для этого я создал на C# класс-оболочку (RSStreaming), заключив в него несколько классов Intel RealSense SDK.
Приложение Windows Form содержит лишь несколько кнопок и элемент управления PictureBox, в котором отображается поток цветного изображения.
Обратите внимание, что я не пытаюсь создать идеальное приложение. Я добавил определенные средства обработки исключений, но этим и ограничился. Все остальное — уже ваша забота, если вы хотите применить правильные принципы программирования и добиться от приложения стабильности и удобства для пользователей.
Структура проекта опирается на использование событий для передачи данных, вследствие чего можно обойтись без тесных связей. Был создан вспомогательный класс событий RSNewImageArg, наследующий от EventArg. Он используется для публикации текущего кадра из камеры в клиентское приложение.
Начало работы
Для работы потребуется камера Intel RealSense F200. Также требуется Intel RealSense SDK версии R4 или более поздней версии. Помимо этого, на компьютер необходимо установить соответствующий пакет Depth Camera Manager (DCM). SDK и F200 DCM можно загрузить здесь.
Требования
Требования к оборудованию
- Процессор Intel® Core™ 4-го поколения (на основе микроархитектуры Haswell).
- 8 ГБ свободного места на жестком диске.
- Камера Intel RealSense F200 (ее необходимо подключить к порту USB 3).
Требования к программному обеспечению
- 64-разрядная версия операционной системы Microsoft Windows 8.1 или Windows 10.
- Microsoft Visual Studio* 2010–2015 с последней версией пакета обновления.
- Microsoft .NET* 4.0 Framework для разработки на C#.
- Unity* 5.x или более поздней версии для разработки игр на движке Unity.
Компоненты проекта
RSNewImageArg.CS
RSNewImageArg является производным от класса C# EventArgs. Как видите, это небольшой класс-оболочка, к которому добавлен один частный элемент данных. Этот частный элемент данных Bitmap _bitMap содержит текущее растровое изображение, извлеченное из потока камеры.
Этот класс используется как аргумент события, когда класс RSStreaming отправляет событие обратно в класс Form, указывая, что новое растровое изображение готово к показу.
RSStreaming.CS
RSStreaming — это класс-оболочка (своего рода «движок») с данными потока цветного изображения из камеры Intel RealSense. Я написал этот класс, преследуя следующие цели.
- Ясно и четко выделить как можно больше функций Intel RealSense SDK и как можно дальше отделить их от клиентского приложения.
- Попытаться предоставить в коде комментарии, чтобы помочь читателю разобраться в том, что именно делает код.
Ниже описываются все функции, входящие в класс RSSpeechEngine.
public event EventHandler<RSNewImageArg> OnStreamingImage
Событие OnStreamingImage служит для переключения сообщения обратно к клиентскому приложению, сообщая ему о том, что новое цветное растровое изображение готово к показу. Клиент создает обработки событий для обработки объекта RSNewImageArg.
public bool Initialized
Свойство метода получения используется в качестве флага, указывая, что класс RSStreaming инициализирован.
public bool IsStreaming
Свойство метода получения используется в качестве флага, указывая, что класс RSStreaming в настоящее время ведет поточную передачу данных цветного изображения.
public void StartStreaming()
Проверяет, инициализирован ли класс. Если нет, вызывает InitCamera, чтобы убедиться в том, что класс запущен и работает. Затем эта функция вызывает функцию _senseManager.StreamFrames( … ).
Если вы успели ознакомиться с достаточным количеством материалов о разработке приложений для Intel RealSense, то, вероятно, вы обратили внимание, что получение данных с камеры зачастую осуществляется в циклах while. Например, приблизительно так.
while (!Stop)
{
/* Wait until a frame is ready: Synchronized or Asynchronous */
if (sm.AcquireFrame(Synced).IsError())
break;
/* Display images */
PXCMCapture.Sample sample = sm.QuerySample();
/* Render streams */
EventHandler<RenderFrameEventArgs> render = RenderFrame;
PXCMImage image = null;
if (MainPanel != PXCMCapture.StreamType.STREAM_TYPE_ANY && render != null)
{
image = sample[MainPanel];
render(this, new RenderFrameEventArgs(0, image));
}
if (PIPPanel != PXCMCapture.StreamType.STREAM_TYPE_ANY && render != null)
render(this, new RenderFrameEventArgs(1, sample[PIPPanel]));
/* Optional: Set Mirror State */
mirror = Mirror ? PXCMCapture.Device.MirrorMode.MIRROR_MODE_HORIZONTAL :
PXCMCapture.Device.MirrorMode.MIRROR_MODE_DISABLED;
if (mirror != sm.captureManager.device.QueryMirrorMode())
sm.captureManager.device.SetMirrorMode(mirror);
sm.ReleaseFrame();
/* Optional: Show performance tick */
if (image!=null)
timer.Tick(PXCMImage.PixelFormatToString(image.info.format)+" "+image.info.width+"x"+image.info.height);
}
Да, это солидная порция кода. Этот код, возможно, делает гораздо больше, чем мой пример приложения, но я всего лишь хочу сказать, что у меня цикл while не используется таким образом. В моем приложении используется функция StreamFrames(…). Эта функция обрабатывает цикл while внутри себя, а для каждого кадра она включает событие, на которое подписывается RSStreamingRGB. Работает это так.
- Запускаем поток PXCMSenseManager.StreamFrames(…).
- Ловим событие обработчиком событий.
- По окончании поточной передачи вызываем PXCMSenseManager.Close( ).
Мне нравится этот подход, потому что не хочется вручную возиться с циклом while, зная, когда и как останавливать цикл. Лучше пусть SDK позаботится об этом вместо меня. Вы увидите, как устроена эта методика, когда я буду рассказывать о функции InitCamera(), поэтому я не стану рассказывать про нее здесь. Просто убедитесь в том, что вы поняли, как можно передавать поток данных и разрешить SDK управлять циклом над необработанными данными, поступающими из камеры.
После вызова StreamFrames я задаю для логического флага _isStreaming значение true, чтобы уведомить класс и клиентское приложение о начале передачи потока.
public void StopStreaming ( )
Остановка передачи потока — действие, противоположное StartStreaming. Эта функция дает команду SDK для остановки передачи потока данных из камеры и вызывает Dispose() для уничтожения объектов данных.
private void InitCamera ( )
InitCamera() создает экземпляр PXCMSenseManager и включает нужный нам тип потока. Как видите, я указываю поток цветного изображения с разрешением 320 x 240 при скорости 30 кадров в секунду.
Если помните, я говорил о возможности использовать событие из PXCMSenseManger, чтобы сообщить классу о доступности нового кадра данных цветного изображения available. Для этого используется класс событий PXCMSenseMananger.Handler. Процесс достаточно прост: создаем экземпляр класса Handler, назначаем его обработчику событий через onNewSample, затем инициализируем объект PXCMSenseManager; _senseMananger с классом обработчика.
Затем задаем флагу _initialized значение true. Как было сказано выше, с помощью этого флага мы сообщаем либо этому классу, либо клиентскому приложению о том, что класс RSStreaming инициализирован.
private pxcmStatus OnNewSample( )
Это обработчик событий для объекта PXCMSenseMananger.Handler(). Если помните, в функции InitCamera() я задаю обработчик событий объектов для этой функции.
Обработчик событий должен соответствовать заданной подписи функции. Функция должна возвращать значение pxcmStatus и принимать два параметра.
- Mid. Идентификатор потока. Если посредством функции EnableVideoStreams запрошено несколько потоков, то это может быть PXCMCapture.CUID+0, PXCMCapture.CUID+1 и т. д.
- Sample. Доступное изображение.
Нужно преобразовать объект PXCMCapture.Sample в пригодное к использованию растровое изображение, которое может быть показано клиентским приложением.
Сначала я проверяю, что объект sample.color не является пустым и что внутреннее растровое изображение класса _colorImageData также не является пустым. Нужно убедиться в том, что _colorImageData не содержит никаких данных, а если содержит, то их нужно высвободить.
Затем нужно использовать объект sample.colors для заполнения _colorImagedata. Это, по сути, объект метаданных для объекта цветного изображения PXCMCapture.Sample. После этого можно перейти к созданию растрового изображения, указав его размер.
Получив растровое изображение и убедившись в том, что оно не пустое, я включаю событие OnStreamingImage, указывая источник события и новый объект RSNewImageArg.
Наконец, нам НЕОБХОДИМО высвободить текущий кадр из объекта PXCMSenseMananger, а также, поскольку это требуется подписью функции, возвратить pxcmStatus. В этом месте можно было добавить код для обработки исключений, но я предпочел этого не делать ради простоты. Если бы я написал такой код, то мог бы возвращать другое состояние pxcmStatus, но я просто возвращаю состояние успеха.
private void Dispose ( )
Dispose() выполняет очистку. Я проверяю, что диспетчер не является пустым и что он был инициализирован. Если оба условия выполнены, то я взываю его метод очистки. Я проверяю, что растровое изображение RSStreaming не является пустым, и очищаю его. Затем я везде устанавливаю значение null.
MainForm.CS
Главная форма — это графический пользовательский интерфейс, в котором отображается поток цветного изображения. В нем можно управлять объектом RSStreaming. У него две глобальных переменных: экземпляр RSStreamingRGB и растровое изображение. Растровое изображение будет содержать текущее изображение из текущего кадра, который отправлен классом RSStreamingRGB.
public MainForm( )
Конструктор форм. Он создает новый объект RSSTreamingRGB и предоставляет событию OnStreamingImage обработчик событий.
private void btnStream_Click( )
Обработчик событий при нажатии кнопки Start Streaming. Дает команду объекту _rsStreaming на начало передачи потока путем вызова функции StartStreaming() этого объекта.
private void btnStopStream_Click( )
Обработчик событий при нажатии кнопки Stop Streaming. Дает команду объекту _rsStreaming на остановку передачи потока путем вызова функции StopStreaming() этого объекта.
private void UpdateColorImageBox( object source, RSNewImageArg e )
UpdateColorImageBox — обработчик событий для события _rsStream.OnStreamingImage. Проверяет, что аргумент newImage не пуст и присваивает в этом случае _currentBitMap новому растровому изображению, используя newImage в качестве источника растрового изображения.
Если не создать новое растровое изображение, то _currentBitMap формы будет указывать на первоначальное растровое изображение, созданное пакетом SDK. Из-за этого могут возникнуть проблемы при вызове метода RSStreaming.Dispose. В клиентской программе имеется рамка рисунка, а в этой рамке отображается изображение, которое берется из SDK. Если при активной форме и рамке рисунка я попробую вызвать RSStreaming.Dispose, высвобождающий ресурсы SDK, то получится аварийный сбой, поскольку удаляется исходное изображение для рамки рисунка.
После назначения нового изображения для _currentBitMap я вызываю pictureBox.Invalidate() и тем самым запускаю событие Paint рамки рисунка.
private void pictureBox_Paint( object sender, PaintEventArgs e )
Это обработчик события Paint рамки рисунка, он переключается вызовом pictureBox.Invalidate(). Он дает команду на перерисовку рамки рисунка с использованием текущего исходного изображения.
Сначала проверяем, что _currentBitMap не пуст, а затем задаем для него наиболее позднее растровое изображение, которое сохраняется в _currentBitMap.
private void btnExit_Click( )
Здесь все элементарно. Просто вызываем Close(). Нет необходимости обрабатывать очистку, поскольку все это происходит в методе MainForm_FormClosing.
private void bMainForm_FormClosing( )
Это событие форм, закрывающее обработчик событий. При вызове метода Close() в любой заданной функции вызывается событие FormClosing. Чтобы не дублировать код, я разместил здесь весь код очистки. Проверяем, что _rsStream не пуст и что идет передача потока. Если эти условия выполнены, то вызываем _rsStream.StopStreaming(). Нет необходимости вызывать метод очистки для _rsStreaming, поскольку все необходимое уже сделано в StopStreaming.
Заключение
Надеюсь, с помощью этой статьи и примера кода вы лучше поняли, как применять Intel RealSense SDK для создания простых приложений с передачей цветного изображения. Я стремился продемонстрировать, что это можно сделать в простом и понятном приложении: в нем реализованы все элементы, необходимые для успешного создания вашего собственного приложения для передачи цветного изображения.