Skip to content

Commit 2e7d4b4

Browse files
committed
RGB Camera support (reflection-only)
1 parent 89df3b9 commit 2e7d4b4

File tree

3 files changed

+190
-0
lines changed

3 files changed

+190
-0
lines changed

KinectHandler/KinectHandler.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ namespace KinectHandler
5252
// implemented in the c# handler
5353
}
5454

55+
array<BYTE>^ GetImageBuffer()
56+
{
57+
if (!IsInitialized || !kinect_->camera_enabled()) return __nullptr;
58+
const auto& [unmanagedBuffer, size] = kinect_->color_buffer();
59+
if (size <= 0) return __nullptr;
60+
61+
auto data = gcnew array<byte>(size); // Managed image placeholder
62+
Marshal::Copy(IntPtr(unmanagedBuffer), data, 0, size);
63+
return data; // Return managed array of bytes for our camera image
64+
}
65+
5566
List<KinectJoint^>^ GetTrackedKinectJoints()
5667
{
5768
if (!IsInitialized) return gcnew List<KinectJoint^>;
@@ -102,11 +113,36 @@ namespace KinectHandler
102113
int get() { return kinect_->status_result(); }
103114
}
104115

116+
property bool IsCameraEnabled
117+
{
118+
bool get() { return kinect_->camera_enabled(); }
119+
void set(const bool value) { kinect_->camera_enabled(value); }
120+
}
121+
105122
property bool IsSettingsDaemonSupported
106123
{
107124
bool get() { return DeviceStatus == 0; }
108125
}
109126

127+
property int CameraImageWidth
128+
{
129+
int get() { return kinect_->CameraImageSize().first; }
130+
}
131+
132+
property int CameraImageHeight
133+
{
134+
int get() { return kinect_->CameraImageSize().second; }
135+
}
136+
137+
Drawing::Size MapCoordinate(Vector3 position)
138+
{
139+
if (!IsInitialized) return Drawing::Size::Empty;
140+
const auto& [width, height] =
141+
kinect_->MapCoordinate(CameraSpacePoint{position.X, position.Y, position.Z});
142+
143+
return Drawing::Size(width, height);
144+
}
145+
110146
int InitializeKinect()
111147
{
112148
return kinect_->initialize();

KinectHandler/KinectWrapper.h

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class KinectWrapper
3030

3131
WAITABLE_HANDLE h_statusChangedEvent;
3232
WAITABLE_HANDLE h_bodyFrameEvent;
33+
WAITABLE_HANDLE h_colorFrameEvent;
3334
bool newBodyFrameArrived = false;
3435

3536
std::array<JointOrientation, JointType_Count> bone_orientations_;
@@ -39,6 +40,7 @@ class KinectWrapper
3940

4041
inline static bool initialized_ = false;
4142
bool skeleton_tracked_ = false;
43+
bool rgb_stream_enabled_ = false;
4244

4345
void updater()
4446
{
@@ -83,6 +85,20 @@ class KinectWrapper
8385
}
8486
}
8587

88+
void updateColorData()
89+
{
90+
if (!colorFrameReader)return; // Give up already
91+
92+
IColorFrame* colorFrame = nullptr;
93+
colorFrameReader->AcquireLatestFrame(&colorFrame);
94+
95+
if (!colorFrame) return;
96+
ResetBuffer(CameraBufferSize()); // Allocate buffer for image for copy
97+
98+
colorFrame->CopyConvertedFrameDataToArray(
99+
CameraBufferSize(), color_buffer_, ColorImageFormat_Bgra);
100+
}
101+
86102
bool initKinect()
87103
{
88104
// Get a working Kinect Sensor
@@ -130,6 +146,22 @@ class KinectWrapper
130146
if (bodyFrameSource) bodyFrameSource->Release();
131147
}
132148

