@camstack/addon-pipeline 1.2.293 → 1.2.295

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 (39) hide show
  1. package/THIRD_PARTY_MODELS.md +233 -0
  2. package/dist/audio-analyzer/index.js +2 -2
  3. package/dist/audio-analyzer/index.mjs +2 -2
  4. package/dist/{default-detection-model-DCQlW9fx.mjs → default-detection-model-CAUBVbgK.mjs} +53 -19
  5. package/dist/{default-detection-model-DTKHLLcC.js → default-detection-model-wNEISVA9.js} +58 -18
  6. package/dist/detection-pipeline/index.js +286 -458
  7. package/dist/detection-pipeline/index.mjs +286 -458
  8. package/dist/{dist-Ce8s7XqV.js → dist-BsVcf5wO.js} +1321 -833
  9. package/dist/{dist-C0Fr4hM5.mjs → dist-CuxSNLKW.mjs} +1316 -834
  10. package/dist/motion-wasm/index.js +1 -1
  11. package/dist/motion-wasm/index.mjs +1 -1
  12. package/dist/{node-Xm7amG2e.mjs → node-Cqb1QiXk.mjs} +1 -1
  13. package/dist/{node-DpOrymbW.js → node-UFk6I2f6.js} +1 -1
  14. package/dist/pipeline-runner/index.js +266 -72
  15. package/dist/pipeline-runner/index.mjs +266 -72
  16. package/dist/{process-memory-CBRjuhyg.js → process-memory-CJV29sPf.js} +1 -1
  17. package/dist/{process-memory-BN5qYGsD.mjs → process-memory-D0Nvs9rr.mjs} +1 -1
  18. package/dist/recorder/index.js +12 -2
  19. package/dist/recorder/index.mjs +12 -2
  20. package/dist/{segment-demux-js-BhMlS_Ow.js → segment-demux-js-DGwmf5hu.js} +1 -1
  21. package/dist/{segment-demux-js-Bhwpj5Wl.mjs → segment-demux-js-DIDWw1iE.mjs} +1 -1
  22. package/dist/stream-broker/_stub.js +2 -2
  23. package/dist/stream-broker/{_virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-DKd7oYba.mjs → _virtual_mf-localSharedImportMap___mfe_internal__addon_stream_broker_widgets-B7nBFqva.mjs} +2 -2
  24. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-CCIyBvRa.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-DoyA71_q.mjs → _virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_ui_mf_2_library__loadShare__.js-DeD_UFdb.mjs} +1 -1
  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-CCMIHjff.mjs → hostInit-BKlb3qac.mjs} +2 -2
  29. package/dist/stream-broker/index.js +3 -3
  30. package/dist/stream-broker/index.mjs +3 -3
  31. package/dist/stream-broker/remoteEntry.js +1 -1
  32. package/package.json +3 -2
  33. package/python/postprocessors/__init__.py +2 -0
  34. package/python/postprocessors/ssd.py +73 -17
  35. package/python/postprocessors/test_ssd.py +205 -0
  36. package/python/postprocessors/test_yunet.py +292 -0
  37. package/python/postprocessors/testdata/ssdlite_mobiledet_outputs.json +1 -0
  38. package/python/postprocessors/yunet.py +275 -0
  39. package/dist/stream-broker/_virtual_mf___mfe_internal__addon_stream_broker_widgets__loadShare___mf_0_camstack_mf_1_types__loadShare__.js-6IHzlLJ_.mjs +0 -26
