1 | // -----------------------------------------------------------------------
|
---|
2 | // <copyright file="UserViewerColorizer.cs" company="Microsoft">
|
---|
3 | // Copyright (c) Microsoft Corporation. All rights reserved.
|
---|
4 | // </copyright>
|
---|
5 | // -----------------------------------------------------------------------
|
---|
6 |
|
---|
7 | namespace Microsoft.Samples.Kinect.Webserver.Sensor
|
---|
8 | {
|
---|
9 | using System;
|
---|
10 | using System.Collections.Generic;
|
---|
11 | using System.Diagnostics;
|
---|
12 | using System.Globalization;
|
---|
13 |
|
---|
14 | using Microsoft.Kinect;
|
---|
15 | using Microsoft.Kinect.Toolkit.Interaction;
|
---|
16 |
|
---|
17 | /// <summary>
|
---|
18 | /// Colorizes a depth image according to desired size and color mapping.
|
---|
19 | /// </summary>
|
---|
20 | internal class UserViewerColorizer
|
---|
21 | {
|
---|
22 | /// <summary>
|
---|
23 | /// Number of bytes per pixel in colorized image.
|
---|
24 | /// </summary>
|
---|
25 | private const int BytesPerPixel = 4;
|
---|
26 |
|
---|
27 | /// <summary>
|
---|
28 | /// Background color is always transparent.
|
---|
29 | /// </summary>
|
---|
30 | private const int BackgroundColor = 0x00000000;
|
---|
31 |
|
---|
32 | /// <summary>
|
---|
33 | /// Lookup table mapping player indexes (observable in depth image pixels) to 32-bit
|
---|
34 | /// ARGB color.
|
---|
35 | /// </summary>
|
---|
36 | private readonly int[] playerColorLookupTable;
|
---|
37 |
|
---|
38 | /// <summary>
|
---|
39 | /// Initializes a new instance of the <see cref="UserViewerColorizer"/> class.
|
---|
40 | /// </summary>
|
---|
41 | /// <param name="width">
|
---|
42 | /// Desired width of colorized image.
|
---|
43 | /// </param>
|
---|
44 | /// <param name="height">
|
---|
45 | /// Desired height of colorized image.
|
---|
46 | /// </param>
|
---|
47 | public UserViewerColorizer(int width, int height)
|
---|
48 | {
|
---|
49 | this.playerColorLookupTable = new int[SharedConstants.MaxUsersTracked + 1];
|
---|
50 |
|
---|
51 | this.SetResolution(width, height);
|
---|
52 | }
|
---|
53 |
|
---|
54 | /// <summary>
|
---|
55 | /// Desired width of colorized image.
|
---|
56 | /// </summary>
|
---|
57 | public int Width { get; private set; }
|
---|
58 |
|
---|
59 | /// <summary>
|
---|
60 | /// Desired height of colorized image.
|
---|
61 | /// </summary>
|
---|
62 | public int Height { get; private set; }
|
---|
63 |
|
---|
64 | /// <summary>
|
---|
65 | /// Buffer that holds colorized image.
|
---|
66 | /// </summary>
|
---|
67 | public byte[] Buffer { get; private set; }
|
---|
68 |
|
---|
69 | /// <summary>
|
---|
70 | /// Updates the desired resolution for user viewer image.
|
---|
71 | /// </summary>
|
---|
72 | /// <param name="width">
|
---|
73 | /// Desired width of colorized image.
|
---|
74 | /// </param>
|
---|
75 | /// <param name="height">
|
---|
76 | /// Desired height of colorized image.
|
---|
77 | /// </param>
|
---|
78 | public void SetResolution(int width, int height)
|
---|
79 | {
|
---|
80 | if ((this.Buffer == null) || (this.Width != width) || (this.Height != height))
|
---|
81 | {
|
---|
82 | this.Width = width;
|
---|
83 | this.Height = height;
|
---|
84 |
|
---|
85 | this.Buffer = new byte[width * height * BytesPerPixel];
|
---|
86 | }
|
---|
87 | }
|
---|
88 |
|
---|
89 | /// <summary>
|
---|
90 | /// Color the user viewer image pixels appropriately given the specified depth image.
|
---|
91 | /// </summary>
|
---|
92 | /// <param name="depthImagePixels">
|
---|
93 | /// Depth image from which we will colorize user viewer image.
|
---|
94 | /// </param>
|
---|
95 | /// <param name="depthWidth">
|
---|
96 | /// Width of depth image, in pixels.
|
---|
97 | /// </param>
|
---|
98 | /// <param name="depthHeight">
|
---|
99 | /// Height of depth image, in pixels.
|
---|
100 | /// </param>
|
---|
101 | public void ColorizeDepthPixels(DepthImagePixel[] depthImagePixels, int depthWidth, int depthHeight)
|
---|
102 | {
|
---|
103 | if (depthImagePixels == null)
|
---|
104 | {
|
---|
105 | throw new ArgumentNullException("depthImagePixels");
|
---|
106 | }
|
---|
107 |
|
---|
108 | if (depthWidth <= 0)
|
---|
109 | {
|
---|
110 | throw new ArgumentException(@"Width of depth image must be greater than zero", "depthWidth");
|
---|
111 | }
|
---|
112 |
|
---|
113 | if (depthWidth % this.Width != 0)
|
---|
114 | {
|
---|
115 | throw new ArgumentException(
|
---|
116 | string.Format(
|
---|
117 | CultureInfo.InvariantCulture,
|
---|
118 | "Depth image width '{0}' is not a multiple of the desired user viewer image width '{1}'",
|
---|
119 | depthWidth,
|
---|
120 | this.Width),
|
---|
121 | "depthWidth");
|
---|
122 | }
|
---|
123 |
|
---|
124 | if (depthHeight <= 0)
|
---|
125 | {
|
---|
126 | throw new ArgumentException(@"Height of depth image must be greater than zero", "depthHeight");
|
---|
127 | }
|
---|
128 |
|
---|
129 | if (depthHeight % this.Height != 0)
|
---|
130 | {
|
---|
131 | throw new ArgumentException(
|
---|
132 | string.Format(
|
---|
133 | CultureInfo.InvariantCulture,
|
---|
134 | "Depth image height '{0}' is not a multiple of the desired user viewer image height '{1}'",
|
---|
135 | depthHeight,
|
---|
136 | this.Height),
|
---|
137 | "depthHeight");
|
---|
138 | }
|
---|
139 |
|
---|
140 | int downscaleFactor = depthWidth / this.Width;
|
---|
141 | Debug.Assert(depthHeight / this.Height == downscaleFactor, "Downscale factor in x and y dimensions should be exactly the same.");
|
---|
142 |
|
---|
143 | int pixelDisplacementBetweenRows = depthWidth * downscaleFactor;
|
---|
144 |
|
---|
145 | unsafe
|
---|
146 | {
|
---|
147 | fixed (byte* colorBufferPtr = this.Buffer)
|
---|
148 | {
|
---|
149 | fixed (DepthImagePixel* depthImagePixelPtr = depthImagePixels)
|
---|
150 | {
|
---|
151 | fixed (int* playerColorLookupPtr = this.playerColorLookupTable)
|
---|
152 | {
|
---|
153 | // Write color values using int pointers instead of byte pointers,
|
---|
154 | // since each color pixel is 32-bits wide.
|
---|
155 | int* colorBufferIntPtr = (int*)colorBufferPtr;
|
---|
156 | DepthImagePixel* currentPixelRowPtr = depthImagePixelPtr;
|
---|
157 |
|
---|
158 | for (int row = 0; row < depthHeight; row += downscaleFactor)
|
---|
159 | {
|
---|
160 | DepthImagePixel* currentPixelPtr = currentPixelRowPtr;
|
---|
161 | for (int column = 0; column < depthWidth; column += downscaleFactor)
|
---|
162 | {
|
---|
163 | *colorBufferIntPtr++ = playerColorLookupPtr[currentPixelPtr->PlayerIndex];
|
---|
164 | currentPixelPtr += downscaleFactor;
|
---|
165 | }
|
---|
166 |
|
---|
167 | currentPixelRowPtr += pixelDisplacementBetweenRows;
|
---|
168 | }
|
---|
169 | }
|
---|
170 | }
|
---|
171 | }
|
---|
172 | }
|
---|
173 | }
|
---|
174 |
|
---|
175 | /// <summary>
|
---|
176 | /// Reset the lookup table mapping user indexes (observable in depth image pixels)
|
---|
177 | /// to 32-bit ARGB color to map all indexes to background color.
|
---|
178 | /// </summary>
|
---|
179 | public void ResetColorLookupTable()
|
---|
180 | {
|
---|
181 | // Initialize all player indexes to background color
|
---|
182 | for (int entryIndex = 0; entryIndex < this.playerColorLookupTable.Length; ++entryIndex)
|
---|
183 | {
|
---|
184 | this.playerColorLookupTable[entryIndex] = BackgroundColor;
|
---|
185 | }
|
---|
186 | }
|
---|
187 |
|
---|
188 | /// <summary>
|
---|
189 | /// Update the lookup table mapping user indexes (observable in depth image pixels)
|
---|
190 | /// to 32-bit ARGB color.
|
---|
191 | /// </summary>
|
---|
192 | /// <param name="userInfos">
|
---|
193 | /// User information obtained from an <see cref="InteractionFrame"/>.
|
---|
194 | /// </param>
|
---|
195 | /// <param name="defaultUserColor">
|
---|
196 | /// 32-bit ARGB representation of default color assigned to users.
|
---|
197 | /// </param>
|
---|
198 | /// <param name="userStates">
|
---|
199 | /// Mapping between user tracking IDs and user states (obtained from
|
---|
200 | /// <see cref="IUserStateManager"/>).
|
---|
201 | /// </param>
|
---|
202 | /// <param name="userColors">
|
---|
203 | /// Mapping between user states and 32-bit ARGB color.
|
---|
204 | /// </param>
|
---|
205 | public void UpdateColorLookupTable(UserInfo[] userInfos, int defaultUserColor, IDictionary<int, string> userStates, IDictionary<string, int> userColors)
|
---|
206 | {
|
---|
207 | if ((userInfos == null) || (userStates == null) || (userColors == null))
|
---|
208 | {
|
---|
209 | this.ResetColorLookupTable();
|
---|
210 | return;
|
---|
211 | }
|
---|
212 |
|
---|
213 | // Reset lookup table to have all player indexes map to default user color
|
---|
214 | for (int i = 1; i < this.playerColorLookupTable.Length; i++)
|
---|
215 | {
|
---|
216 | this.playerColorLookupTable[i] = defaultUserColor;
|
---|
217 | }
|
---|
218 |
|
---|
219 | // Iterate through user tracking Ids to populate color table.
|
---|
220 | for (int i = 0; i < userInfos.Length; ++i)
|
---|
221 | {
|
---|
222 | // Player indexes in depth image are shifted by one in order to be able to
|
---|
223 | // use zero as a marker to mean "pixel does not correspond to any player".
|
---|
224 | int depthPlayerIndex = i + 1;
|
---|
225 | var trackingId = userInfos[i].SkeletonTrackingId;
|
---|
226 |
|
---|
227 | string state;
|
---|
228 | if (userStates.TryGetValue(trackingId, out state))
|
---|
229 | {
|
---|
230 | int color;
|
---|
231 | if (userColors.TryGetValue(state, out color))
|
---|
232 | {
|
---|
233 | this.playerColorLookupTable[depthPlayerIndex] = color;
|
---|
234 | }
|
---|
235 | }
|
---|
236 | }
|
---|
237 | }
|
---|
238 | }
|
---|
239 | }
|
---|