PB Unity commited on
Commit
56dab45
·
verified ·
1 Parent(s): 1f90ae3

Upload 2 files

Browse files
Files changed (3) hide show
  1. .gitattributes +1 -0
  2. RunBlazePalm.cs +348 -0
  3. palm_detection_lite.sentis +3 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ palm_detection_lite.sentis filter=lfs diff=lfs merge=lfs -text
RunBlazePalm.cs ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ using UnityEngine;
2
+ using Unity.Sentis;
3
+ using UnityEngine.Video;
4
+ using UnityEngine.UI;
5
+ using Lays = Unity.Sentis.Layers;
6
+
7
+ /*
8
+ * Blaze Palm Inference
9
+ * ====================
10
+ *
11
+ * Basic inference script for blaze palm
12
+ *
13
+ * Put this script on the Main Camera
14
+ * Put blazeface.sentis in the Assets/StreamingAssets folder
15
+ * Create a RawImage of in the scene
16
+ * Put a link to that image in previewUI
17
+ * Put a video in Assets/StreamingAssets folder and put the name of it int videoName
18
+ * Or put a test image in inputImage
19
+ * Set inputType to appropriate input
20
+ */
21
+
22
+
23
+ public class RunBlazePalm : MonoBehaviour
24
+ {
25
+ //Drag a link to a raw image here:
26
+ public RawImage previewUI = null;
27
+
28
+ // Put your bounding box sprite image here
29
+ public Sprite boundingBoxTexture;
30
+
31
+ // optional images for palm markers
32
+ public Sprite[] markerTextures;
33
+
34
+ public string videoName = "chatting.mp4";
35
+
36
+ //
37
+ public Texture2D inputImage;
38
+
39
+ public InputType inputType = InputType.Video;
40
+
41
+ //Resolution of preview image/video
42
+ Vector2Int resolution = new Vector2Int(640, 640);
43
+ WebCamTexture webcam;
44
+ VideoPlayer video;
45
+
46
+ const BackendType backend = BackendType.GPUCompute;
47
+
48
+ RenderTexture targetTexture;
49
+ public enum InputType { Image, Video, Webcam };
50
+
51
+ //Some adjustable parameters for the model
52
+ [SerializeField, Range(0, 1)] float iouThreshold = 0.5f;
53
+ [SerializeField, Range(0, 1)] float scoreThreshold = 0.5f;
54
+ const int maxOutputBoxes = 64;
55
+
56
+ IWorker worker;
57
+
58
+ //Holds image size
59
+ int size;
60
+
61
+ Ops ops;
62
+ ITensorAllocator allocator;
63
+
64
+ Model model;
65
+
66
+ //webcam device name:
67
+ const string deviceName = "";
68
+
69
+ bool closing = false;
70
+
71
+ const string regressorsOutput = "Identity";
72
+ const string classificatorsOutput = "Identity_1";
73
+
74
+ public struct BoundingBox
75
+ {
76
+ public float centerX;
77
+ public float centerY;
78
+ public float width;
79
+ public float height;
80
+ }
81
+
82
+ void Start()
83
+ {
84
+ allocator = new TensorCachingAllocator();
85
+
86
+ //(Note: if using a webcam on mobile get permissions here first)
87
+
88
+ targetTexture = new RenderTexture(resolution.x, resolution.y, 0);
89
+ previewUI.texture = targetTexture;
90
+
91
+ SetupInput();
92
+
93
+ SetupModel();
94
+
95
+ SetupEngine();
96
+ }
97
+
98
+ void SetupInput()
99
+ {
100
+ switch (inputType)
101
+ {
102
+ case InputType.Webcam:
103
+ {
104
+ webcam = new WebCamTexture(deviceName, resolution.x, resolution.y);
105
+ webcam.requestedFPS = 30;
106
+ webcam.Play();
107
+ break;
108
+ }
109
+ case InputType.Video:
110
+ {
111
+ video = gameObject.AddComponent<VideoPlayer>();//new VideoPlayer();
112
+ video.renderMode = VideoRenderMode.APIOnly;
113
+ video.source = VideoSource.Url;
114
+ video.url = Application.streamingAssetsPath + "/"+videoName;
115
+ video.isLooping = true;
116
+ video.Play();
117
+ break;
118
+ }
119
+ default:
120
+ {
121
+ Graphics.Blit(inputImage, targetTexture);
122
+ }
123
+ break;
124
+ }
125
+ }
126
+
127
+ void Update()
128
+ {
129
+ if (inputType == InputType.Webcam)
130
+ {
131
+ // Format video input
132
+ if (!webcam.didUpdateThisFrame) return;
133
+
134
+ var aspect1 = (float)webcam.width / webcam.height;
135
+ var aspect2 = (float)resolution.x / resolution.y;
136
+ var gap = aspect2 / aspect1;
137
+
138
+ var vflip = webcam.videoVerticallyMirrored;
139
+ var scale = new Vector2(gap, vflip ? -1 : 1);
140
+ var offset = new Vector2((1 - gap) / 2, vflip ? 1 : 0);
141
+
142
+ Graphics.Blit(webcam, targetTexture, scale, offset);
143
+ }
144
+ if (inputType == InputType.Video)
145
+ {
146
+ var aspect1 = (float)video.width / video.height;
147
+ var aspect2 = (float)resolution.x / resolution.y;
148
+ var gap = aspect2 / aspect1;
149
+
150
+ var vflip = false;
151
+ var scale = new Vector2(gap, vflip ? -1 : 1);
152
+ var offset = new Vector2((1 - gap) / 2, vflip ? 1 : 0);
153
+ Graphics.Blit(video.texture, targetTexture, scale, offset);
154
+ }
155
+ if (inputType == InputType.Image)
156
+ {
157
+ Graphics.Blit(inputImage, targetTexture);
158
+ }
159
+
160
+ if (Input.GetKeyDown(KeyCode.Escape))
161
+ {
162
+ closing = true;
163
+ Application.Quit();
164
+ }
165
+
166
+ if (Input.GetKeyDown(KeyCode.P))
167
+ {
168
+ previewUI.enabled = !previewUI.enabled;
169
+ }
170
+ }
171
+
172
+
173
+ void LateUpdate()
174
+ {
175
+ if (!closing)
176
+ {
177
+ RunInference(targetTexture);
178
+ }
179
+ }
180
+
181
+
182
+ void AddGrid(float[] offsets, int rows, int repeats, int cellWidth, ref int n)
183
+ {
184
+ for (int j = 0; j < repeats * rows * rows; j++)
185
+ {
186
+ offsets[n++] = cellWidth * ((j / repeats) % rows - (rows - 1) * 0.5f);
187
+ offsets[n++] = cellWidth * ((j / repeats / rows) - (rows - 1) * 0.5f);
188
+ n += 2;
189
+ }
190
+ }
191
+
192
+ float[] GetGridBoxCoords()
193
+ {
194
+ var offsets = new float[2016 * 4];
195
+ int n = 0;
196
+ AddGrid(offsets, 24, 2, 8, ref n);
197
+ AddGrid(offsets, 12, 6, 16, ref n);
198
+ return offsets;
199
+ }
200
+
201
+ void SetupModel()
202
+ {
203
+ float[] offsets = GetGridBoxCoords();
204
+
205
+ model = ModelLoader.Load(Application.streamingAssetsPath + "/palm_detection_lite.sentis");
206
+
207
+ //We need to add extra layers to the model in order to aggregate the box predicions:
208
+ size = model.inputs[0].shape.ToTensorShape()[2]; // Input tensor width (192)
209
+
210
+ model.AddConstant(new Lays.Constant("zero", new int[] { 0 }));
211
+ model.AddConstant(new Lays.Constant("two", new int[] { 2 }));
212
+ model.AddConstant(new Lays.Constant("four", new int[] { 4 }));
213
+
214
+ model.AddLayer(new Lays.Slice("boxes", regressorsOutput, "zero", "four", "two"));
215
+
216
+ model.AddLayer(new Lays.Transpose("scores", classificatorsOutput, new int[] { 0, 2, 1 }));
217
+
218
+ model.AddConstant(new Lays.Constant("offsets",
219
+ new TensorFloat(new TensorShape(1, offsets.Length/4, 4), offsets)
220
+ ));
221
+ model.AddLayer(new Lays.Add("boxCoords", "boxes", "offsets"));
222
+ model.AddOutput("boxCoords");
223
+
224
+ model.AddConstant(new Lays.Constant("maxOutputBoxes", new int[] { maxOutputBoxes }));
225
+ model.AddConstant(new Lays.Constant("iouThreshold", new float[] { iouThreshold }));
226
+ model.AddConstant(new Lays.Constant("scoreThreshold", new float[] { scoreThreshold }));
227
+
228
+ model.AddLayer(new Lays.NonMaxSuppression("NMS", "boxCoords", "scores",
229
+ "maxOutputBoxes", "iouThreshold", "scoreThreshold",
230
+ centerPointBox: Lays.CenterPointBox.Center
231
+ ));
232
+ model.AddOutput("NMS");
233
+ }
234
+ public void SetupEngine()
235
+ {
236
+ worker = WorkerFactory.CreateWorker(backend, model);
237
+ ops = WorkerFactory.CreateOps(backend, allocator);
238
+ }
239
+
240
+ void DrawPalms(TensorFloat index3, TensorFloat regressors, int NMAX, Vector2 scale)
241
+ {
242
+ for (int n = 0; n < NMAX; n++)
243
+ {
244
+ //Draw bounding box of face
245
+ var box = new BoundingBox
246
+ {
247
+ centerX = index3[0, n, 0] * scale.x,
248
+ centerY = index3[0, n, 1] * scale.y,
249
+ width = index3[0, n, 2] * scale.x,
250
+ height = index3[0, n, 3] * scale.y
251
+ };
252
+ DrawBox(box, boundingBoxTexture);
253
+ if (regressors == null) continue;
254
+
255
+ //Draw markers starts of fingers
256
+ for (int j = 0; j < 7; j++)
257
+ {
258
+ var marker = new BoundingBox
259
+ {
260
+ centerX = box.centerX + (regressors[0, n, 4 + j * 2] - regressors[0, n, 0]) * scale.x,
261
+ centerY = box.centerY + (regressors[0, n, 4 + j * 2 + 1] - regressors[0, n, 1]) * scale.y,
262
+ width = 4f * scale.x,
263
+ height = 4f * scale.y,
264
+ };
265
+ DrawBox(marker, j < markerTextures.Length ? markerTextures[j] : boundingBoxTexture);
266
+ }
267
+ }
268
+ }
269
+
270
+ void RunInference(Texture source)
271
+ {
272
+ var transform = new TextureTransform();
273
+ transform.SetDimensions(size, size, 3);
274
+ transform.SetTensorLayout(0, 3, 1, 2);
275
+ using var image0 = TextureConverter.ToTensor(source, transform);
276
+
277
+ // Pre-process the image to make input in range (-1..1)
278
+ //using var image = ops.Mad(image0, 2f, -1f);
279
+
280
+ worker.Execute(image0);
281
+
282
+ using var boxCoords = worker.PeekOutput("boxCoords") as TensorFloat; //face coords
283
+ using var regressors = worker.PeekOutput(regressorsOutput) as TensorFloat; //contains markers
284
+
285
+ var NMS = worker.PeekOutput("NMS") as TensorInt;
286
+
287
+ using var boxCoords2 = boxCoords.ShallowReshape(boxCoords.shape.Unsqueeze(0)) as TensorFloat;
288
+ using var output = ops.GatherND(boxCoords2, NMS, 0);
289
+
290
+ using var regressors2 = regressors.ShallowReshape(regressors.shape.Unsqueeze(0)) as TensorFloat;
291
+ using var markersOutput = ops.GatherND(regressors2, NMS, 0);
292
+
293
+ output.MakeReadable();
294
+ markersOutput.MakeReadable();
295
+
296
+ ClearAnnotations();
297
+
298
+ Vector2 markerScale = previewUI.rectTransform.rect.size / size;
299
+
300
+ DrawPalms(output, markersOutput, output.shape[0], markerScale);
301
+
302
+ }
303
+
304
+
305
+ public void DrawBox(BoundingBox box, Sprite sprite)
306
+ {
307
+ var panel = new GameObject("ObjectBox");
308
+ panel.AddComponent<CanvasRenderer>();
309
+ panel.AddComponent<Image>();
310
+ panel.transform.SetParent(previewUI.transform, false);
311
+
312
+ var img = panel.GetComponent<Image>();
313
+ img.color = Color.white;
314
+ img.sprite = sprite;
315
+ img.type = Image.Type.Sliced;
316
+
317
+ panel.transform.localPosition = new Vector3(box.centerX, -box.centerY);
318
+ RectTransform rt = panel.GetComponent<RectTransform>();
319
+ rt.sizeDelta = new Vector2(box.width, box.height);
320
+ }
321
+ public void ClearAnnotations()
322
+ {
323
+ foreach (Transform child in previewUI.transform)
324
+ {
325
+ Destroy(child.gameObject);
326
+ }
327
+ }
328
+
329
+ void CleanUp()
330
+ {
331
+ closing = true;
332
+ ops?.Dispose();
333
+ allocator?.Dispose();
334
+ if (webcam) Destroy(webcam);
335
+ if (video) Destroy(video);
336
+ RenderTexture.active = null;
337
+ targetTexture.Release();
338
+ worker?.Dispose();
339
+ worker = null;
340
+ }
341
+
342
+ void OnDestroy()
343
+ {
344
+ CleanUp();
345
+ }
346
+
347
+ }
348
+
palm_detection_lite.sentis ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9d1746c58054f5419c994d8869e7945dd074d34bbedb27b518cb4d106447973b
3
+ size 3953743