@@ -0,0 +1,205 @@
1
+ """Pins the SSD decoder on REAL SSDLite MobileDet output tensors.
2
+
3
+ `ssdlite-mobiledet-coco-edgetpu` joins the two Coral detectors that already use
4
+ `postprocessor: 'ssd'`. Its graph ends in the same `TFLite_Detection_PostProcess`
5
+ op, but nothing had checked that the decoder reads MobileDet's outputs the way
6
+ the model writes them. Each class below pins one thing that could silently turn
7
+ a working model into wrong labels or empty frames:
8
+
9
+ - the output ORDER (boxes, classes, scores, count on positional keys "0".."3");
10
+ - the class-index OFFSET (91 score columns, background dropped by the op, so
11
+ the emitted index is 0-based and 0 is person);
12
+ - the COUNT clamp (MobileDet emits N=100 rows, not 20 like SSD MobileNet V2);
13
+ - the letterbox inversion, against boxes an unrelated detector (DETR) drew on
14
+ the same COCO frame;
15
+ - the score CEILING of this quantised graph (about 0.771), which decides which
16
+ confidence thresholds can ever pass.
17
+
18
+ The fixture (`testdata/ssdlite_mobiledet_outputs.json`) holds the model's raw
19
+ outputs on two COCO val2017 frames; its `_provenance` says how they were made.
20
+
21
+ `unittest.TestCase` with no numpy: `scripts/test-python-pool.sh` runs these on
22
+ CI's bare interpreter, where numpy is absent, and `ssd.py` decodes plain lists
23
+ for exactly that reason. The one case that needs numpy (ndarray inputs decode
24
+ the same as lists) is skipped, by name, without it.
25
+ """
26
+ import json
27
+ import os
28
+ import unittest
29
+
30
+ try: # real numpy when the sandbox has it
31
+ import numpy as _numpy_probe
32
+ except ImportError:
33
+ _numpy_probe = None
34
+
35
+ # `ndarray` is real-numpy-only: a stub another test module installed would import.
36
+ _HAS_REAL_NUMPY = _numpy_probe is not None and hasattr(_numpy_probe, "ndarray")
37
+
38
+ from ssd import COCO_90, label_for_class, postprocess_ssd # noqa: E402
39
+
40
+ _FIXTURE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "testdata",
41
+ "ssdlite_mobiledet_outputs.json")
42
+ with open(_FIXTURE, encoding="utf-8") as _fh:
43
+ _FRAMES = json.load(_fh)["frames"]
44
+
45
+ CATS = _FRAMES["coco_val2017_000000039769_two_cats"]
46
+ STREET = _FRAMES["coco_val2017_000000252219_street"]
47
+
48
+ # The catalog entry's input side; the pool sets `inputSize` from the graph.
49
+ INPUT_SIZE = 320
50
+ # The score column of this graph is LOGISTIC over a uint8 logit quantised at
51
+ # scale 0.046695, zero point 229, so the largest logit it can carry is
52
+ # (255 - 229) * 0.046695 = 1.2141 and sigmoid(1.2141) = 0.77102.
53
+ SCORE_CEILING = 0.77102
54
+
55
+
56
+ def _decode(frame: dict, confidence: float = 0.5, outputs: "dict | None" = None) -> list:
57
+ width, height = frame["orig"]
58
+ result = postprocess_ssd(
59
+ outputs if outputs is not None else frame["outputs"],
60
+ {"confidence": confidence, "inputSize": INPUT_SIZE},
61
+ width, height, frame["scale"], tuple(frame["pad"]),
62
+ )
63
+ return result["detections"]
64
+
65
+
66
+ def _iou(a: list, b: list) -> float:
67
+ ix = max(0.0, min(a[2], b[2]) - max(a[0], b[0]))
68
+ iy = max(0.0, min(a[3], b[3]) - max(a[1], b[1]))
69
+ inter = ix * iy
70
+ union = (a[2] - a[0]) * (a[3] - a[1]) + (b[2] - b[0]) * (b[3] - b[1]) - inter
71
+ return inter / union if union > 0 else 0.0
72
+
73
+
74
+ class FixtureShapeTest(unittest.TestCase):
75
+ """The fixture is what the MobileDet graph emits: [1,100,4] [1,100] [1,100] [1]."""
76
+
77
+ def test_output_shapes(self) -> None:
78
+ for frame in (CATS, STREET):
79
+ out = frame["outputs"]
80
+ self.assertEqual(len(out["0"]), 1)
81
+ self.assertEqual(len(out["0"][0]), 100)
82
+ self.assertTrue(all(len(row) == 4 for row in out["0"][0]))
83
+ self.assertEqual(len(out["1"][0]), 100)
84
+ self.assertEqual(len(out["2"][0]), 100)
85
+ self.assertEqual(out["3"], [100.0])
86
+
87
+ def test_scores_are_sorted_descending(self) -> None:
88
+ # TFLite_Detection_PostProcess ranks by score; a reordered export would not.
89
+ scores = STREET["outputs"]["2"][0]
90
+ self.assertEqual(scores, sorted(scores, reverse=True))
91
+
92
+
93
+ class OutputOrderTest(unittest.TestCase):
94
+ """Positional "1" is CLASSES and "2" is SCORES. Swapped, the labels are nonsense."""
95
+
96
+ def test_cats_decode_as_cats(self) -> None:
97
+ labels = [d["class"] for d in _decode(CATS)]
98
+ self.assertEqual(labels.count("cat"), 2, labels)
99
+
100
+ def test_street_decodes_three_people(self) -> None:
101
+ people = [d for d in _decode(STREET) if d["class"] == "person"]
102
+ self.assertEqual(len(people), 3)
103
+ # Left, middle and right of the frame.
104
+ centres = sorted((d["bbox"][0] + d["bbox"][2]) / 2 for d in people)
105
+ self.assertLess(centres[0], 200)
106
+ self.assertTrue(250 < centres[1] < 450, centres)
107
+ self.assertGreater(centres[2], 500)
108
+
109
+ def test_every_score_is_a_probability(self) -> None:
110
+ for frame in (CATS, STREET):
111
+ self.assertTrue(all(0.0 <= s <= 1.0 for s in frame["outputs"]["2"][0]))
112
+
113
+
114
+ class ClassOffsetTest(unittest.TestCase):
115
+ """Emitted class index = COCO-90 line number of `coco_labels.txt`, 0 = person."""
116
+
117
+ def test_raw_indices_behind_the_labels(self) -> None:
118
+ classes = STREET["outputs"]["1"][0]
119
+ scores = STREET["outputs"]["2"][0]
120
+ top = [int(c) for c, s in zip(classes, scores) if s >= 0.7]
121
+ self.assertEqual(set(top), {0}) # the three people
122
+ cat_classes = {int(c) for c, s in zip(CATS["outputs"]["1"][0], CATS["outputs"]["2"][0]) if s >= 0.7}
123
+ self.assertEqual(cat_classes, {16})
124
+
125
+ def test_label_map(self) -> None:
126
+ self.assertEqual(len(COCO_90), 90)
127
+ self.assertEqual(label_for_class(0), "person")
128
+ self.assertEqual(label_for_class(2), "car")
129
+ self.assertEqual(label_for_class(16), "cat")
130
+ self.assertEqual(label_for_class(17), "dog")
131
+ self.assertEqual(label_for_class(89), "toothbrush")
132
+ self.assertEqual(label_for_class(11), "n/a")
133
+ self.assertEqual(label_for_class(90), "90")
134
+
135
+ def test_gap_classes_are_dropped(self) -> None:
136
+ out = {k: v for k, v in STREET["outputs"].items()}
137
+ out["1"] = [[11.0] + list(STREET["outputs"]["1"][0][1:])] # "n/a" on the top row
138
+ labels = [d["class"] for d in _decode(STREET, outputs=out)]
139
+ self.assertNotIn("n/a", labels)
140
+ self.assertEqual(labels.count("person"), 2)
141
+
142
+
143
+ class CountClampTest(unittest.TestCase):
144
+ """All 100 rows are read when count says 100, and only `count` rows otherwise."""
145
+
146
+ def test_low_threshold_reads_past_row_20(self) -> None:
147
+ # SSD MobileNet V2 stops at 20 rows; MobileDet's detections go on to row 100.
148
+ dets = _decode(STREET, confidence=0.01)
149
+ self.assertGreater(len(dets), 20)
150
+
151
+ def test_count_limits_the_rows(self) -> None:
152
+ out = dict(STREET["outputs"])
153
+ out["3"] = [2.0]
154
+ self.assertEqual(len(_decode(STREET, confidence=0.01, outputs=out)), 2)
155
+
156
+
157
+ class LetterboxGeometryTest(unittest.TestCase):
158
+ """Boxes land in ORIGINAL-frame pixels, where another detector puts the same cats."""
159
+
160
+ # DETR ResNet-50 on this exact COCO frame (the facebook/detr-resnet-50 model
161
+ # card example): an independent reference for where the two cats are.
162
+ DETR_CATS = ([13.24, 52.05, 314.02, 470.93], [345.4, 23.85, 640.37, 368.72])
163
+
164
+ def test_cats_match_the_reference_boxes(self) -> None:
165
+ cats = [d["bbox"] for d in _decode(CATS) if d["class"] == "cat"]
166
+ for ref in self.DETR_CATS:
167
+ best = max(_iou(ref, box) for box in cats)
168
+ self.assertGreater(best, 0.85, (ref, cats))
169
+
170
+ def test_boxes_stay_inside_the_frame(self) -> None:
171
+ for frame in (CATS, STREET):
172
+ width, height = frame["orig"]
173
+ for d in _decode(frame, confidence=0.01):
174
+ x1, y1, x2, y2 = d["bbox"]
175
+ self.assertTrue(0 <= x1 < x2 <= width and 0 <= y1 < y2 <= height, d)
176
+
177
+
178
+ class ScoreCeilingTest(unittest.TestCase):
179
+ """This graph cannot score above ~0.771, so a stricter threshold returns nothing."""
180
+
181
+ def test_no_score_exceeds_the_ceiling(self) -> None:
182
+ for frame in (CATS, STREET):
183
+ self.assertLessEqual(max(frame["outputs"]["2"][0]), SCORE_CEILING)
184
+
185
+ def test_the_ceiling_is_reached(self) -> None:
186
+ self.assertAlmostEqual(max(CATS["outputs"]["2"][0]), 0.76953125)
187
+
188
+ def test_threshold_above_the_ceiling_finds_nothing(self) -> None:
189
+ self.assertEqual(_decode(STREET, confidence=0.78), [])
190
+ self.assertEqual(len([d for d in _decode(STREET, confidence=0.65) if d["class"] == "person"]), 3)
191
+
192
+
193
+ class NumpyInputTest(unittest.TestCase):
194
+ """The pool hands ndarrays; they must decode exactly like the lists above."""
195
+
196
+ @unittest.skipUnless(_HAS_REAL_NUMPY, "needs real numpy (not on CI's bare interpreter)")
197
+ def test_ndarrays_decode_like_lists(self) -> None:
198
+ np = _numpy_probe
199
+ for frame in (CATS, STREET):
200
+ arrays = {k: np.asarray(v, dtype=np.float32) for k, v in frame["outputs"].items()}
201
+ self.assertEqual(_decode(frame, confidence=0.3, outputs=arrays), _decode(frame, confidence=0.3))
202
+
203
+
204
+ if __name__ == "__main__":
205
+ unittest.main()
@@ -0,0 +1,292 @@
1
+ """Pins the YuNet decoder: prior layout, box/landmark decode, landmark order.
2
+
3
+ YuNet out of the box found 0 faces on 413 person crops through the SCRFD
4
+ decoder (2026-09-26 model-replacement spike, §2.2): one prior per cell instead
5
+ of two, centre+exp(size) boxes instead of anchor distances, and a score split
6
+ across `cls_*` and `obj_*`. Each class below pins one of those.
7
+
8
+ `unittest.TestCase`, not pytest-style: `scripts/test-python-pool.sh` runs the
9
+ `unittest.TestCase` modules on CI's bare interpreter — the self-hosted runner
10
+ has NO numpy. So everything that decides is plain Python and tested here
11
+ without it: prior layout, decode, landmark order, the score, NMS, the top-K cap,
12
+ and the output gate (`check_outputs` runs before `postprocess_yunet` touches
13
+ numpy, so its refusals are exercised through `postprocess_yunet` itself).
14
+
15
+ What genuinely needs real numpy, and is SKIPPED (named) without it: the
16
+ vectorised pre-filter (`np.maximum(cls, obj) >= conf`), and the decode of a
17
+ live prior through `postprocess_yunet` end to end (flatten/reshape of the
18
+ tensors, the cap applied inside it). Those run wherever numpy is installed —
19
+ every pool host — and in the spike harness.
20
+ """
21
+ import math
22
+ import sys
23
+ import types
24
+ import unittest
25
+
26
+ try: # real numpy when the sandbox has it
27
+ import numpy as _numpy_probe # noqa: F401
28
+ except ImportError:
29
+ _numpy_probe = None
30
+
31
+ # `ndarray` is real-numpy-only: a stub another module installed would import.
32
+ _HAS_REAL_NUMPY = _numpy_probe is not None and hasattr(_numpy_probe, "ndarray")
33
+
34
+ # Without numpy, `yunet.py`'s `import numpy` is satisfied by an EMPTY stub for
35
+ # the duration of the import only, and the stub is removed again at once: a
36
+ # stub left in `sys.modules` would be picked up by the next test module in the
37
+ # same `python3 -m unittest` process (test_scrfd needs `np.ceil` from its OWN
38
+ # stub), making the result depend on module order. `yunet` keeps its own
39
+ # reference; nothing it runs here touches it.
40
+ if _numpy_probe is None:
41
+ sys.modules["numpy"] = types.ModuleType("numpy")
42
+ try:
43
+ import yunet # noqa: F401
44
+ finally:
45
+ del sys.modules["numpy"]
46
+
47
+ from yunet import ( # noqa: E402
48
+ NUM_LANDMARKS,
49
+ STRIDES,
50
+ TOP_K,
51
+ cap_top_k,
52
+ check_outputs,
53
+ decode_prior,
54
+ grid_cols,
55
+ missing_outputs,
56
+ nms,
57
+ postprocess_yunet,
58
+ prior_cell,
59
+ score_of,
60
+ to_source,
61
+ value_count,
62
+ )
63
+
64
+
65
+ class PriorLayoutTest(unittest.TestCase):
66
+ """One prior per cell, row-major — NOT SCRFD's two anchors per cell."""
67
+
68
+ def test_grid_is_one_prior_per_cell_at_640(self):
69
+ self.assertEqual([grid_cols(s, 640) for s in STRIDES], [80, 40, 20])
70
+ # 6400 + 1600 + 400. SCRFD's layout would be twice this (16800).
71
+ self.assertEqual(sum(grid_cols(s, 640) ** 2 for s in STRIDES), 8400)
72
+
73
+ def test_index_is_row_major(self):
74
+ cols = grid_cols(8, 640)
75
+ self.assertEqual(prior_cell(0, cols), (0, 0))
76
+ self.assertEqual(prior_cell(1, cols), (0, 1)) # SCRFD: index 1 is still cell (0,0)
77
+ self.assertEqual(prior_cell(cols + 2, cols), (1, 2))
78
+ self.assertEqual(prior_cell(cols * cols - 1, cols), (cols - 1, cols - 1))
79
+
80
+
81
+ class DecodeTest(unittest.TestCase):
82
+ def test_box_is_centre_plus_exp_size_from_the_cell_corner(self):
83
+ # cell (row 1, col 2) at stride 8; centre offset (0.5, 0.5) → centre
84
+ # ((2+.5)*8, (1+.5)*8) = (20, 12); log size 0 → 8×8.
85
+ box, _ = decode_prior(8, 1, 2, [0.5, 0.5, 0.0, 0.0], [0.0] * 10)
86
+ self.assertEqual(box, (16.0, 8.0, 24.0, 16.0))
87
+
88
+ def test_size_is_exponential(self):
89
+ box, _ = decode_prior(16, 0, 0, [0.0, 0.0, math.log(3.0), math.log(2.0)], [0.0] * 10)
90
+ x1, y1, x2, y2 = box
91
+ self.assertAlmostEqual(x2 - x1, 48.0, places=4) # 3 * 16
92
+ self.assertAlmostEqual(y2 - y1, 32.0, places=4) # 2 * 16
93
+
94
+ def test_landmarks_are_cell_relative_in_stride_units(self):
95
+ kps = [0.25, 0.5, 0.75, 0.5, 0.5, 1.0, 0.25, 1.5, 0.75, 1.5]
96
+ _, points = decode_prior(32, 3, 4, [0.0, 0.0, 0.0, 0.0], kps)
97
+ self.assertEqual(len(points), NUM_LANDMARKS)
98
+ self.assertEqual(points[0], ((4 + 0.25) * 32, (3 + 0.5) * 32))
99
+ self.assertEqual(points[2], ((4 + 0.5) * 32, (3 + 1.0) * 32))
100
+
101
+ def test_score_is_geometric_mean_of_clamped_cls_and_obj(self):
102
+ self.assertAlmostEqual(score_of(0.81, 1.0), 0.9)
103
+ self.assertAlmostEqual(score_of(0.64, 0.25), 0.4)
104
+ self.assertEqual(score_of(1.3, 1.0), 1.0)
105
+ self.assertEqual(score_of(-0.2, 1.0), 0.0)
106
+
107
+ def test_to_source_undoes_the_letterbox(self):
108
+ # 320×160 crop letterboxed into 640: scale 2, pad (0, 160).
109
+ self.assertEqual(to_source(100.0, 260.0, 2.0, (0, 160)), (50.0, 50.0))
110
+
111
+
112
+ class LandmarkOrderTest(unittest.TestCase):
113
+ """YuNet emits the SUBJECT's right eye first = image-LEFT eye, which is
114
+ the order `face-align.ts` maps onto ARCFACE_TEMPLATE_112. The decoder must
115
+ pass the five points through in model order — a sort or a swap here
116
+ mirrors every aligned face."""
117
+
118
+ def test_points_keep_model_order(self):
119
+ # A frontal face: image-left eye, image-right eye, nose, mouth L, mouth R.
120
+ kps = [0.2, 0.3, 0.8, 0.3, 0.5, 0.55, 0.3, 0.8, 0.7, 0.8]
121
+ _, points = decode_prior(8, 0, 0, [0.5, 0.5, 0.0, 0.0], kps)
122
+ xs = [p[0] for p in points]
123
+ self.assertLess(xs[0], xs[1]) # eye 0 is image-left
124
+ self.assertLess(xs[3], xs[4]) # mouth 3 is image-left
125
+ self.assertEqual([round(x, 3) for x in xs], [1.6, 6.4, 4.0, 2.4, 5.6])
126
+
127
+
128
+ class MissingOutputsTest(unittest.TestCase):
129
+ """Name-only matching: a model without YuNet's twelve named tensors is
130
+ refused, not decoded as "no faces"."""
131
+
132
+ def test_all_twelve_present(self):
133
+ names = [f"{k}_{s}" for k in ("cls", "obj", "bbox", "kps") for s in STRIDES]
134
+ self.assertEqual(missing_outputs(names), [])
135
+
136
+ def test_names_what_is_missing(self):
137
+ names = [f"{k}_{s}" for k in ("cls", "bbox", "kps") for s in STRIDES]
138
+ self.assertEqual(missing_outputs(names), ["obj_8", "obj_16", "obj_32"])
139
+
140
+ def test_scrfd_style_outputs_are_refused(self):
141
+ names = [f"{k}_{s}" for k in ("score", "bbox", "kps") for s in STRIDES]
142
+ self.assertIn("cls_8", missing_outputs(names))
143
+
144
+
145
+ def _list_outputs(input_size=640, drop=()):
146
+ """Correctly-SHAPED outputs as nested lists — no numpy needed."""
147
+ out = {}
148
+ for s in STRIDES:
149
+ n = grid_cols(s, input_size) ** 2
150
+ for kind, width in (("cls", 1), ("obj", 1), ("bbox", 4), ("kps", 10)):
151
+ if f"{kind}_{s}" not in drop:
152
+ out[f"{kind}_{s}"] = [[[0.0] * width for _ in range(n)]]
153
+ return out
154
+
155
+
156
+ class OutputGateTest(unittest.TestCase):
157
+ """`check_outputs` runs before any numpy call, so these go through
158
+ `postprocess_yunet` itself on the bare interpreter."""
159
+
160
+ def test_missing_output_raises_instead_of_reporting_no_faces(self):
161
+ raw = _list_outputs(drop=("obj_32",))
162
+ with self.assertRaises(ValueError) as ctx:
163
+ postprocess_yunet(raw, {"confidence": 0.5}, 640, 640, 1.0, (0, 0))
164
+ self.assertIn("obj_32", str(ctx.exception))
165
+
166
+ def test_wrong_input_size_raises(self):
167
+ raw = _list_outputs(input_size=320)
168
+ with self.assertRaises(ValueError) as ctx:
169
+ postprocess_yunet(raw, {"confidence": 0.5, "inputSize": 640}, 640, 640, 1.0, (0, 0))
170
+ self.assertIn("does not match the model", str(ctx.exception))
171
+
172
+ def test_correct_outputs_pass_the_gate(self):
173
+ check_outputs(_list_outputs(), 640) # no raise
174
+
175
+ def test_value_count_of_nested_lists(self):
176
+ self.assertEqual(value_count([[[0.0] * 4 for _ in range(3)]]), 12)
177
+ self.assertEqual(value_count(7.0), 1)
178
+
179
+
180
+ class TopKCapTest(unittest.TestCase):
181
+ """The pure-Python NMS is O(n·kept): 5000 disjoint candidates took 2.8 s.
182
+ At most TOP_K, the best by score, may reach it."""
183
+
184
+ def _cands(self, n):
185
+ return [{"score": (i % 997) / 997.0, "_xyxy": (i, 0, i + 1, 1)} for i in range(n)]
186
+
187
+ def test_under_the_cap_is_untouched(self):
188
+ cands = self._cands(10)
189
+ kept, dropped = cap_top_k(cands)
190
+ self.assertIs(kept, cands)
191
+ self.assertEqual(dropped, 0)
192
+
193
+ def test_over_the_cap_keeps_the_best_k(self):
194
+ cands = self._cands(TOP_K + 250)
195
+ kept, dropped = cap_top_k(cands)
196
+ self.assertEqual(len(kept), TOP_K)
197
+ self.assertEqual(dropped, 250)
198
+ worst_kept = min(c["score"] for c in kept)
199
+ best_dropped = max(c["score"] for c in cands if c not in kept)
200
+ self.assertGreaterEqual(worst_kept, best_dropped)
201
+
202
+ def test_cap_is_well_below_upstream_and_above_real_frames(self):
203
+ # upstream 5000 (C++ NMS); measured max 110 candidates/frame at 0.1.
204
+ self.assertLessEqual(TOP_K, 5000)
205
+ self.assertGreaterEqual(TOP_K, 110 * 5)
206
+
207
+
208
+ class NmsTest(unittest.TestCase):
209
+ def test_keeps_best_of_overlapping_and_disjoint(self):
210
+ cands = [
211
+ {"score": 0.6, "_xyxy": (0, 0, 10, 10)},
212
+ {"score": 0.9, "_xyxy": (1, 1, 11, 11)},
213
+ {"score": 0.7, "_xyxy": (50, 50, 60, 60)},
214
+ ]
215
+ kept = nms(cands, 0.45)
216
+ self.assertEqual([k["score"] for k in kept], [0.9, 0.7])
217
+
218
+
219
+ def _outputs(np, input_size=640, live=None):
220
+ """Raw YuNet-shaped outputs, all priors dead except `live`:
221
+ {stride: (index, cls, obj, bbox4, kps10)}."""
222
+ out = {}
223
+ for s in STRIDES:
224
+ n = grid_cols(s, input_size) ** 2
225
+ out[f"cls_{s}"] = np.zeros((1, n, 1), np.float32)
226
+ out[f"obj_{s}"] = np.zeros((1, n, 1), np.float32)
227
+ out[f"bbox_{s}"] = np.zeros((1, n, 4), np.float32)
228
+ out[f"kps_{s}"] = np.zeros((1, n, 10), np.float32)
229
+ for s, (index, cls, obj, bbox, kps) in (live or {}).items():
230
+ out[f"cls_{s}"][0, index, 0] = cls
231
+ out[f"obj_{s}"][0, index, 0] = obj
232
+ out[f"bbox_{s}"][0, index] = bbox
233
+ out[f"kps_{s}"][0, index] = kps
234
+ return out
235
+
236
+
237
+ @unittest.skipUnless(_HAS_REAL_NUMPY, "postprocess_yunet round trip needs real ndarray ops")
238
+ class PostprocessYunetRoundTripTest(unittest.TestCase):
239
+ def setUp(self):
240
+ import numpy as np
241
+
242
+ self.np = np
243
+
244
+ def test_one_live_prior_decodes_to_source_pixels(self):
245
+ cols = grid_cols(16, 640)
246
+ index = 10 * cols + 20 # row 10, col 20
247
+ kps = [0.2, 0.3, 0.8, 0.3, 0.5, 0.55, 0.3, 0.8, 0.7, 0.8]
248
+ raw = _outputs(self.np, live={16: (index, 0.81, 1.0, [0.5, 0.5, math.log(4), math.log(4)], kps)})
249
+ # 1280×640 frame letterboxed into 640: scale 0.5, pad (0, 160).
250
+ out = postprocess_yunet(raw, {"confidence": 0.5, "inputSize": 640}, 1280, 640, 0.5, (0, 160))
251
+ dets = out["detections"]
252
+ self.assertEqual(len(dets), 1)
253
+ d = dets[0]
254
+ self.assertEqual(d["class"], "face")
255
+ self.assertAlmostEqual(d["score"], 0.9, places=3)
256
+ # network centre (328, 168), size 64 → [296,136,360,200] → source /0.5, y-160.
257
+ self.assertEqual(d["bbox"], [592.0, 0.0, 720.0, 80.0])
258
+ self.assertEqual(len(d["landmarks"]), NUM_LANDMARKS)
259
+ self.assertEqual(d["landmarks"][0], {"x": round((20.2 * 16) / 0.5, 1), "y": round((10.3 * 16 - 160) / 0.5, 1)})
260
+ self.assertLess(d["landmarks"][0]["x"], d["landmarks"][1]["x"])
261
+
262
+ def test_score_uses_both_tensors(self):
263
+ cols = grid_cols(8, 640)
264
+ # cls alone high, obj low → sqrt(0.9*0.1)=0.3 < 0.5: dropped.
265
+ raw = _outputs(self.np, live={8: (cols + 1, 0.9, 0.1, [0.5, 0.5, 1.0, 1.0], [0.0] * 10)})
266
+ out = postprocess_yunet(raw, {"confidence": 0.5}, 640, 640, 1.0, (0, 0))
267
+ self.assertEqual(out["detections"], [])
268
+
269
+ def test_prior_with_low_cls_but_high_obj_still_passes(self):
270
+ # sqrt(0.45 * 0.95) = 0.654 >= 0.5 although cls alone is under it: a
271
+ # prefilter on ONE tensor would drop a real face here.
272
+ cols = grid_cols(32, 640)
273
+ raw = _outputs(self.np, live={32: (cols + 3, 0.45, 0.95, [0.5, 0.5, 0.0, 0.0], [0.0] * 10)})
274
+ out = postprocess_yunet(raw, {"confidence": 0.5}, 640, 640, 1.0, (0, 0))
275
+ self.assertEqual(len(out["detections"]), 1)
276
+ self.assertAlmostEqual(out["detections"][0]["score"], 0.6538, places=3)
277
+
278
+ def test_cap_applies_inside_postprocess(self):
279
+ # TOP_K + 50 disjoint live priors on the stride-8 grid: all pass the
280
+ # score gate, none overlap, so only the cap can remove any.
281
+ n = grid_cols(8, 640) ** 2
282
+ raw = _outputs(self.np)
283
+ live = list(range(0, n, 2))[: TOP_K + 50]
284
+ raw["cls_8"][0, live, 0] = 0.9
285
+ raw["obj_8"][0, live, 0] = 0.9
286
+ raw["bbox_8"][0, live] = [0.5, 0.5, -1.0, -1.0] # ~3 px boxes, disjoint
287
+ out = postprocess_yunet(raw, {"confidence": 0.5}, 640, 640, 1.0, (0, 0))
288
+ self.assertEqual(len(out["detections"]), TOP_K)
289
+
290
+
291
+ if __name__ == "__main__":
292
+ unittest.main()
@@ -0,0 +1 @@
1
+ {"_provenance":"Raw output tensors of google-coral/test_data ssdlite_mobiledet_coco_qat_postprocess.tflite (sha256 32c486140391eb4dc43fca7113ad392be632dc5366687f2731f73d740678693f; the CPU build of the same QAT graph as ssdlite_mobiledet_coco_qat_postprocess_edgetpu.tflite, whose four outputs have the identical names, shapes and dtypes), captured 2026-09-26 on ai-edge-litert 2.1.3 through inference_pool.py's edgetpu load/preprocess/predict path with a CPU interpreter, letterbox 320. Frames are COCO val2017 images; only the model's numeric outputs are stored here, never the pixels.","frames":{"coco_val2017_000000039769_two_cats":{"orig":[640,480],"scale":0.5,"pad":[0,40],"outputs":{"0":[[[0.15955263376235962,0.534464955329895,0.713003933429718,0.9716978073120117],[0.2043571174144745,0.02350570261478424,0.8648291826248169,0.5011452436447144],[0.10257476568222046,0.014032959938049316,0.9505413174629211,0.996590256690979],[0.22124464809894562,0.04488908499479294,0.3162550926208496,0.29261064529418945],[0.03658798336982727,0.012482762336730957,0.40508130192756653,0.987517237663269],[0.1555221676826477,0.6529091000556946,0.8028101325035095,1.006462574005127],[0.24686561524868011,0.5220372080802917,0.4193393588066101,0.5692116618156433],[0.2072891891002655,0.1583276093006134,0.3572945296764374,0.4583382308483124],[0.23659078776836395,0.5290685892105103,0.42224830389022827,0.5870484113693237],[0.5743775367736816,0.23019926249980927,0.6594175100326538,0.34779107570648193],[0.3052724301815033,0.24310186505317688,0.9099631309509277,0.9973288774490356],[0.11994627118110657,0.3792511224746704,0.5946398973464966,0.9894982576370239],[0.2084560990333557,0.2897113263607025,0.3498774766921997,0.4769533574581146],[0.048862308263778687,-0.024405688047409058,0.8030894994735718,0.6076090335845947],[0.47406071424484253,0.039728641510009766,0.8791598677635193,0.9857875108718872],[0.20506274700164795,0.006122678518295288,0.3345203399658203,0.26888036727905273],[0.21482816338539124,0.2670758366584778,0.4976709187030792,0.48709094524383545],[0.17930985987186432,0.5188078880310059,0.6031879782676697,0.87400221824646],[0.5569137930870056,0.8219212889671326,0.6033983826637268,0.91482013463974],[0.5594297051429749,0.06481415033340454,0.6947370171546936,0.3435206711292267],[0.2410658895969391,0.526638925075531,0.3487098515033722,0.5821121335029602],[0.24299399554729462,0.8754947781562805,0.3305767774581909,0.9966028332710266],[0.09360367059707642,-0.027666032314300537,0.4855625033378601,0.7005844712257385],[0.21883206069469452,0.37879708409309387,0.3296947479248047,0.46383705735206604],[0.2706087529659271,0.8736841082572937,0.34845462441444397,0.9984135031700134],[0.23773013055324554,0.5050985217094421,0.5164366364479065,0.5824022889137268],[0.8736730217933655,0.004488527774810791,1.0054889917373657,0.9902228713035583],[0.23501813411712646,0.1322900503873825,0.3002501130104065,0.2705683708190918],[0.2720530033111572,0.9462981820106506,0.35584938526153564,0.9963359236717224],[0.247519388794899,0.944722592830658,0.3231050968170166,0.9993845820426941],[0.38305434584617615,0.378726065158844,0.47873106598854065,0.49631792306900024],[0.5535222291946411,0.7801342010498047,0.7023705244064331,0.8831243515014648],[0.7894991040229797,0.03270852565765381,0.9892374873161316,0.9928076267242432],[0.20900268852710724,0.05372738838195801,0.29099857807159424,0.3962726294994354],[0.2068709284067154,0.006370965391397476,0.3372364044189453,0.0244777649641037],[0.23342448472976685,-0.01042163372039795,0.3790746331214905,0.2895890474319458],[0.22075217962265015,0.22743815183639526,0.8087112307548523,0.824122965335846],[0.5976130962371826,0.05392265319824219,0.9607176780700684,0.9731613397598267],[0.11443797498941422,0.7428056597709656,0.24389559030532837,0.9905272126197815],[0.22740966081619263,0.4848347008228302,0.4475909471511841,0.5901657938957214],[0.21618488430976868,0.5098341703414917,0.44631537795066833,0.6151652336120605],[0.8084112405776978,0.0015302002429962158,0.9273114204406738,0.9038747549057007],[-0.003454655408859253,0.01914122700691223,0.29720693826675415,0.9142001867294312],[0.23717385530471802,0.4077231287956238,0.6862777471542358,1.043309211730957],[0.40243661403656006,0.4213453233242035,0.46671468019485474,0.49919119477272034],[0.038508474826812744,0.13357895612716675,0.5688574910163879,0.814859926700592],[0.2079186886548996,0.4072537422180176,0.25239354372024536,0.45305848121643066],[0.18039143085479736,0.7189949750900269,0.346691370010376,1.0018376111984253],[0.17740964889526367,0.8574067950248718,0.3975909352302551,0.9967599511146545],[0.30818599462509155,0.000822313129901886,0.6126490831375122,0.1720949113368988],[0.06299805641174316,0.032074540853500366,0.2399788498878479,0.8108158111572266],[0.13213692605495453,0.5332436561584473,0.41890597343444824,0.9667563438415527],[0.3001849353313446,0.8360094428062439,0.5748157501220703,1.0098237991333008],[0.46447721123695374,0.7584561109542847,0.6980230808258057,0.8998774290084839],[0.22825893759727478,0.5397037863731384,0.7288434505462646,0.8317379355430603],[0.21569259464740753,0.8997492790222168,0.2564050257205963,0.9679288864135742],[0.25803816318511963,0.4330609440803528,0.3111131191253662,0.4992610216140747],[0.22732794284820557,0.3999912738800049,0.4643397331237793,0.5687592029571533],[0.23802617192268372,0.5667713284492493,0.42239078879356384,0.8623947501182556],[0.34699746966362,0.2612840533256531,0.488418847322464,0.5053830146789551],[0.5587438941001892,0.22770456969738007,0.6354513764381409,0.34881260991096497],[0.5473014116287231,0.6006264090538025,0.6144839525222778,0.6729443669319153],[0.5691072940826416,0.5631829500198364,0.6691073179244995,0.6897634267807007],[0.197118878364563,0.5749090313911438,0.5393403768539429,1.0084216594696045],[0.2407025694847107,0.4220343232154846,0.41971439123153687,0.648799479007721],[0.43011459708213806,0.42714160680770874,0.49631467461586,0.4729463458061218],[0.5331307053565979,0.7939615845680237,0.61097651720047,0.8855019211769104],[0.2184237539768219,0.7220790386199951,0.6754136085510254,1.0115272998809814],[0.23764437437057495,0.8479610085487366,0.8712865710258484,0.9984578490257263],[0.12035168707370758,0.8447889089584351,0.35048216581344604,0.9947942495346069],[0.21286840736865997,0.9534194469451904,0.26364877820014954,0.9906877279281616],[0.23123027384281158,0.04802142083644867,0.31140387058258057,0.12702254951000214],[0.5154188871383667,0.7573367357254028,0.6169029474258423,0.8059217929840088],[0.697390079498291,0.05803816393017769,0.8673416376113892,0.11111311614513397],[0.22868293523788452,0.3173567056655884,0.32868295907974243,0.45768725872039795],[0.24360230565071106,0.01714465767145157,0.5480654239654541,0.2307719588279724],[0.2897666394710541,0.5481002926826477,0.4560665786266327,0.839400589466095],[0.37094348669052124,0.7636645436286926,0.6415581107139587,0.9050858616828918],[0.46699267625808716,0.5482616424560547,0.6871740221977234,0.7246556282043457],[0.27251920104026794,0.03307163715362549,0.827480673789978,0.32943978905677795],[0.03661632537841797,0.2884189486503601,0.7459261417388916,1.0189385414123535],[0.05953405797481537,0.8303914070129395,0.2862992286682129,0.9966913461685181],[0.17441785335540771,0.2090485841035843,0.27974894642829895,0.5367846488952637],[0.18664157390594482,0.5720193386077881,0.35294151306152344,0.919648289680481],[0.19373293220996857,-0.003133825957775116,0.531268835067749,0.1656341254711151],[0.27887362241744995,0.9335945248603821,0.658628523349762,0.9955716729164124],[0.32571566104888916,0.0015931539237499237,0.6534517407417297,0.07340744137763977],[0.4985097050666809,0.058443158864974976,0.6723241209983826,0.32905781269073486],[0.5864514112472534,0.15539874136447906,0.6753339767456055,0.29165494441986084],[0.5612818598747253,0.6305587291717529,0.6754595637321472,0.719441294670105],[0.513027012348175,0.5583153367042542,0.7057222723960876,0.8958513140678406],[0.004697104915976524,-0.005696093663573265,0.040883369743824005,0.03212530165910721],[0.20103901624679565,0.33512550592422485,0.35104435682296753,0.5940406918525696],[0.20186400413513184,0.4540380537509918,0.37313663959503174,0.716795802116394],[0.22820225358009338,0.3030018210411072,0.40721407532691956,0.5136640667915344],[0.43144962191581726,0.2230268269777298,0.6185503005981445,0.4936414361000061],[0.48286062479019165,0.7618263959884644,0.6039686799049377,0.8043785095214844],[0.5385960936546326,0.6290205121040344,0.6187697052955627,0.7091941237449646],[0.4680514335632324,0.7121118903160095,0.7194494009017944,0.871222198009491],[0.6098172664642334,-0.0005137994885444641,0.7401827573776245,0.04462110251188278]]],"1":[[16.0,16.0,64.0,74.0,62.0,16.0,74.0,16.0,43.0,73.0,64.0,16.0,16.0,62.0,64.0,83.0,16.0,16.0,84.0,16.0,74.0,74.0,62.0,16.0,74.0,74.0,64.0,83.0,74.0,74.0,74.0,84.0,64.0,74.0,83.0,83.0,16.0,64.0,0.0,74.0,74.0,64.0,62.0,16.0,74.0,62.0,74.0,31.0,74.0,83.0,62.0,16.0,0.0,58.0,16.0,74.0,74.0,16.0,16.0,16.0,73.0,76.0,76.0,16.0,74.0,74.0,84.0,16.0,64.0,0.0,74.0,83.0,84.0,74.0,16.0,83.0,16.0,83.0,39.0,16.0,62.0,0.0,16.0,16.0,83.0,0.0,83.0,16.0,74.0,84.0,30.0,0.0,16.0,74.0,16.0,73.0,76.0,84.0,31.0,83.0]],"2":[[0.76953125,0.76953125,0.5234375,0.39453125,0.30078125,0.24609375,0.23828125,0.19140625,0.18359375,0.17578125,0.1640625,0.15234375,0.14453125,0.14453125,0.140625,0.1328125,0.1328125,0.125,0.11328125,0.11328125,0.109375,0.109375,0.109375,0.10546875,0.10546875,0.10546875,0.10546875,0.1015625,0.1015625,0.09765625,0.09765625,0.09765625,0.09765625,0.08984375,0.08984375,0.08984375,0.0859375,0.0859375,0.08203125,0.08203125,0.08203125,0.08203125,0.08203125,0.08203125,0.078125,0.078125,0.07421875,0.07421875,0.07421875,0.07421875,0.07421875,0.07421875,0.0703125,0.0703125,0.0703125,0.06640625,0.06640625,0.06640625,0.06640625,0.06640625,0.06640625,0.06640625,0.06640625,0.06640625,0.0625,0.0625,0.0625,0.0625,0.0625,0.05859375,0.05859375,0.05859375,0.05859375,0.05859375,0.0546875,0.0546875,0.0546875,0.0546875,0.0546875,0.0546875,0.0546875,0.05078125,0.05078125,0.05078125,0.05078125,0.05078125,0.05078125,0.05078125,0.05078125,0.05078125,0.05078125,0.046875,0.046875,0.046875,0.046875,0.046875,0.046875,0.046875,0.046875,0.046875]],"3":[100.0]}},"coco_val2017_000000252219_street":{"orig":[640,428],"scale":0.5,"pad":[0,53],"outputs":{"0":[[[0.4162486791610718,0.024550773203372955,0.7795857191085815,0.1983652412891388],[0.4350929260253906,0.5026833415031433,0.7440744638442993,0.6160659193992615],[0.4274504780769348,0.8110100030899048,0.760050356388092,0.9848244190216064],[0.5311018228530884,0.3237488269805908,0.7480632066726685,0.5054173469543457],[0.6483170986175537,0.6389470100402832,0.7928438186645508,0.7346237897872925],[0.5338170528411865,0.3173767626285553,0.6368073225021362,0.3987402021884918],[0.4912804067134857,0.9553788900375366,0.6911295056343079,1.0005137920379639],[0.30877313017845154,0.8685252070426941,0.41176334023475647,1.0109384059906006],[0.28655675053596497,0.8688049912452698,0.5342783331871033,1.0041122436523438],[0.5341765880584717,0.30358779430389404,0.6143501996994019,0.3802952766418457],[0.24573861062526703,0.535965085029602,0.3307785391807556,0.599303126335144],[0.4633682072162628,0.9736822247505188,0.6661832332611084,0.9969421029090881],[0.5260692834854126,0.38162389397621155,0.6489746570587158,0.48016151785850525],[0.7162842154502869,0.5424903035163879,0.7484474778175354,0.6030901074409485],[0.3242916762828827,0.9250457882881165,0.40213754773139954,0.9984369874000549],[0.5646662712097168,0.32681071758270264,0.6617629528045654,0.4069843292236328],[0.43745797872543335,0.8556292653083801,0.506649374961853,0.8928975462913513],[0.5453707575798035,0.9623616933822632,0.6898974776268005,0.9964772462844849],[0.3656116724014282,0.9378393292427063,0.6885550022125244,0.9913268685340881],[0.6316292881965637,0.35618144273757935,0.7316293120384216,0.47908681631088257],[0.299285888671875,0.9094834923744202,0.39638257026672363,1.0051602125167847],[0.5499159693717957,0.13722729682922363,0.5882986187934875,0.20540684461593628],[0.5260947346687317,0.3105463683605194,0.6739063858985901,0.4977883994579315],[0.5227016806602478,0.9099226593971252,0.6287714838981628,0.9592286944389343],[0.5940675735473633,0.3829779624938965,0.6574056148529053,0.46554887294769287],[0.4623275697231293,0.8369593620300293,0.4979846775531769,0.8631285429000854],[0.5491682291030884,0.47726282477378845,0.62587571144104,0.5139861106872559],[0.2056179791688919,0.4550957679748535,0.2488015741109848,0.5022701621055603],[0.21111512184143066,0.5228007435798645,0.24625077843666077,0.552243173122406],[0.5356869101524353,0.49689340591430664,0.6069472432136536,0.5310089588165283],[0.5498236417770386,0.4632295072078705,0.7031227350234985,0.511814534664154],[0.46009230613708496,0.7904802560806274,0.7690738439559937,0.9199378490447998],[0.5481249690055847,0.4862275719642639,0.7283921837806702,0.5313624739646912],[0.5189509391784668,0.02130730450153351,0.7701113224029541,0.44223350286483765],[0.6050348877906799,0.9128004312515259,0.6596968770027161,0.966663122177124],[0.4300398528575897,0.8400573134422302,0.4641554057598114,0.8629769682884216],[0.3168220520019531,0.9083377122879028,0.6748456954956055,0.9979112148284912],[0.5227682590484619,0.8477926850318909,0.6257585287094116,0.913992702960968],[0.7170714735984802,0.3222942352294922,0.7432406544685364,0.360115647315979],[0.49940651655197144,0.5581434369087219,0.5609056353569031,0.6006955504417419],[0.48628464341163635,0.40381699800491333,0.730383574962616,0.5295159816741943],[0.5414602756500244,0.5834677815437317,0.6011738777160645,0.6092543005943298],[0.7611274719238281,0.0054911356419324875,0.8006578683853149,0.03861616551876068],[0.5396264791488647,0.10698680579662323,0.760373592376709,0.5930132269859314],[0.17262639105319977,0.8333241939544678,0.4315415620803833,1.0020921230316162],[0.48002371191978455,0.8558622598648071,0.5729225873947144,0.9073963165283203],[0.5417873859405518,0.05324568599462509,0.6141053438186646,0.11295924335718155],[0.5150630474090576,0.8205909729003906,0.6275711059570312,0.8500334024429321],[0.5987178683280945,0.4551270604133606,0.7453894019126892,0.5037120580673218],[0.23898977041244507,0.5919873118400574,0.33752739429473877,0.6653785109519958],[0.2905657887458801,0.5573469996452332,0.3343902826309204,0.6073847413063049],[0.41652873158454895,0.8505421876907349,0.4688275158405304,0.8861992359161377],[0.4871012568473816,0.10838092118501663,0.5746840834617615,0.1622435450553894],[0.5822117328643799,0.850226879119873,0.6545296907424927,0.914504885673523],[0.6951959729194641,0.34702178835868835,0.737125813961029,0.4853001534938812],[0.5352706909179688,0.10564260184764862,0.6014707088470459,0.17087455093860626],[0.6321781277656555,0.9626111388206482,0.6927779316902161,0.9962278008460999],[0.7240806818008423,0.4945259094238281,0.7494900226593018,0.5657862424850464],[0.7278625965118408,0.6477597951889038,0.7912006378173828,0.699293851852417],[0.44048047065734863,0.9741995334625244,0.5787588357925415,0.9905321598052979],[0.5262652635574341,0.526103138923645,0.5561445951461792,0.550413966178894],[0.5032247304916382,0.8301358819007874,0.5570874214172363,0.8566934466362],[0.5438975691795349,0.31178784370422363,0.688424289226532,0.37798792123794556],[0.741370677947998,0.0036882534623146057,0.7968438863754272,0.1787216067314148],[0.26776328682899475,0.8388197422027588,0.4030705988407135,1.0153470039367676],[0.4859636723995209,0.8949870467185974,0.5802412629127502,0.956486165523529],[0.6024523973464966,0.49162277579307556,0.7387086153030396,0.5348063707351685],[0.5774096250534058,0.6351754665374756,0.797590970993042,0.762739896774292],[0.2301507294178009,0.5263333320617676,0.3286883533000946,0.5708081722259521],[0.2292805016040802,0.6279017925262451,0.3369244635105133,0.6867420673370361],[0.5606799125671387,0.027291707694530487,0.5996322631835938,0.05806448310613632],[0.5880016088485718,0.5785675644874573,0.6782033443450928,0.6112081408500671],[0.718255341053009,0.01484508067369461,0.7508959174156189,0.13957443833351135],[0.7315396666526794,0.8035227060317993,0.7597091794013977,0.837638258934021],[0.2944031357765198,0.809653639793396,0.453513503074646,0.9861807823181152],[0.6220482587814331,0.33179566264152527,0.7205859422683716,0.4476678669452667],[0.42942866683006287,0.7300208806991577,0.7904558181762695,1.0069555044174194],[0.44813793897628784,0.14109531044960022,0.46520859003067017,0.15604627132415771],[0.5543093681335449,0.5982905626296997,0.6266273260116577,0.6222459077835083],[0.4242232143878937,0.5523462295532227,0.7424439191818237,0.6393190622329712],[0.5669523477554321,0.5963963866233826,0.6992526054382324,0.6285596489906311],[0.21041908860206604,0.4460771381855011,0.34374770522117615,0.683089017868042],[0.5402801036834717,0.27855896949768066,0.7388873100280762,0.4006084203720093],[0.5964339971542358,0.8200300335884094,0.6535660028457642,0.8447017073631287],[0.2541125416755676,0.4373975098133087,0.3120924234390259,0.5199684500694275],[0.5770773887634277,0.04363440349698067,0.6699762344360352,0.12993639707565308],[0.7549723386764526,0.26059067249298096,0.7994471788406372,0.3144533038139343],[0.39701393246650696,0.9599695801734924,0.46035197377204895,0.9885571599006653],[0.5154039859771729,0.14906154572963715,0.5684789419174194,0.20535801351070404],[0.5914602875709534,0.08590969443321228,0.6511738896369934,0.16261713206768036],[0.6257311701774597,0.5529274940490723,0.7242688536643982,0.602965235710144],[0.7689149975776672,0.748873770236969,0.7928703427314758,0.7981798052787781],[0.212331622838974,0.4396534562110901,0.24356108903884888,0.46190768480300903],[0.5010358691215515,0.03401685133576393,0.5607494711875916,0.046919818967580795],[0.5261155366897583,0.33853837847709656,0.5592405796051025,0.390837162733078],[0.43047335743904114,0.00413740798830986,0.7486940622329712,0.07919672131538391],[0.5710752606391907,0.0036823004484176636,0.6921833157539368,0.06252261996269226],[0.6423197388648987,0.6038827300071716,0.8014300465583801,0.7752847075462341],[0.5016130208969116,0.3145033121109009,0.7622942924499512,0.5751845240592957],[0.497750461101532,0.720516562461853,0.561088502407074,0.7368491888046265]]],"1":[[0.0,0.0,0.0,61.0,32.0,26.0,0.0,27.0,27.0,26.0,0.0,0.0,26.0,40.0,27.0,26.0,0.0,30.0,0.0,61.0,27.0,30.0,26.0,30.0,26.0,0.0,26.0,0.0,0.0,30.0,61.0,0.0,61.0,14.0,30.0,0.0,0.0,30.0,36.0,30.0,0.0,30.0,40.0,14.0,27.0,26.0,30.0,30.0,61.0,71.0,0.0,0.0,26.0,30.0,61.0,30.0,32.0,40.0,40.0,0.0,30.0,30.0,26.0,40.0,27.0,30.0,61.0,32.0,0.0,61.0,0.0,61.0,40.0,36.0,27.0,61.0,0.0,76.0,30.0,0.0,61.0,71.0,0.0,30.0,83.0,30.0,36.0,27.0,42.0,30.0,40.0,40.0,0.0,0.0,26.0,0.0,14.0,32.0,14.0,0.0]],"2":[[0.76953125,0.76953125,0.76953125,0.55859375,0.46484375,0.453125,0.453125,0.39453125,0.375,0.36328125,0.3515625,0.34375,0.33203125,0.33203125,0.3203125,0.30078125,0.28125,0.28125,0.2734375,0.2734375,0.24609375,0.24609375,0.24609375,0.24609375,0.24609375,0.23828125,0.23828125,0.23046875,0.23046875,0.22265625,0.22265625,0.22265625,0.21484375,0.21484375,0.20703125,0.19921875,0.19921875,0.19921875,0.19921875,0.19140625,0.19140625,0.19140625,0.19140625,0.19140625,0.18359375,0.18359375,0.18359375,0.18359375,0.18359375,0.17578125,0.17578125,0.17578125,0.17578125,0.17578125,0.16796875,0.1640625,0.1640625,0.1640625,0.1640625,0.15625,0.15625,0.15625,0.15625,0.15625,0.15234375,0.15234375,0.15234375,0.15234375,0.14453125,0.14453125,0.14453125,0.14453125,0.14453125,0.14453125,0.140625,0.140625,0.140625,0.1328125,0.1328125,0.1328125,0.1328125,0.12890625,0.12890625,0.12890625,0.125,0.125,0.125,0.1171875,0.1171875,0.1171875,0.1171875,0.1171875,0.11328125,0.11328125,0.11328125,0.11328125,0.11328125,0.11328125,0.11328125,0.109375]],"3":[100.0]}}}}