@camstack/addon-pipeline 1.2.328 → 1.2.330

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/THIRD_PARTY_MODELS.md +2 -2
  2. package/dist/audio-analyzer/index.js +2 -2
  3. package/dist/audio-analyzer/index.mjs +2 -2
  4. package/dist/{default-detection-model-1SadEKsW.js → default-detection-model-B6gKGnfx.js} +53 -71
  5. package/dist/{default-detection-model-CMeVTcPZ.mjs → default-detection-model-CPDT_eFT.mjs} +53 -71
  6. package/dist/detection-pipeline/index.js +607 -39
  7. package/dist/detection-pipeline/index.mjs +607 -39
  8. package/dist/{dist-wJO7mXeO.js → dist-CMeICL07.js} +781 -465
  9. package/dist/{dist-Bpub2VcJ.mjs → dist-CuPEyZY2.mjs} +736 -438
  10. package/dist/motion-wasm/index.js +1 -1
  11. package/dist/motion-wasm/index.mjs +1 -1
  12. package/dist/{node-BjCSgD4M.mjs → node-USq5jYwq.mjs} +1 -1
  13. package/dist/{node-BCrqaejt.js → node-X_0Tka7-.js} +1 -1
  14. package/dist/pipeline-runner/index.js +355 -104
  15. package/dist/pipeline-runner/index.mjs +355 -104
  16. package/dist/{process-memory-BaKQ8u2G.mjs → process-memory-07XYts8V.mjs} +1 -1
  17. package/dist/{process-memory-iUJuTpXs.js → process-memory-o4LFWbBN.js} +1 -1
  18. package/dist/recorder/index.js +7 -7
  19. package/dist/recorder/index.mjs +7 -7
  20. package/dist/{segment-demux-js-C5ez9LYG.mjs → segment-demux-js-Bq8DrH0b.mjs} +2 -2
  21. package/dist/{segment-demux-js-Opx6Hv9G.js → segment-demux-js-FqaQjG7n.js} +2 -2
  22. package/dist/stream-broker/_stub.js +2 -2
  23. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B94LgsV0.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-EYRUYv_r.mjs} +3 -3
  24. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BnP7C_HO.mjs +26 -0
  25. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-CR0--kRv.mjs +26 -0
  26. package/dist/stream-broker/demux-worker-child.js +1 -1
  27. package/dist/stream-broker/demux-worker-child.mjs +1 -1
  28. package/dist/stream-broker/{hostInit-xOx7AvgW.mjs → hostInit-iGdBpvGc.mjs} +3 -3
  29. package/dist/stream-broker/index.js +4 -4
  30. package/dist/stream-broker/index.mjs +4 -4
  31. package/dist/stream-broker/remoteEntry.js +1 -1
  32. package/package.json +1 -1
  33. package/python/edgetpu_dequant.py +38 -1
  34. package/python/inference_pool.py +31 -17
  35. package/python/postprocessors/test_yolox_plate_edgetpu.py +126 -0
  36. package/python/postprocessors/test_yunet_edgetpu.py +149 -0
  37. package/python/postprocessors/testdata/yolox_plate_int8_outputs.json +1 -0
  38. package/python/postprocessors/testdata/yunet_int8_outputs.json +1 -0
  39. package/python/postprocessors/yolo.py +44 -0
  40. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-BPWYf4_O.mjs +0 -26
  41. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-BpXPNINd.mjs +0 -26
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-pipeline",
3
- "version": "1.2.328",
3
+ "version": "1.2.330",
4
4
  "description": "Pipeline bundle — runner, detection, motion, audio + stream broker. Multi-entry npm package shipping pipeline addons under a single bundle.",
