repzo 1.0.292 → 1.0.293

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 (34) hide show
  1. package/changelog.md +28 -0
  2. package/lib/index.d.ts +244 -2
  3. package/lib/index.js +541 -0
  4. package/lib/types/index.d.ts +4530 -1
  5. package/package.json +1 -1
  6. package/src/index.ts +1270 -0
  7. package/src/oas/activity-ai-object-detection-session-frame.yaml +600 -0
  8. package/src/oas/ai-object-detection-assigned-missions.yaml +283 -0
  9. package/src/oas/ai-object-detection-assignment-rule.yaml +357 -0
  10. package/src/oas/ai-object-detection-category.yaml +338 -0
  11. package/src/oas/ai-object-detection-dataset.yaml +342 -0
  12. package/src/oas/ai-object-detection-detection-settings.yaml +410 -0
  13. package/src/oas/ai-object-detection-inference.yaml +818 -0
  14. package/src/oas/ai-object-detection-label-group.yaml +265 -0
  15. package/src/oas/ai-object-detection-label-report.yaml +366 -0
  16. package/src/oas/ai-object-detection-label.yaml +395 -0
  17. package/src/oas/ai-object-detection-metric-result.yaml +734 -0
  18. package/src/oas/ai-object-detection-metric.yaml +559 -0
  19. package/src/oas/ai-object-detection-mission-results.yaml +370 -0
  20. package/src/oas/ai-object-detection-mission-set.yaml +250 -0
  21. package/src/oas/ai-object-detection-mission.yaml +349 -0
  22. package/src/oas/ai-object-detection-model-version-epoch.yaml +214 -0
  23. package/src/oas/ai-object-detection-model-version-train-agent.yaml +88 -0
  24. package/src/oas/ai-object-detection-model-version.yaml +567 -0
  25. package/src/oas/ai-object-detection-model.yaml +369 -0
  26. package/src/oas/ai-object-detection-segment.yaml +301 -0
  27. package/src/oas/ai-object-detection-session-analysis.yaml +1306 -0
  28. package/src/oas/ai-object-detection-session-election.yaml +193 -0
  29. package/src/oas/ai-object-detection-session-insight.yaml +418 -0
  30. package/src/oas/ai-object-detection-session.yaml +840 -0
  31. package/src/oas/ai-object-detection-settings.yaml +249 -0
  32. package/src/oas/ai-object-detection-task.yaml +1007 -0
  33. package/src/oas/object-detection-analytics-report.yaml +674 -0
  34. package/src/types/index.ts +5156 -1