149+
void initializeColor()
150+
{
151+
if (colorFrameReader)
152+
colorFrameReader->Release();
153+
154+
IColorFrameSource* colorFrameSource;
155+
kinectSensor->get_ColorFrameSource(&colorFrameSource);
156+
colorFrameSource->OpenReader(&colorFrameReader);
157+
158+
// Newfangled event based frame capture
159+
// https://github.com/StevenHickson/PCL_Kinect2SDK/blob/master/src/Microsoft_grabber2.cpp
160+
h_colorFrameEvent = (WAITABLE_HANDLE)CreateEvent(nullptr, FALSE, FALSE, nullptr);
161+
colorFrameReader->SubscribeFrameArrived(&h_colorFrameEvent);
162+
if (colorFrameSource) colorFrameSource->Release();
163+
}
164+
133165
void terminateSkeleton()
134166
{
135167
if (!bodyFrameReader)return; // No need to do anything
@@ -150,6 +182,26 @@ class KinectWrapper
150182
bodyFrameReader = nullptr;
151183
}
152184

185+
void terminateColor()
186+
{
187+
if (!colorFrameReader)return; // No need to do anything
188+
if (FAILED(colorFrameReader->UnsubscribeFrameArrived(h_colorFrameEvent)))
189+
{
190+
throw std::exception("Couldn't unsubscribe frame!");
191+
}
192+
__try
193+
{
194+
CloseHandle((HANDLE)h_colorFrameEvent);
195+
colorFrameReader->Release();
196+
}
197+
__except (EXCEPTION_EXECUTE_HANDLER)
198+
{
199+
// ignored
200+
}
201+
h_colorFrameEvent = NULL;
202+
colorFrameReader = nullptr;
203+
}
204+
153205
HRESULT kinect_status_result()
154206
{
155207
BOOLEAN avail;
@@ -246,6 +298,7 @@ class KinectWrapper
246298
if (!initialized_) return 1;
247299

248300
initializeSkeleton();
301+
initializeColor();
249302

250303
// Recreate the updater thread
251304
if (!updater_thread_)
@@ -306,6 +359,24 @@ class KinectWrapper
306359
pArgs->Release(); // Release the frame
307360
}
308361
}
362+
363+
if (h_colorFrameEvent && rgb_stream_enabled_)
364+
if (HANDLE handles[] = {reinterpret_cast<HANDLE>(h_colorFrameEvent)};
365+
// Wait for a frame to arrive, give up after >3s of nothing
366+
MsgWaitForMultipleObjects(_countof(handles), handles,
367+
false, 3000, QS_ALLINPUT) == WAIT_OBJECT_0)
368+
{
369+
IColorFrameArrivedEventArgs* pArgs = nullptr;
370+
if (colorFrameReader &&
371+
SUCCEEDED(colorFrameReader->GetFrameArrivedEventData(h_colorFrameEvent, &pArgs)))
372+
{
373+
[&,this](IColorFrameReader& sender, IColorFrameArrivedEventArgs& eventArgs)
374+
{
375+
updateColorData();
376+
}(*colorFrameReader, *pArgs);
377+
pArgs->Release(); // Release the frame
378+
}
379+
}
309380
}
310381
}
311382
}
@@ -319,6 +390,7 @@ class KinectWrapper
319390
{
320391
// Protect from null call
321392
terminateSkeleton();
393+
terminateColor();
322394

323395
return [&, this]
324396
{
@@ -376,13 +448,74 @@ class KinectWrapper
376448
return skeleton_positions_;
377449
}
378450

451+
std::tuple<BYTE*, int> color_buffer()
452+
{
453+
return std::make_tuple(color_buffer_, size_in_bytes_last_);
454+
}
455+
379456
bool skeleton_tracked()
380457
{
381458
return skeleton_tracked_;
382459
}
383460

461+
void camera_enabled(bool enabled)
462+
{
463+
rgb_stream_enabled_ = enabled;
464+
}
465+
466+
bool camera_enabled(void)
467+
{
468+
return rgb_stream_enabled_;
469+
}
470+
384471
int KinectJointType(int kinectJointType)
385472
{
386473
return KinectJointTypeDictionary.at(static_cast<JointType>(kinectJointType));
387474
}
475+
476+
std::pair<int, int> CameraImageSize()
477+
{
478+
return std::make_pair(1920, 1080);
479+
}
480+
481+
unsigned long CameraBufferSize()
482+
{
483+
const auto& [width, height] = CameraImageSize();
484+
return width * height * 4;
485+
}
486+
487+
std::pair<int, int> MapCoordinate(const CameraSpacePoint& skeletonPoint)
488+
{
489+
ColorSpacePoint spacePoint; // Holds the mapped coordinate
490+
const auto& result = coordMapper->MapCameraPointToColorSpace(skeletonPoint, &spacePoint);
491+
492+
return SUCCEEDED(result) && !std::isnan(spacePoint.X) && !std::isnan(spacePoint.Y)
493+
? std::make_pair(spacePoint.X, spacePoint.Y) // Send the mapped ones
494+
: std::make_pair(-1, -1); // Unknown coordinates - fall back to default drawing
495+
}
496+
497+
private:
498+
DWORD size_in_bytes_ = 0;
499+
DWORD size_in_bytes_last_ = 0;
500+
BYTE* color_buffer_ = nullptr;
501+
502+
BYTE* ResetBuffer(UINT size)
503+
{
504+
if (!color_buffer_ || size_in_bytes_ != size)
505+
{
506+
if (color_buffer_)
507+
{
508+
delete[] color_buffer_;
509+
color_buffer_ = nullptr;
510+
}
511+
512+
if (0 != size)
513+
{
514+
color_buffer_ = new BYTE[size];
515+
}
516+
size_in_bytes_ = size;
517+
}
518+
519+
return color_buffer_;
520+
}
388521
};