5
5
  "keywords": [
6
6
  "camstack",
@@ -28,7 +28,16 @@ def dequant_edgetpu_output(
28
28
  that exposes ``tolist``. Float dtypes are returned unchanged. A zero or
29
29
  missing scale becomes a plain float cast — we never invent a scale.
30
30
  """
31
- if hasattr(value, "tolist") and not isinstance(value, (list, tuple)):
31
+ is_array = hasattr(value, "tolist") and not isinstance(value, (list, tuple))
32
+ if is_array and _is_integer_dtype(dtype_name) and scale is not None and float(scale) > 0.0:
33
+ # A numpy / TFLite tensor is dequantised VECTORISED, through the
34
+ # array's own methods (no numpy import here). The list walk below
35
+ # costs ~0.3 us per value: YuNet at 640 emits 134 400 of them per
36
+ # frame, tens of milliseconds the predict closure would spend in
37
+ # Python. The result is a float32 array; every postprocessor reads
38
+ # its inputs through `np.asarray`/`np.array`.
39
+ return (value.astype("float32") - float(int(zero_point))) * float(scale)
40
+ if is_array:
32
41
  # numpy / TFLite tensor — decide from dtype_name, then walk lists.
33
42
  value = value.tolist()
34
43
 
@@ -53,3 +62,31 @@ def _scale_tree(value: Any, scale: float, zero_point: int) -> Any:
53
62
  if isinstance(value, (list, tuple)):
54
63
  return [_scale_tree(v, scale, zero_point) for v in value]
55
64
  return (float(value) - zero_point) * scale
65
+
66
+
67
+ # Postprocessors that find their tensors by NAME. The edgetpu predict closure
68
+ # keys their outputs by the TFLite tensor name, exactly as the onnx, openvino
69
+ # and coreml runtimes key every model's; every other postprocessor keeps the
70
+ # positional "0".."n-1" keys (`ssd.py` reads "0".."3" of
71
+ # TFLite_Detection_PostProcess, whose names carry ":1", ":2", ":3").
72
+ # `yunet` matches its twelve outputs by name only (D645): `cls_*` and `obj_*`
73
+ # have identical shapes, so a position cannot say which is which. `yolo` finds
74
+ # the raw YOLOX head of the `yolox-tiny-plate` Coral build by name
75
+ # (`reg_8` … `score_32`); its other paths pick a tensor by shape, never by key.
76
+ NAME_KEYED_POSTPROCESSORS = frozenset({"yunet", "yolo"})
77
+
78
+
79
+ def edgetpu_output_keys(names: "list[str]", postprocessor: "str | None") -> "list[str]":
80
+ """The key under which each output of an Edge TPU model reaches its postprocessor.
81
+
82
+ Named only for a postprocessor in ``NAME_KEYED_POSTPROCESSORS``, and only
83
+ when every name is non-empty and distinct; otherwise positional — and a
84
+ name-keyed postprocessor then raises, naming the keys it got, instead of
85
+ decoding tensors it cannot tell apart.
86
+ """
87
+ positional = [str(i) for i in range(len(names))]
88
+ if postprocessor not in NAME_KEYED_POSTPROCESSORS:
89
+ return positional
90
+ if any(not n for n in names) or len(set(names)) != len(names):
91
+ return positional
92
+ return list(names)
@@ -1319,9 +1319,19 @@ def _load_model(slot: ModelSlot, config: dict) -> None:
1319
1319
  slot.config["_edgetpu_in_dtype"] = in_dtype
1320
1320
  slot.config["inputWidth"] = in_w
1321
1321
  slot.config["inputHeight"] = in_h
1322
- slot.config.setdefault("inputSize", max(in_w, in_h))
1322
+ # The FILE's input size is the truth for a compiled Edge TPU graph: a
1323
+ # Coral build may be compiled at a smaller input than the catalog
1324
+ # entry's other formats (activations share the 8 MB on-chip memory
1325
+ # with the parameter cache, D710), and a decoder sized from the
1326
+ # catalog would put every prior in the wrong cell.
1327
+ slot.config["inputSize"] = max(in_w, in_h)
1323
1328
  in_index = int(in_det["index"])
1324
1329
  out_index = [int(o["index"]) for o in out_dets]
1330
+ from edgetpu_dequant import edgetpu_output_keys
1331
+
1332
+ out_keys = edgetpu_output_keys(
1333
+ [str(o.get("name") or "") for o in out_dets], slot.config.get("postprocessor")
1334
+ )
1325
1335
  sys.stderr.write(
1326
1336
  f"EdgeTPU: loaded {os.path.basename(path)} in={in_w}x{in_h} "
1327
1337
  f"dtype={in_dtype} outputs={len(out_dets)}\n"
@@ -1331,6 +1341,7 @@ def _load_model(slot: ModelSlot, config: dict) -> None:
1331
1341
  def predict(
1332
1342
  inp_dict: dict,
1333
1343
  _interp=interp, _lock=lock, _in=in_index, _outs=out_index, _details=out_dets,
1344
+ _keys=out_keys,
1334
1345
  ) -> dict:
1335
1346
  from edgetpu_dequant import dequant_edgetpu_output
1336
1347
 
@@ -1338,22 +1349,25 @@ def _load_model(slot: ModelSlot, config: dict) -> None:
1338
1349
  with _lock:
1339
1350
  _interp.set_tensor(_in, tensor)
1340
1351
  _interp.invoke()
1341
- # Positional keys — ssd reads "0".."3"; yolo-split-dfl pairs by
1342
- # shape. Integer outputs are dequantised here so every
1343
- # postprocessor sees float32 (SSD Detection_PostProcess is
1344
- # already float and passes through).
1345
- result = {}
1346
- for i, idx in enumerate(_outs):
1347
- raw = _interp.get_tensor(idx)
1348
- detail = _details[i]
1349
- q = detail.get("quantization") or (0.0, 0)
1350
- scale = float(q[0]) if q else 0.0
1351
- zp = int(q[1]) if q else 0
1352
- dtype_name = str(np.dtype(detail["dtype"]).name)
1353
- result[str(i)] = dequant_edgetpu_output(
1354
- raw, dtype_name=dtype_name, scale=scale, zero_point=zp
1355
- )
1356
- return result
1352
+ # `get_tensor` returns a copy, so the Coral is released before
1353
+ # the dequantisation below runs.
1354
+ raws = [_interp.get_tensor(idx) for idx in _outs]
1355
+ # Positional keys — ssd reads "0".."3"; yolo-split-dfl pairs by
1356
+ # shape — except for a postprocessor that matches by name
1357
+ # (`edgetpu_output_keys`). Integer outputs are dequantised here so
1358
+ # every postprocessor sees float32 (SSD Detection_PostProcess is
1359
+ # already float and passes through).
1360
+ result = {}
1361
+ for i, raw in enumerate(raws):
1362
+ detail = _details[i]
1363
+ q = detail.get("quantization") or (0.0, 0)
1364
+ scale = float(q[0]) if q else 0.0
1365
+ zp = int(q[1]) if q else 0
1366
+ dtype_name = str(np.dtype(detail["dtype"]).name)
1367
+ result[_keys[i]] = dequant_edgetpu_output(
1368
+ raw, dtype_name=dtype_name, scale=scale, zero_point=zp
1369
+ )
1370
+ return result
1357
1371
 
1358
1372
  slot.model = interp
1359
1373
  slot.predict_fn = predict
@@ -0,0 +1,126 @@
1
+ """Pins the YOLOX-tiny plate Coral build on REAL output tensors (D710 enforcement).
2
+
3
+ `yolox-tiny-plate` ships a `tflite` format compiled for the Edge TPU
4
+ (`camstack-yolox-tiny-plate_edgetpu.tflite`). Unlike its other builds — which
5
+ bake YOLOX's grid decode into the graph and emit the YOLOv8 layout — the Coral
6
+ graph stops at the raw head (an `Exp` and a coordinate Concat would share one
7
+ int8 scale between pixels and probabilities): per stride, `reg_s` (dx, dy,
8
+ log w, log h) and `score_s` (obj*cls), NHWC, int8. Three things must hold for
9
+ the step to emit the box the ONNX build emits:
10
+
11
+ - the edgetpu predict closure keys the `yolo` postprocessor's outputs by
12
+ tensor NAME (`edgetpu_output_keys`) — the head is found by its names;
13
+ - every output is dequantised (`edgetpu_dequant`);
14
+ - `yolo.py`'s `yolox_head_to_v8` decodes the head (row-major cells, centre =
15
+ (offset + cell) * stride, size = exp(log) * stride) into the layout the rest
16
+ of the postprocessor already reads.
17
+
18
+ The fixture (`testdata/yolox_plate_int8_outputs.json`) holds the raw outputs of
19
+ the int8 graph the Edge TPU file is compiled from, on the public
20
+ `reference-images/1-car-plate.jpg` letterboxed to 640, and the published ONNX
21
+ build's detections on the same frame. Produced by
22
+ `scripts/build-yolox-plate-edgetpu.py`.
23
+
24
+ `unittest.TestCase`: the key and dequant cases run on CI's bare interpreter;
25
+ the decode cases need numpy and are skipped, by name, without it.
26
+ """
27
+ import base64
28
+ import json
29
+ import os
30
+ import sys
31
+ import unittest
32
+ import zlib
33
+
34
+ _HERE = os.path.dirname(os.path.abspath(__file__))
35
+ sys.path.insert(0, os.path.dirname(_HERE)) # edgetpu_dequant lives one level up
36
+
37
+ from edgetpu_dequant import dequant_edgetpu_output, edgetpu_output_keys # noqa: E402
38
+
39
+ try:
40
+ import numpy as _numpy_probe
41
+ except ImportError:
42
+ _numpy_probe = None
43
+ _HAS_REAL_NUMPY = _numpy_probe is not None and hasattr(_numpy_probe, "ndarray")
44
+
45
+ with open(os.path.join(_HERE, "testdata", "yolox_plate_int8_outputs.json"), encoding="utf-8") as _fh:
46
+ _FIXTURE = json.load(_fh)
47
+
48
+ _OUTPUTS = _FIXTURE["outputs"]
49
+ _FRAME = _FIXTURE["frame"]
50
+ _STEP_GATE = 0.5 # plate-detection.defaultConfidence
51
+ _HEAD = ["reg_8", "score_8", "reg_16", "score_16", "reg_32", "score_32"]
52
+
53
+
54
+ def _raw(name):
55
+ data = zlib.decompress(base64.b64decode(_OUTPUTS[name]["zlibB64"]))
56
+ return [b - 256 if b > 127 else b for b in data]
57
+
58
+
59
+ def _iou(a, b):
60
+ ix = max(0.0, min(a[2], b[2]) - max(a[0], b[0]))
61
+ iy = max(0.0, min(a[3], b[3]) - max(a[1], b[1]))
62
+ inter = ix * iy
63
+ union = (a[2] - a[0]) * (a[3] - a[1]) + (b[2] - b[0]) * (b[3] - b[1]) - inter
64
+ return inter / union if union > 0 else 0.0
65
+
66
+
67
+ class YoloxPlateEdgetpuContract(unittest.TestCase):
68
+ def test_fixture_is_the_shipped_contract(self):
69
+ self.assertEqual(sorted(_OUTPUTS), sorted(_HEAD))
70
+ self.assertEqual(_OUTPUTS["reg_8"]["shape"], [1, 80, 80, 4])
71
+ self.assertEqual(_OUTPUTS["score_32"]["shape"], [1, 20, 20, 1])
72
+ for name, o in _OUTPUTS.items():
73
+ self.assertEqual(o["dtype"], "int8", name)
74
+ self.assertEqual(len(_FIXTURE["onnxDetections"]), 1)
75
+
76
+ def test_the_yolo_postprocessor_gets_named_outputs_on_the_coral(self):
77
+ listed = list(_OUTPUTS)
78
+ self.assertEqual(edgetpu_output_keys(listed, "yolo"), listed)
79
+
80
+ def test_dequantised_scores_are_probabilities(self):
81
+ for name in ("score_8", "score_16", "score_32"):
82
+ o = _OUTPUTS[name]
83
+ probs = dequant_edgetpu_output(_raw(name), dtype_name="int8", scale=o["scale"], zero_point=o["zeroPoint"])
84
+ self.assertTrue(all(-1e-6 <= p <= 1.0 + 1e-6 for p in probs), name)
85
+
86
+
87
+ @unittest.skipUnless(_HAS_REAL_NUMPY, "yolo.py needs numpy")
88
+ class YoloxPlateEdgetpuDecode(unittest.TestCase):
89
+ def _decode(self, keys):
90
+ import numpy as np
91
+ from yolo import postprocess_yolo
92
+
93
+ preds = {}
94
+ for key, name in zip(keys, _OUTPUTS):
95
+ o = _OUTPUTS[name]
96
+ raw = np.array(_raw(name), dtype=np.int8).reshape(o["shape"])
97
+ preds[key] = dequant_edgetpu_output(raw, dtype_name="int8", scale=o["scale"], zero_point=o["zeroPoint"])
98
+ config = {"confidence": _STEP_GATE, "labels": ["plate"], "numClasses": 1, "nmsIouThreshold": 0.45,
99
+ "inputSize": 640}
100
+ return postprocess_yolo(preds, config, _FRAME["w"], _FRAME["h"], _FRAME["scale"],
101
+ tuple(_FRAME["pad"]))["detections"]
102
+
103
+ def test_the_int8_head_decodes_to_the_onnx_plate(self):
104
+ got = self._decode(edgetpu_output_keys(list(_OUTPUTS), "yolo"))
105
+ ref = _FIXTURE["onnxDetections"]
106
+ self.assertEqual(len(got), len(ref))
107
+ self.assertEqual(got[0]["class"], "plate")
108
+ self.assertGreaterEqual(_iou(got[0]["bbox"], ref[0]["bbox"]), 0.9)
109
+ self.assertAlmostEqual(got[0]["score"], ref[0]["score"], delta=0.05)
110
+
111
+ def test_a_head_sized_for_another_input_is_refused(self):
112
+ import numpy as np
113
+ from yolo import yolox_head_to_v8
114
+
115
+ preds = {name: np.zeros(_OUTPUTS[name]["shape"], dtype=np.float32) for name in _OUTPUTS}
116
+ with self.assertRaises(ValueError):
117
+ yolox_head_to_v8(preds, 416)
118
+
119
+ def test_positional_keys_never_reach_the_head_decode(self):
120
+ from yolo import yolox_head_to_v8
121
+
122
+ self.assertIsNone(yolox_head_to_v8({str(i): None for i in range(6)}, 640))
123
+
124
+
125
+ if __name__ == "__main__":
126
+ unittest.main()
@@ -0,0 +1,149 @@
1
+ """Pins the YuNet Coral build on REAL output tensors (D710 enforcement).
2
+
3
+ `yunet-2023mar` ships a `tflite` format compiled for the Edge TPU
4
+ (`camstack-yunet-2023mar_edgetpu.tflite`). Its twelve outputs are int8, each
5
+ with its own scale and zero point, and `cls_*`/`obj_*` have identical shapes.
6
+ Three things must hold for the face step to emit what the ONNX build emits:
7
+
8
+ - the edgetpu predict closure keys this postprocessor's outputs by tensor NAME
9
+ (`edgetpu_output_keys`) — keyed "0".."11" the decoder cannot tell `cls_8`
10
+ from `obj_8` and refuses the frame;
11
+ - every output is dequantised (`edgetpu_dequant`) — read raw, a "probability"
12
+ is an integer in [-128, 127];
13
+ - the `yunet` decoder, at the model's own gate (0.85, D662), then finds the
14
+ ONNX build's faces, boxes AND landmarks (face-align depends on them).
15
+
16
+ The fixture (`testdata/yunet_int8_outputs.json`) holds the raw outputs of the
17
+ int8 graph the Edge TPU file is compiled from, on the public
18
+ `reference-images/3-persons.jpg` letterboxed to 640, and the ONNX build's
19
+ detections on the same frame. Produced by `scripts/build-yunet-edgetpu.py`.
20
+
21
+ `unittest.TestCase`: the key and dequant cases run on CI's bare interpreter;
22
+ the decode cases need numpy (`yunet.py` imports it) and are skipped, by name,
23
+ without it.
24
+ """
25
+ import base64
26
+ import json
27
+ import os
28
+ import sys
29
+ import unittest
30
+ import zlib
31
+
32
+ _HERE = os.path.dirname(os.path.abspath(__file__))
33
+ sys.path.insert(0, os.path.dirname(_HERE)) # edgetpu_dequant lives one level up
34
+
35
+ from edgetpu_dequant import dequant_edgetpu_output, edgetpu_output_keys # noqa: E402
36
+
37
+ try:
38
+ import numpy as _numpy_probe
39
+ except ImportError:
40
+ _numpy_probe = None
41
+ _HAS_REAL_NUMPY = _numpy_probe is not None and hasattr(_numpy_probe, "ndarray")
42
+
43
+ with open(os.path.join(_HERE, "testdata", "yunet_int8_outputs.json"), encoding="utf-8") as _fh:
44
+ _FIXTURE = json.load(_fh)
45
+
46
+ _OUTPUTS = _FIXTURE["outputs"]
47
+ _FRAME = _FIXTURE["frame"]
48
+ _STEP_GATE = 0.85 # yunet-2023mar.defaultConfidence (D662)
49
+ _NAMES = [f"{k}_{s}" for k in ("cls", "obj", "bbox", "kps") for s in (8, 16, 32)]
50
+
51
+
52
+ def _raw(name):
53
+ """The int8 tensor as a flat list of ints — no numpy needed."""
54
+ data = zlib.decompress(base64.b64decode(_OUTPUTS[name]["zlibB64"]))
55
+ return [b - 256 if b > 127 else b for b in data]
56
+
57
+
58
+ def _dequant_list(name):
59
+ o = _OUTPUTS[name]
60
+ return dequant_edgetpu_output(_raw(name), dtype_name=o["dtype"], scale=o["scale"], zero_point=o["zeroPoint"])
61
+
62
+
63
+ def _iou(a, b):
64
+ ix = max(0.0, min(a[2], b[2]) - max(a[0], b[0]))
65
+ iy = max(0.0, min(a[3], b[3]) - max(a[1], b[1]))
66
+ inter = ix * iy
67
+ union = (a[2] - a[0]) * (a[3] - a[1]) + (b[2] - b[0]) * (b[3] - b[1]) - inter
68
+ return inter / union if union > 0 else 0.0
69
+
70
+
71
+ class YunetEdgetpuContract(unittest.TestCase):
72
+ def test_fixture_is_the_shipped_contract(self):
73
+ self.assertEqual(sorted(_OUTPUTS), sorted(_NAMES))
74
+ for name, o in _OUTPUTS.items():
75
+ self.assertEqual(o["dtype"], "int8", name)
76
+ self.assertEqual(_OUTPUTS["cls_8"]["shape"], [1, 6400, 1])
77
+ self.assertEqual(_OUTPUTS["kps_32"]["shape"], [1, 400, 10])
78
+ self.assertGreaterEqual(len(_FIXTURE["onnxDetections"]), 3)
79
+
80
+ def test_yunet_outputs_are_keyed_by_name(self):
81
+ # The order the TFLite file lists them in is not the ONNX order.
82
+ listed = list(_OUTPUTS)
83
+ self.assertEqual(edgetpu_output_keys(listed, "yunet"), listed)
84
+
85
+ def test_ssd_keeps_its_positional_keys(self):
86
+ # ssd.py reads "0".."3"; TFLite_Detection_PostProcess names carry ":1".
87
+ names = ["TFLite_Detection_PostProcess", "TFLite_Detection_PostProcess:1",
88
+ "TFLite_Detection_PostProcess:2", "TFLite_Detection_PostProcess:3"]
89
+ self.assertEqual(edgetpu_output_keys(names, "ssd"), ["0", "1", "2", "3"])
90
+
91
+ def test_unnamed_or_duplicate_outputs_stay_positional(self):
92
+ self.assertEqual(edgetpu_output_keys(["cls_8", ""], "yunet"), ["0", "1"])
93
+ self.assertEqual(edgetpu_output_keys(["cls_8", "cls_8"], "yunet"), ["0", "1"])
94
+
95
+ def test_dequantised_probabilities_are_probabilities(self):
96
+ for name in ("cls_8", "obj_8", "cls_32", "obj_32"):
97
+ raw = _raw(name)
98
+ self.assertLess(min(raw), 0, name) # raw int8 is not a probability
99
+ probs = _dequant_list(name)
100
+ self.assertTrue(all(-1e-6 <= p <= 1.0 + 1e-6 for p in probs), name)
101
+
102
+
103
+ @unittest.skipUnless(_HAS_REAL_NUMPY, "yunet.py needs numpy")
104
+ class YunetEdgetpuDecode(unittest.TestCase):
105
+ def _decode(self, keys):
106
+ import numpy as np
107
+ from yunet import postprocess_yunet
108
+
109
+ preds = {}
110
+ for key, name in zip(keys, _OUTPUTS):
111
+ o = _OUTPUTS[name]
112
+ raw = np.array(_raw(name), dtype=np.int8).reshape(o["shape"])
113
+ preds[key] = dequant_edgetpu_output(raw, dtype_name="int8", scale=o["scale"], zero_point=o["zeroPoint"])
114
+ config = {"confidence": _STEP_GATE, "inputSize": 640, "nmsIouThreshold": 0.45}
115
+ return postprocess_yunet(preds, config, _FRAME["w"], _FRAME["h"], _FRAME["scale"],
116
+ tuple(_FRAME["pad"]))["detections"]
117
+
118
+ def test_the_int8_build_finds_the_onnx_faces(self):
119
+ got = self._decode(edgetpu_output_keys(list(_OUTPUTS), "yunet"))
120
+ ref = _FIXTURE["onnxDetections"]
121
+ self.assertEqual(len(got), len(ref))
122
+ for r in ref:
123
+ best = max(got, key=lambda g: _iou(g["bbox"], r["bbox"]))
124
+ self.assertGreaterEqual(_iou(best["bbox"], r["bbox"]), 0.9)
125
+ self.assertAlmostEqual(best["score"], r["score"], delta=0.03)
126
+ diag = ((r["bbox"][2] - r["bbox"][0]) ** 2 + (r["bbox"][3] - r["bbox"][1]) ** 2) ** 0.5
127
+ errors = [((p["x"] - q["x"]) ** 2 + (p["y"] - q["y"]) ** 2) ** 0.5
128
+ for p, q in zip(best["landmarks"], r["landmarks"])]
129
+ # face-align reads these. The build's parity pass measured a mean
130
+ # point error of 1.1% of the face diagonal (p95 2.8%) on 110 faces.
131
+ self.assertLess(sum(errors) / len(errors), 0.03 * diag)
132
+ self.assertLess(max(errors), 0.05 * diag)
133
+
134
+ def test_positional_keys_are_refused_not_misdecoded(self):
135
+ with self.assertRaises(ValueError):
136
+ self._decode([str(i) for i in range(len(_OUTPUTS))])
137
+
138
+ def test_a_vectorised_dequant_matches_the_list_walk(self):
139
+ import numpy as np
140
+
141
+ o = _OUTPUTS["bbox_8"]
142
+ arr = dequant_edgetpu_output(np.array(_raw("bbox_8"), dtype=np.int8), dtype_name="int8",
143
+ scale=o["scale"], zero_point=o["zeroPoint"])
144
+ self.assertEqual(str(arr.dtype), "float32")
145
+ self.assertTrue(np.allclose(arr, np.array(_dequant_list("bbox_8"), dtype=np.float32), atol=1e-6))
146
+
147
+
148
+ if __name__ == "__main__":
149
+ unittest.main()
@@ -0,0 +1 @@
1
+ {"frame": {"w": 1280, "h": 853, "scale": 0.5, "pad": [0, 107]}, "outputs": {"reg_32": {"shape": [1, 20, 20, 4], "dtype": "int8", "scale": 0.031176157295703888, "zeroPoint": 30, "zlibB64": "eNoV1Nlv24YBx3HZsiRTIkWdpCRKJMX7piiREmXdsmw1ki85sVMfSVfHreOtyZrt79keVgzDsIetGLJsRbOh6Lp1XTAUTQO0b20xIGlnp06d1LasY+7D5/X79MNPUhY5Re0JSmZNULMbsprbUtT8NVUuXNdk+5om2duaaG+pkr2pivaGIhZfVnh7TWYLl0XGWhJoo8MyWoum5AbFMhVGpBtphZmjdL5DGcoqmzU2pJy5rWQvmkbxmqoXtxSttClfkLTyhqiWrwpS+QovzqxwvN2l2dwsyehFjFGsuMiaKYaQYyIhRCUmE1blGqpnOoRhXXRLa6JRW1cyjcuK3rwsZxo9SW/0BLWxLMj1BV6otllupkbSZi5Bakw4LSRhnmIRAo2FcMQPkgnUx5E8LPNFRM9eIrKlHp9tXFEzs5czWmtV11urmjbXU5XWkiK2ujJXnxPo4gxNGnIiyaNBLO0HKRILxUPhaDTogREEBpNYMsgxRlw35hmzuqZn57csrb1tSy9tF6TO1g/yQvdli+1cyVGtRZ2szEpE1qAwBkXiGACROBpMBRKpRCiWimM0RlI6IalNySytWXbrlRlz/kZFbe9WpPbrVeHSXo3v7tWYxd0KufCjEn5ps0g2Vwtpu6ljopBGCSSWwlPxdIQXKExVGaGqS8aiadgbRbt6o2Y39hqZxs2mXNufFWo/nuMqt+boyu15on5rPtHcn4/N7s6R9etNptIr4bmqnuQUDk8rFIdkNJksFTPqatUyX6kXCrv1or3XsAo361p+vyHmf9LkzNstLv/mPG3e6aSsO4uodXsplt9fTFu7HdbebFL5hTIhlzMUbbNKwjZ0ulU19Y1mXtuZLeg36ra+U7a0nRlDea0syzdrvHRrVlDfbDPKzxZS0p2lOPvGcoLc7abxrQZLLtsU0zTwtCXjZIEyktWcybxUt+T1hilvVS31asHOXM1axnpW1zctWd6pyNx+U2V/2haZn3fS+J2lFLrXwaBeGXPmJdwpphPuFB6Hkqk4KiZyqXouR8yVLHq5UKCWMybf5vJmm5QvtkplVmRC3LA47tWSkr5ZEfE3GlRyr4nBCxbqxJPhs1Nv6OTxBHzyXyc8OPT4QQhUwqZyQdNjJVFL2pTFFZJmtoQJ1lwMNRfYEL+SJYgVg0GWFDawpOH+poK6Ulho9LUTPv3gBOr/5Qj4/p0j7+BBf9r7YoL2YGnC4ccIIBQmfWhIRJiIntZQ2mjEwmID8ZMtNoKYWMJPhTEgGUHckTA8fjYNnX94Fjj9wxF8/utvps/fegIM7z3zQIfD2GgYjr74Egz3v5gITjydiLl8IB3hA+lkMYoi9UTAzycgj9MTnjoYBh1PRn7Hk7F3+PkIOr9/HDj7zQE0+MVjaPDLr73ju8cAdOgIjR4DwRfvjeCzd0/A849OfOMnjrDP5UYCsBcFpn3Rqf854PGHx/7RvUPv6K9HwPhf34ODD06h/p+eBfq//SboeOsAGv3q0Dt+5xQAv52Ehl85fSfv9uHzt5+Do7vfgaP3n/snHw6izs+GIdeD49Dk/afBibcPoPHvDrzOP3/nHf/tFBzeP4aG9771j+8+BR2/PwJHf3zuG/9j6PM9cwOTLzzuwSdDwPHPvnfy76eA86OhZ+rRwDX12cDtfnTucf/nDPD8u+/xPDj3TH8ynHZ/OgRcD4eA89HIM/Fx3+34+AwYPxyAzi+nA/AEHAxgCa/XE53yOWAXOA644KmoJwzGp6OhBICE4kAUQnwIEAERTxCMTYehmDfijwFhX8QV8gUn/d7QVMAXcoX9YQCLYUmSZSsKLVQ0UqpLpDovkUZXocxVjS6u6+mZNQ0v9tRUoSsThbZIFFoCaTd5olDjCKvC4GaZIc0ySxozHC7aInWxtgzXFXShJ+jyVVpTt8mM9iqZUV8jVW3v4mteJ3l9B+eM60kxt5EQc+txMXclJpqrqJBbQYTc0g9QLtPFWKVNMGyD/D+01aR5"}, "score_8": {"shape": [1, 80, 80, 1], "dtype": "int8", "scale": 0.0028875411953777075, "zeroPoint": -128, "zlibB64": "eNrt0CEBAAAMArDqKHLT4epii7AEAAAA+KoKAABOBrlRgLs="}, "score_32": {"shape": [1, 20, 20, 1], "dtype": "int8", "scale": 0.0024907481856644154, "zeroPoint": -128, "zlibB64": "eNpraBgFgwkAAK60yAE="}, "score_16": {"shape": [1, 40, 40, 1], "dtype": "int8", "scale": 0.003375293919816613, "zeroPoint": -128, "zlibB64": "eNpraBgFo2AUDAOQU8JElLqSshKi1KkWB4wG6ijADwAR3B7W"}, "reg_8": {"shape": [1, 80, 80, 4], "dtype": "int8", "scale": 0.02890142798423767, "zeroPoint": 11, "zlibB64": "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"}, "reg_16": {"shape": [1, 40, 40, 4], "dtype": "int8", "scale": 0.04153960198163986, "zeroPoint": 35, "zlibB64": "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"}}, "int8Detections": [{"score": 0.8303, "bbox": [301.8, 569.5, 492.8, 634.4]}], "onnxDetections": [{"score": 0.8295, "bbox": [300.4, 570.4, 494.4, 633.2]}], "producedBy": "scripts/build-yolox-plate-edgetpu.py: raw outputs of the int8 graph the Edge TPU file is compiled from, on reference-images/1-car-plate.jpg letterboxed to 640; onnxDetections = the published ONNX at the step gate"}