@@ -0,0 +1,265 @@
1
+ openapi: 3.0.3
2
+ info:
3
+ title: Repzo API - AI Object Detection Label Group
4
+ version: 1.0.0
5
+ description: |
6
+ A **label group** is a family of sibling object-detection labels that the
7
+ detector tends to confuse — the size or flavour variants of one product
8
+ line (e.g. the 400g / 800g jars of the same mayonnaise). Groups exist for
9
+ the **dims reclassifier**: after a shelf-scan session is analysed, a
10
+ detection whose measured physical size fits a sibling better than its
11
+ predicted label may be re-labelled — but only ever BETWEEN labels that
12
+ share a group, never across groups.
13
+
14
+ A group is just a named bucket; labels join it through their own
15
+ `label_group` field on `ai-object-detection-label` (one group per label).
16
+ Use `GET /ai-object-detection-label?label_group=<id>` to list a group's
17
+ members.
18
+
19
+ **Who calls it.** Back-office admins from the label screen.
20
+
21
+ **Multi-tenancy & lifecycle.** Scoped by `company_namespace[]` (injected
22
+ from the caller's session). Soft-delete via `disabled: true`; `name` is
23
+ unique per namespace among non-deleted groups. Deleting a group that still
24
+ has live member labels is rejected with 400 (the error payload carries
25
+ `assigned_labels` and `requires_force: true`) unless `?force=true`, which
26
+ first unsets `label_group` on those labels and then soft-deletes the
27
+ group — the dashboard asks for explicit confirmation before forcing.
28
+ `PATCH` is not allowed (400).
29
+
30
+ **Key relationships.** Referenced by `ai-object-detection-label`
31
+ (`label_group`).
32
+ servers:
33
+ - url: https://sv.api.repzo.me
34
+ security:
35
+ - ApiKeyAuth: []
36
+ - JwtAuth: []
37
+ paths:
38
+ /ai-object-detection-label-group:
39
+ get:
40
+ summary: Find label groups
41
+ operationId: findAiObjectDetectionLabelGroups
42
+ parameters:
43
+ - in: query
44
+ name: _id
45
+ description: Filter by group `_id`. Pass once or as `?_id[]=...` for multiple.
46
+ schema:
47
+ oneOf:
48
+ - type: string
49
+ - type: array
50
+ items: { type: string }
51
+ - in: query
52
+ name: name
53
+ description: Exact-match on `name` (single value or list).
54
+ schema:
55
+ oneOf:
56
+ - type: string
57
+ - type: array
58
+ items: { type: string }
59
+ - in: query
60
+ name: search
61
+ description: Case-insensitive substring / regex match on `name`.
62
+ schema: { type: string }
63
+ example: mayonnaise
64
+ - in: query
65
+ name: disabled
66
+ description: "`true` returns only soft-deleted groups, `false` only live ones. Omitted returns both."
67
+ schema: { type: boolean }
68
+ - in: query
69
+ name: from_updatedAt
70
+ description: Return groups with `updatedAt` on/after this Unix timestamp (ms); snapped to start of day unless `exact_time=true`.
71
+ schema: { type: number }
72
+ - in: query
73
+ name: to_updatedAt
74
+ description: Return groups with `updatedAt` on/before this Unix timestamp (ms).
75
+ schema: { type: number }
76
+ - in: query
77
+ name: from_createdAt
78
+ description: Return groups with `createdAt` on/after this Unix timestamp (ms).
79
+ schema: { type: number }
80
+ - in: query
81
+ name: to_createdAt
82
+ description: Return groups with `createdAt` on/before this Unix timestamp (ms).
83
+ schema: { type: number }
84
+ - in: query
85
+ name: per_page
86
+ description: Page size (capped by the server's pagination max).
87
+ schema: { type: integer, minimum: 1 }
88
+ example: 20
89
+ - in: query
90
+ name: page
91
+ description: 1-based page number.
92
+ schema: { type: integer, minimum: 1 }
93
+ example: 1
94
+ - in: query
95
+ name: sort
96
+ description: Field to sort by. Defaults to `_id`.
97
+ schema: { type: string, default: _id }
98
+ - in: query
99
+ name: sortPageOrder
100
+ description: Sort direction. Defaults to descending.
101
+ schema: { type: string, enum: [asc, dsc], default: dsc }
102
+ responses:
103
+ "200":
104
+ description: Paginated list of label groups.
105
+ content:
106
+ application/json:
107
+ schema:
108
+ $ref: "#/components/schemas/OdLabelGroupFindResult"
109
+ post:
110
+ summary: Create a label group
111
+ operationId: createAiObjectDetectionLabelGroup
112
+ requestBody:
113
+ required: true
114
+ content:
115
+ application/json:
116
+ schema:
117
+ $ref: "#/components/schemas/OdLabelGroupCreateBody"
118
+ responses:
119
+ "201":
120
+ description: The created group.
121
+ content:
122
+ application/json:
123
+ schema:
124
+ $ref: "#/components/schemas/OdLabelGroupSchema"
125
+ "400":
126
+ description: Missing name, or the name already exists among live groups in the namespace.
127
+ /ai-object-detection-label-group/{id}:
128
+ get:
129
+ summary: Get a label group by id
130
+ operationId: getAiObjectDetectionLabelGroup
131
+ parameters:
132
+ - in: path
133
+ name: id
134
+ required: true
135
+ schema: { type: string }
136
+ responses:
137
+ "200":
138
+ description: The group document.
139
+ content:
140
+ application/json:
141
+ schema:
142
+ $ref: "#/components/schemas/OdLabelGroupSchema"
143
+ "400":
144
+ description: No group with that id in the caller's namespace.
145
+ put:
146
+ summary: Update a label group
147
+ description: "Standard put (mongoose `updateOne` with validators). Set `disabled: true` to soft-delete without the member-label guard."
148
+ operationId: updateAiObjectDetectionLabelGroup
149
+ parameters:
150
+ - in: path
151
+ name: id
152
+ required: true
153
+ schema: { type: string }
154
+ requestBody:
155
+ required: true
156
+ content:
157
+ application/json:
158
+ schema:
159
+ $ref: "#/components/schemas/OdLabelGroupUpdateBody"
160
+ responses:
161
+ "200":
162
+ description: The group after the update.
163
+ content:
164
+ application/json:
165
+ schema:
166
+ $ref: "#/components/schemas/OdLabelGroupSchema"
167
+ "404":
168
+ description: No group with that id in the caller's namespace.
169
+ delete:
170
+ summary: Soft-delete a label group (guarded)
171
+ description: |
172
+ Sets `disabled: true`. Rejected with 400 while live labels still point
173
+ at the group (`{ assigned_labels: <n>, requires_force: true }` in the
174
+ error data) unless `?force=true`, which unsets `label_group` on those
175
+ labels first.
176
+ operationId: removeAiObjectDetectionLabelGroup
177
+ parameters:
178
+ - in: path
179
+ name: id
180
+ required: true
181
+ schema: { type: string }
182
+ - in: query
183
+ name: force
184
+ description: When `true`, detach the group from its member labels and then soft-delete it.
185
+ schema: { type: boolean, default: false }
186
+ responses:
187
+ "200":
188
+ description: The group after soft-deletion.
189
+ content:
190
+ application/json:
191
+ schema:
192
+ $ref: "#/components/schemas/OdLabelGroupSchema"
193
+ "400":
194
+ description: The group still has member labels (and `force` was not set), or was not found.
195
+ components:
196
+ securitySchemes:
197
+ ApiKeyAuth:
198
+ type: apiKey
199
+ in: header
200
+ name: api-key
201
+ description: |
202
+ Server-issued API key. Also accepted via the `x-api-key` header or the
203
+ `?apiKey=` query parameter as fallbacks.
204
+ JwtAuth:
205
+ type: apiKey
206
+ in: header
207
+ name: Authorization
208
+ description: |
209
+ Raw JWT in the `Authorization` header — **no `Bearer ` prefix**.
210
+ Obtained from `POST /authenticate` (admin / rep / client login).
211
+ schemas:
212
+ OdLabelGroupSchema:
213
+ type: object
214
+ properties:
215
+ _id: { type: string }
216
+ name:
217
+ type: string
218
+ description: Unique per namespace among non-deleted groups.
219
+ disabled:
220
+ type: boolean
221
+ description: Soft-delete flag.
222
+ company_namespace:
223
+ type: array
224
+ items: { type: string }
225
+ description: Tenant key (server-injected).
226
+ createdAt: { type: string, format: date-time }
227
+ updatedAt: { type: string, format: date-time }
228
+ OdLabelGroupCreateBody:
229
+ type: object
230
+ description: "Body for creating a group. `company_namespace` is optional for SDK callers and otherwise injected from the caller's session."
231
+ required: [name]
232
+ properties:
233
+ name:
234
+ type: string
235
+ description: Unique per namespace among non-deleted groups.
236
+ company_namespace:
237
+ type: array
238
+ items: { type: string }
239
+ description: Optional tenant namespace override for SDK callers.
240
+ OdLabelGroupUpdateBody:
241
+ type: object
242
+ description: "Body for updating a group. `company_namespace` is derived from the caller's session — do not send it. Set `disabled: true` to soft-delete."
243
+ properties:
244
+ name: { type: string }
245
+ disabled:
246
+ type: boolean
247
+ description: Soft-delete flag.
248
+ OdLabelGroupFindResult:
249
+ type: object
250
+ description: Standard paginated result envelope.
251
+ properties:
252
+ data:
253
+ type: array
254
+ items:
255
+ $ref: "#/components/schemas/OdLabelGroupSchema"
256
+ total_result: { type: number }
257
+ current_count: { type: number }
258
+ total_pages: { type: number }
259
+ current_page: { type: number }
260
+ per_page: { type: number }
261
+ first_page_url: { type: string }
262
+ last_page_url: { type: string }
263
+ next_page_url: { type: string, nullable: true }
264
+ prev_page_url: { type: string, nullable: true }
265
+ path: { type: string }
@@ -0,0 +1,366 @@
1
+ openapi: 3.0.3
2
+ info:
3
+ title: Repzo API - AI Object Detection Label Report
4
+ version: 1.0.0
5
+ description: |
6
+ Read-only, computed **annotation-health report for one object-detection
7
+ label**. It has no collection of its own — every call aggregates live over
8
+ `ai-object-detection-task` documents in the caller's namespace — and only
9
+ `GET /ai-object-detection-label-report` (Feathers `find`) is served; `get`,
10
+ `create`, `update`, `patch` and `delete` answer 405.
11
+
12
+ **Default mode (`?dataset=<id>&label=<id>`).** Returns a `summary` — how
13
+ the label's annotations in that dataset spread across subsets
14
+ (train / val / test / auto / ignore), annotation-group states (manual /
15
+ auto / auto_edited), usable / confirmed counts and training / validation
16
+ eligibility — plus `items[]`, one row per annotation of the label with the
17
+ frame image URL and dimensions, the YOLO box, confidence, group state, the
18
+ effective subset and `included_in_training` / `included_in_val` flags.
19
+ The summary always covers the whole dataset; the item filters (`subset`,
20
+ `annotation_state`, `label_state`, `usable`, `confirmed`, `included`) and
21
+ the `per_page` / `page` pagination apply to `items[]` only, and
22
+ `total_result` / `total_pages` count the filtered stream. Eligibility
23
+ mirrors the training-set builder: a group counts when
24
+ `usable && confirmed && annotation_state ∈ {manual, auto, auto_edited}`
25
+ (`mode=all`, the model default) or `∈ {manual, auto_edited}`
26
+ (`mode=smart`); the task's subset (≠ ignore) then routes it to train / val.
27
+
28
+ **Dims mode (`?mode=dims&label=<id>`).** The label screen's *smart dims
29
+ analyzer*: collects the label's most recent placed annotations that carry
30
+ a measured `world_size`, rejects low-depth-confidence rows and MAD-based
31
+ outliers (modified z-score above `z_threshold` on either axis), and
32
+ proposes new `physical_size` dims (cm) as the mean of the kept samples —
33
+ only when at least `min_samples` survive. The response is a single object
34
+ (label, effective params, robust stats, every sample with its verdict);
35
+ it is what an admin reviews before writing `physical_size` back onto the
36
+ label with `PUT /ai-object-detection-label/{id}`.
37
+
38
+ **Who calls it.** Back-office admins (dataset / label screens). Scoped to
39
+ the caller's `company_namespace` by the global auth hook; disabled tasks
40
+ and tasks without a real image are excluded. Relates to
41
+ `ai-object-detection-label`, `ai-object-detection-dataset` and
42
+ `ai-object-detection-task`.
43
+ servers:
44
+ - url: https://sv.api.repzo.me
45
+ security:
46
+ - ApiKeyAuth: []
47
+ - JwtAuth: []
48
+ paths:
49
+ /ai-object-detection-label-report:
50
+ get:
51
+ summary: Label annotation-health report, or smart dims analysis
52
+ description: |
53
+ Without `mode=dims`: `dataset` and `label` are required and the
54
+ response is `LabelReportResult`. With `mode=dims`: only `label` is
55
+ required and the response is `LabelDimsResult`.
56
+ operationId: findAiObjectDetectionLabelReport
57
+ parameters:
58
+ - in: query
59
+ name: mode
60
+ description: |
61
+ `all` (default) / `smart` select the eligibility predicate of the
62
+ annotation-health report; `dims` switches to the smart dims
63
+ analyzer. Any other value is treated as `all`.
64
+ schema:
65
+ type: string
66
+ enum: [all, smart, dims]
67
+ default: all
68
+ - in: query
69
+ name: label
70
+ required: true
71
+ description: "`ai-object-detection-label` id (24-hex ObjectId). Required in both modes."
72
+ schema: { type: string }
73
+ - in: query
74
+ name: dataset
75
+ description: "`ai-object-detection-dataset` id (24-hex ObjectId). Required unless `mode=dims`."
76
+ schema: { type: string }
77
+ - in: query
78
+ name: subset
79
+ description: Item filter (default mode) — effective subset. Pass once or as `?subset[]=`.
80
+ schema:
81
+ oneOf:
82
+ - type: string
83
+ enum: [train, val, test, auto, ignore]
84
+ - type: array
85
+ items:
86
+ type: string
87
+ enum: [train, val, test, auto, ignore]
88
+ - in: query
89
+ name: annotation_state
90
+ description: Item filter (default mode) — `group.annotation_state`.
91
+ schema:
92
+ oneOf:
93
+ - type: string
94
+ enum: [auto, manual, auto_edited]
95
+ - type: array
96
+ items:
97
+ type: string
98
+ enum: [auto, manual, auto_edited]
99
+ - in: query
100
+ name: label_state
101
+ description: Item filter (default mode) — the annotation's `label_state`.
102
+ schema:
103
+ oneOf:
104
+ - type: string
105
+ enum: [auto, manual]
106
+ - type: array
107
+ items:
108
+ type: string
109
+ enum: [auto, manual]
110
+ - in: query
111
+ name: usable
112
+ description: Item filter (default mode) — `group.usable`.
113
+ schema: { type: boolean }
114
+ - in: query
115
+ name: confirmed
116
+ description: Item filter (default mode) — `group.confirmed`.
117
+ schema: { type: boolean }
118
+ - in: query
119
+ name: included
120
+ description: Item filter (default mode) — only rows included in training or validation.
121
+ schema:
122
+ type: string
123
+ enum: [train, val]
124
+ - in: query
125
+ name: per_page
126
+ description: Page size for `items[]` (default mode; capped by the server's pagination max).
127
+ schema: { type: integer, minimum: 1 }
128
+ example: 50
129
+ - in: query
130
+ name: page
131
+ description: 1-based page for `items[]` (default mode).
132
+ schema: { type: integer, minimum: 1 }
133
+ example: 1
134
+ - in: query
135
+ name: sample
136
+ description: Dims mode — most recent measured annotations to consider. Clamped to 1..500.
137
+ schema: { type: integer, minimum: 1, maximum: 500, default: 100 }
138
+ - in: query
139
+ name: z_threshold
140
+ description: Dims mode — modified z-score cut-off on either axis.
141
+ schema: { type: number, default: 3.5 }
142
+ - in: query
143
+ name: min_depth_confidence
144
+ description: Dims mode — minimum `depth_confidence` (0..1) for a sample to qualify.
145
+ schema: { type: number, default: 0.5 }
146
+ - in: query
147
+ name: min_samples
148
+ description: Dims mode — minimum kept samples before `proposed` dims are returned.
149
+ schema: { type: integer, default: 8 }
150
+ responses:
151
+ "200":
152
+ description: "`LabelReportResult` in the default mode, `LabelDimsResult` when `mode=dims`."
153
+ content:
154
+ application/json:
155
+ schema:
156
+ oneOf:
157
+ - $ref: "#/components/schemas/LabelReportResult"
158
+ - $ref: "#/components/schemas/LabelDimsResult"
159
+ "400":
160
+ description: "`dataset` / `label` missing or not a valid ObjectId."
161
+ components:
162
+ securitySchemes:
163
+ ApiKeyAuth:
164
+ type: apiKey
165
+ in: header
166
+ name: api-key
167
+ description: |
168
+ Server-issued API key. Also accepted via the `x-api-key` header or the
169
+ `?apiKey=` query parameter as fallbacks.
170
+ JwtAuth:
171
+ type: apiKey
172
+ in: header
173
+ name: Authorization
174
+ description: |
175
+ Raw JWT in the `Authorization` header — **no `Bearer ` prefix**.
176
+ Obtained from `POST /authenticate` (admin / rep / client login).
177
+ schemas:
178
+ Box:
179
+ type: object
180
+ description: YOLO-style pixel box on the frame image.
181
+ properties:
182
+ x1: { type: number }
183
+ y1: { type: number }
184
+ x2: { type: number }
185
+ y2: { type: number }
186
+ LabelReportItem:
187
+ type: object
188
+ description: One annotation of the requested label inside the requested dataset.
189
+ properties:
190
+ task_id:
191
+ type: string
192
+ description: "`ai-object-detection-task` id."
193
+ image_url:
194
+ type: string
195
+ description: Public URL of the frame image.
196
+ image_w:
197
+ type: number
198
+ description: Image width (task `shape[0]`, falling back to the media document).
199
+ image_h:
200
+ type: number
201
+ description: Image height (task `shape[1]`, falling back to the media document).
202
+ subset:
203
+ type: string
204
+ enum: [train, val, test, auto, ignore]
205
+ description: Subset from THIS dataset's `task_dataset` entry.
206
+ box:
207
+ $ref: "#/components/schemas/Box"
208
+ confidence: { type: number }
209
+ label_state:
210
+ type: string
211
+ enum: [auto, manual]
212
+ group:
213
+ type: object
214
+ properties:
215
+ usable: { type: boolean }
216
+ confirmed: { type: boolean }
217
+ annotation_state:
218
+ type: string
219
+ enum: [auto, manual, auto_edited]
220
+ engine:
221
+ type: string
222
+ enum: [trained, zero_shot]
223
+ model_version:
224
+ type: string
225
+ description: "`ai-object-detection-model-version` id when `engine` is `trained`."
226
+ included_in_training:
227
+ type: boolean
228
+ description: "`usable && confirmed && state ∈ mode set` AND subset ∈ {train, auto}."
229
+ included_in_val:
230
+ type: boolean
231
+ description: "`usable && confirmed && state ∈ mode set` AND subset ∈ {val, auto}."
232
+ _qualifies:
233
+ type: boolean
234
+ description: Internal eligibility flag the pipeline leaves on the row.
235
+ LabelReportSummary:
236
+ type: object
237
+ description: Distribution over ALL of the label's annotations in the dataset (item filters do not apply).
238
+ properties:
239
+ annotations_total: { type: integer }
240
+ tasks_total:
241
+ type: integer
242
+ description: Distinct tasks carrying the label.
243
+ subset_train: { type: integer }
244
+ subset_val: { type: integer }
245
+ subset_test: { type: integer }
246
+ subset_auto: { type: integer }
247
+ subset_ignore: { type: integer }
248
+ state_manual: { type: integer }
249
+ state_auto: { type: integer }
250
+ state_auto_edited: { type: integer }
251
+ usable_count: { type: integer }
252
+ confirmed_count: { type: integer }
253
+ training_eligible: { type: integer }
254
+ val_eligible: { type: integer }
255
+ LabelReportResult:
256
+ type: object
257
+ description: Default-mode response.
258
+ properties:
259
+ summary:
260
+ $ref: "#/components/schemas/LabelReportSummary"
261
+ items:
262
+ type: array
263
+ items:
264
+ $ref: "#/components/schemas/LabelReportItem"
265
+ mode:
266
+ type: string
267
+ enum: [all, smart]
268
+ total_result:
269
+ type: integer
270
+ description: Items matching the item filters (the paginated stream).
271
+ current_page: { type: integer }
272
+ per_page: { type: integer }
273
+ total_pages: { type: integer }
274
+ LabelDimsSample:
275
+ type: object
276
+ description: One measured annotation considered by the dims analyzer.
277
+ properties:
278
+ task:
279
+ type: string
280
+ description: "`ai-object-detection-task` id."
281
+ session:
282
+ type: string
283
+ description: "Parent `ai-object-detection-session` id (session frames only)."
284
+ frame_id: { type: integer }
285
+ createdAt: { type: string, format: date-time }
286
+ w_cm:
287
+ type: number
288
+ description: Measured physical width (cm).
289
+ h_cm:
290
+ type: number
291
+ description: Measured physical height (cm).
292
+ confidence: { type: number }
293
+ depth_confidence:
294
+ type: number
295
+ description: 0..1 depth confidence of the placement.
296
+ label_state:
297
+ type: string
298
+ enum: [auto, manual]
299
+ z_w:
300
+ type: number
301
+ nullable: true
302
+ description: Modified z-score of `w_cm` (2 dp); `null` when it is not finite.
303
+ z_h:
304
+ type: number
305
+ nullable: true
306
+ kept:
307
+ type: boolean
308
+ description: Not an outlier and depth confidence at/above `min_depth_confidence`.
309
+ reject_reason:
310
+ type: string
311
+ nullable: true
312
+ enum: [low_depth_confidence, outlier, null]
313
+ LabelDimsResult:
314
+ type: object
315
+ description: "`mode=dims` response."
316
+ properties:
317
+ label:
318
+ type: object
319
+ nullable: true
320
+ description: The analysed label (projection).
321
+ properties:
322
+ _id: { type: string }
323
+ name: { type: string }
324
+ label_group: { type: string }
325
+ physical_size:
326
+ type: object
327
+ properties:
328
+ w_cm: { type: number }
329
+ h_cm: { type: number }
330
+ params:
331
+ type: object
332
+ description: Effective parameters after defaults / clamping.
333
+ properties:
334
+ sample: { type: integer }
335
+ z_threshold: { type: number }
336
+ min_depth_confidence: { type: number }
337
+ min_samples: { type: integer }
338
+ stats:
339
+ type: object
340
+ properties:
341
+ n:
342
+ type: integer
343
+ description: Samples collected.
344
+ n_qualified:
345
+ type: integer
346
+ description: Samples with depth confidence at/above `min_depth_confidence`.
347
+ n_kept:
348
+ type: integer
349
+ description: Samples kept after outlier rejection.
350
+ median_w_cm: { type: number }
351
+ median_h_cm: { type: number }
352
+ mad_w_cm:
353
+ type: number
354
+ description: Median absolute deviation (2 dp).
355
+ mad_h_cm: { type: number }
356
+ proposed:
357
+ type: object
358
+ nullable: true
359
+ description: Mean of kept samples (1 dp); `null` when fewer than `min_samples` were kept.
360
+ properties:
361
+ w_cm: { type: number }
362
+ h_cm: { type: number }
363
+ samples:
364
+ type: array
365
+ items:
366
+ $ref: "#/components/schemas/LabelDimsSample"