plugin_KinectOne/KinectOne.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
using System;
55
using System.Collections.ObjectModel;
66
using System.ComponentModel.Composition;
7+
using System.Drawing;
78
using System.Linq;
89
using System.Numerics;
10+
using System.Runtime.InteropServices.WindowsRuntime;
911
using Amethyst.Plugins.Contract;
12+
using Microsoft.UI.Xaml.Media.Imaging;
1013

1114
// To learn more about WinUI, the WinUI project structure,
1215
// and more about our project templates, see: http://aka.ms/winui-project-info.
@@ -35,6 +38,7 @@ public class KinectOne : KinectHandler.KinectHandler, ITrackingDevice
3538
public bool IsFlipSupported => true;
3639
public bool IsAppOrientationSupported => true;
3740
public object SettingsInterfaceRoot => null;
41+
public WriteableBitmap CameraImage { get; set; }
3842

3943
public ObservableCollection<TrackedJoint> TrackedJoints { get; } =
4044
// Prepend all supported joints to the joints list
@@ -56,6 +60,8 @@ public class KinectOne : KinectHandler.KinectHandler, ITrackingDevice
5660
public void OnLoad()
5761
{
5862
PluginLoaded = true;
63+
64+
CameraImage = new WriteableBitmap(CameraImageWidth, CameraImageHeight);
5965
}
6066

6167
public void Initialize()
@@ -104,6 +110,16 @@ public void Update()
104110
TrackedJoints[trackedJoints.IndexOf(x)].Position = x.Position.Safe();
105111
TrackedJoints[trackedJoints.IndexOf(x)].Orientation = x.Orientation.Safe();
106112
});
113+
114+
// Update camera feed
115+
if (!IsCameraEnabled) return;
116+
CameraImage.DispatcherQueue.TryEnqueue(async () =>
117+
{
118+
var buffer = GetImageBuffer(); // Read from Kinect
119+
if (buffer is null || buffer.Length <= 0) return;
120+
await CameraImage.PixelBuffer.AsStream().WriteAsync(buffer);
121+
CameraImage.Invalidate(); // Enqueue for preview refresh
122+
});
107123
}
108124

109125
public void SignalJoint(int jointId)
@@ -119,6 +135,11 @@ public override void StatusChangedHandler()
119135
// Request a refresh of the status UI
120136
Host?.RefreshStatusInterface();
121137
}
138+
139+
public Func<BitmapSource> GetCameraImage => () => CameraImage;
140+
public Func<bool> GetIsCameraEnabled => () => IsCameraEnabled;
141+
public Action<bool> SetIsCameraEnabled => value => IsCameraEnabled = value;
142+
public Func<Vector3, Size> MapCoordinateDelegate => MapCoordinate;
122143
}
123144

124145
internal static class PoseUtils

0 commit comments

Comments
 (0)