@camstack/addon-post-analysis 1.2.210 → 1.2.211

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.
@@ -34569,6 +34569,37 @@ var TWO_WHEELERS = new Set([
34569
34569
  function intersection(a, b) {
34570
34570
  return Math.max(0, Math.min(a.x + a.w, b.x + b.w) - Math.max(a.x, b.x)) * Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y));
34571
34571
  }
34572
+ /**
34573
+ * The FOLDED subject's box: the union of the rider and the machine.
34574
+ *
34575
+ * The fold's whole claim is that the two halves are ONE subject. Until
34576
+ * 2026-09-08 the box that survived the fold was the MACHINE's alone, so every
34577
+ * consumer that cuts pixels from `TrackedDetectionOut.bbox` — the best-shot
34578
+ * thumbnail, `wideCentralSquareLayout`'s central square, the close-time
34579
+ * keyFrame→thumbnail derive — framed a motorcycle with its rider's head cut
34580
+ * off. Operator, device 617, tracks `7f50622b` and `9cbc4791`, three minutes
34581
+ * apart: "motocicli e veicoli a due ruote devono includere il conducente nel
34582
+ * keyframe" (D407).
34583
+ *
34584
+ * This is the ONE place the folded subject's rectangle is formed, and it is
34585
+ * formed BEFORE the tracker — so no crop path derives a second rectangle
34586
+ * (D52 stays intact: `deriveDetailCropRect` is still the only detail-crop
34587
+ * derivation, and it now receives a subject box that already holds the rider).
34588
+ * Pure geometry: a strict union, never a pad — the fold widens the subject, it
34589
+ * never moves it.
34590
+ */
34591
+ function riderSubjectBox(person, vehicle) {
34592
+ const x = Math.min(person.x, vehicle.x);
34593
+ const y = Math.min(person.y, vehicle.y);
34594
+ const right = Math.max(person.x + person.w, vehicle.x + vehicle.w);
34595
+ const bottom = Math.max(person.y + person.h, vehicle.y + vehicle.h);
34596
+ return {
34597
+ x,
34598
+ y,
34599
+ w: right - x,
34600
+ h: bottom - y
34601
+ };
34602
+ }
34572
34603
  /** True when `vehicle` is a two-wheeler that `person` is riding. */
34573
34604
  function isRiderPair(person, vehicle) {
34574
34605
  if (person.macroClass !== "person") return false;
@@ -36335,17 +36366,64 @@ var FrameProcessor = class {
36335
36366
  const foldByVehicleBbox = /* @__PURE__ */ new Map();
36336
36367
  if (riderPairs.length > 0) {
36337
36368
  const riderIdx = new Set(riderPairs.map((p) => Number(p.personId)));
36369
+ /**
36370
+ * Move every bbox-OBJECT-keyed side table from the machine's box to the
36371
+ * folded subject's box (D407).
36372
+ *
36373
+ * This file carries frame-local facts across the tracker by bbox-object
36374
+ * REFERENCE (`labelsByBbox`, `embeddingByBbox`, `maskByBbox`, …); the
36375
+ * tracker then assigns `track.bbox = det.bbox`, and the emit block below
36376
+ * reads every one of those maps with `td.bbox`. Widening the subject
36377
+ * therefore means the widened box must inherit the machine's entries —
36378
+ * otherwise a folded rider silently loses its vehicle-classifier label
36379
+ * ("Motorcycle"), its mask, its plate read and its source id. One place,
36380
+ * exhaustive, next to the maps it moves.
36381
+ */
36382
+ const adoptSubjectBox = (from, to) => {
36383
+ const labels = labelsByBbox.get(from);
36384
+ if (labels !== void 0) labelsByBbox.set(to, labels);
36385
+ const tier = originalClassTierByBbox.get(from);
36386
+ if (tier !== void 0) originalClassTierByBbox.set(to, tier);
36387
+ const embedding = embeddingByBbox.get(from);
36388
+ if (embedding !== void 0) embeddingByBbox.set(to, embedding);
36389
+ const mask = maskByBbox.get(from);
36390
+ if (mask !== void 0) maskByBbox.set(to, mask);
36391
+ const plate = plateByBbox.get(from);
36392
+ if (plate !== void 0) plateByBbox.set(to, plate);
36393
+ const faceBbox = faceBboxByBbox.get(from);
36394
+ if (faceBbox !== void 0) faceBboxByBbox.set(to, faceBbox);
36395
+ const faceSize = nativeFaceSizeByBbox.get(from);
36396
+ if (faceSize !== void 0) nativeFaceSizeByBbox.set(to, faceSize);
36397
+ const alignedCrop = faceAlignedCropByBbox.get(from);
36398
+ if (alignedCrop !== void 0) faceAlignedCropByBbox.set(to, alignedCrop);
36399
+ const sourceId = sourceIdByBbox.get(from);
36400
+ if (sourceId !== void 0) {
36401
+ sourceIdByBbox.set(to, sourceId);
36402
+ firstLevelBboxById.set(sourceId, to);
36403
+ }
36404
+ };
36405
+ /** Folded vehicles, by their index in `filteredDetections`. */
36406
+ const foldedVehicles = /* @__PURE__ */ new Map();
36338
36407
  for (const p of riderPairs) {
36339
- const vehicle = filteredDetections[Number(p.vehicleId)];
36340
- if (vehicle === void 0) continue;
36341
- foldByVehicleBbox.set(vehicle.bbox, {
36408
+ const vehicleIdx = Number(p.vehicleId);
36409
+ const vehicle = filteredDetections[vehicleIdx];
36410
+ const person = filteredDetections[Number(p.personId)];
36411
+ if (vehicle === void 0 || person === void 0) continue;
36412
+ const subjectBox = riderSubjectBox(person.bbox, vehicle.bbox);
36413
+ adoptSubjectBox(vehicle.bbox, subjectBox);
36414
+ const folded = {
36415
+ ...vehicle,
36416
+ bbox: subjectBox
36417
+ };
36418
+ foldedVehicles.set(vehicleIdx, folded);
36419
+ foldByVehicleBbox.set(subjectBox, {
36342
36420
  overlap: p.overlap,
36343
- personScore: filteredDetections[Number(p.personId)]?.score ?? 0,
36421
+ personScore: person.score,
36344
36422
  vehicleScore: vehicle.score,
36345
36423
  vehicleClass: vehicle.originalClass ?? "two-wheeler"
36346
36424
  });
36347
36425
  }
36348
- filteredDetections = filteredDetections.filter((_, i) => !riderIdx.has(i));
36426
+ filteredDetections = filteredDetections.map((d, i) => foldedVehicles.get(i) ?? d).filter((_, i) => !riderIdx.has(i));
36349
36427
  }
36350
36428
  const gate = this.stationaryGate ? this.stationaryGate.filter({
36351
36429
  detections: filteredDetections,
@@ -49068,10 +49146,9 @@ var FaceRecognizer = class {
49068
49146
  margin: settings.margin,
49069
49147
  minIdentitySamples: settings.minIdentitySamples
49070
49148
  }) : /* @__PURE__ */ new Map();
49071
- if (this.deps.onSemanticMatch !== void 0) for (const c of matchCandidates) {
49149
+ if (this.deps.onSemanticFace !== void 0) for (const c of matchCandidates) {
49072
49150
  const match = matches.get(c.trackId);
49073
- if (match === void 0) continue;
49074
- this.deps.onSemanticMatch({
49151
+ this.deps.onSemanticFace({
49075
49152
  deviceId: input.deviceId,
49076
49153
  trackId: c.trackId,
49077
49154
  timestamp: input.timestamp,
@@ -49079,8 +49156,10 @@ var FaceRecognizer = class {
49079
49156
  frameHeight: input.frameHeight,
49080
49157
  bbox: c.bbox,
49081
49158
  confidence: c.confidence,
49082
- matchScore: match.score,
49083
- identityId: match.identityId,
49159
+ ...match !== void 0 ? {
49160
+ matchScore: match.score,
49161
+ identityId: match.identityId
49162
+ } : {},
49084
49163
  ...input.frameHandle !== void 0 ? { frameHandle: input.frameHandle } : {}
49085
49164
  });
49086
49165
  }
@@ -53427,6 +53506,19 @@ var DeferredBirthRegistry = class {
53427
53506
  }
53428
53507
  };
53429
53508
  //#endregion
53509
+ //#region src/pipeline-analytics/pipeline/detail-semantic-gate.ts
53510
+ /** The detail `className` whose result IS the recognition. */
53511
+ var PLATE_CLASS = "plate";
53512
+ /**
53513
+ * The moment `className`'s detail result earns the semantic tier.
53514
+ *
53515
+ * `recognition` — promote as soon as the result exists (the plate read).
53516
+ * `label-write` — promote only once the track ACCEPTED the label.
53517
+ */
53518
+ function detailSemanticTrigger(className) {
53519
+ return className === PLATE_CLASS ? "recognition" : "label-write";
53520
+ }
53521
+ //#endregion
53430
53522
  //#region src/pipeline-analytics/pipeline/dropout-skip.ts
53431
53523
  function shouldSkipDropoutFrame(activeTrackCount, detectionCount, consecutiveSkips, cfg) {
53432
53524
  if (!cfg.enabled) return false;
@@ -71126,18 +71218,18 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
71126
71218
  },
71127
71219
  getKeyFrameMediaKey: (trackId) => this.residents.keyFrameKey(trackId),
71128
71220
  emitFaceGalleryChanged: (payload) => this.emitFaceGalleryChanged(payload),
71129
- onSemanticMatch: (match) => this.promoteSemanticRecognition({
71130
- deviceId: match.deviceId,
71131
- trackId: match.trackId,
71132
- timestamp: match.timestamp,
71133
- bbox: match.bbox,
71134
- frameWidth: match.frameWidth,
71135
- frameHeight: match.frameHeight,
71136
- kind: "face",
71137
- score: match.matchScore,
71138
- confidence: match.confidence,
71139
- ...match.frameHandle !== void 0 ? { frameHandle: match.frameHandle } : {}
71140
- }),
71221
+ onSemanticFace: (face) => this.promoteSemanticRecognition({
71222
+ deviceId: face.deviceId,
71223
+ trackId: face.trackId,
71224
+ timestamp: face.timestamp,
71225
+ bbox: face.bbox,
71226
+ frameWidth: face.frameWidth,
71227
+ frameHeight: face.frameHeight,
71228
+ kind: face.identityId !== void 0 ? "face" : "face-visible",
71229
+ score: face.matchScore ?? face.confidence,
71230
+ confidence: face.confidence,
71231
+ ...face.frameHandle !== void 0 ? { frameHandle: face.frameHandle } : {}
71232
+ }, face.identityId !== void 0 ? "identified" : "evidence"),
71141
71233
  onLiveHierarchy: (input) => this.requestTrackHierarchy(input),
71142
71234
  logger: logger.child("FaceRecognizer")
71143
71235
  });
@@ -74153,10 +74245,12 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
74153
74245
  } } : {}
74154
74246
  });
74155
74247
  }
74248
+ if (detailSemanticTrigger(d.className) === "recognition") this.promoteSemanticRecognition(semanticFrameForDetailAttribution(deviceId, trackId, d, frame), "identified");
74156
74249
  }
74157
74250
  const resolved = d.className === "plate" ? this.plateRecognizer?.resolveLabelWithVehicle(d.label, d.score) ?? null : { text: d.label };
74158
74251
  if (resolved !== null && resolved.text !== void 0) {
74159
- if ((await this.applyTrackEnrichmentLabel(deviceId, trackId, resolved.text, d, frame.timestamp, resolved.vehicleId)).trackWritten) this.promoteSemanticRecognition(semanticFrameForDetailAttribution(deviceId, trackId, d, frame));
74252
+ const applied = await this.applyTrackEnrichmentLabel(deviceId, trackId, resolved.text, d, frame.timestamp, resolved.vehicleId);
74253
+ if (detailSemanticTrigger(d.className) === "label-write" && applied.trackWritten) this.promoteSemanticRecognition(semanticFrameForDetailAttribution(deviceId, trackId, d, frame), "identified");
74160
74254
  }
74161
74255
  } else if (d.className === "plate") this.failureReport.notePlateRead(deviceId, d.bbox === void 0 ? REASON_NO_BBOX : REASON_EMPTY_READ);
74162
74256
  } catch (err) {
@@ -74597,8 +74691,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends require_dist.B
74597
74691
  * when it wins and the frame's pixels are still reachable. Callers: the
74598
74692
  * FaceRecognizer's per-frame auto-match, and `routeDetailResults`' plate /
74599
74693
  * fine-classification attribution. */
74600
- promoteSemanticRecognition(input) {
74601
- this.recognizedTrackIds.add(input.trackId);
74694
+ promoteSemanticRecognition(input, origin) {
74695
+ if (origin === "identified") this.recognizedTrackIds.add(input.trackId);
74602
74696
  const ctx = this.ctxIfReady;
74603
74697
  if (ctx === null) return;
74604
74698
  promoteSemanticBestFrame({
@@ -34544,6 +34544,37 @@ var TWO_WHEELERS = new Set([
34544
34544
  function intersection(a, b) {
34545
34545
  return Math.max(0, Math.min(a.x + a.w, b.x + b.w) - Math.max(a.x, b.x)) * Math.max(0, Math.min(a.y + a.h, b.y + b.h) - Math.max(a.y, b.y));
34546
34546
  }
34547
+ /**
34548
+ * The FOLDED subject's box: the union of the rider and the machine.
34549
+ *
34550
+ * The fold's whole claim is that the two halves are ONE subject. Until
34551
+ * 2026-09-08 the box that survived the fold was the MACHINE's alone, so every
34552
+ * consumer that cuts pixels from `TrackedDetectionOut.bbox` — the best-shot
34553
+ * thumbnail, `wideCentralSquareLayout`'s central square, the close-time
34554
+ * keyFrame→thumbnail derive — framed a motorcycle with its rider's head cut
34555
+ * off. Operator, device 617, tracks `7f50622b` and `9cbc4791`, three minutes
34556
+ * apart: "motocicli e veicoli a due ruote devono includere il conducente nel
34557
+ * keyframe" (D407).
34558
+ *
34559
+ * This is the ONE place the folded subject's rectangle is formed, and it is
34560
+ * formed BEFORE the tracker — so no crop path derives a second rectangle
34561
+ * (D52 stays intact: `deriveDetailCropRect` is still the only detail-crop
34562
+ * derivation, and it now receives a subject box that already holds the rider).
34563
+ * Pure geometry: a strict union, never a pad — the fold widens the subject, it
34564
+ * never moves it.
34565
+ */
34566
+ function riderSubjectBox(person, vehicle) {
34567
+ const x = Math.min(person.x, vehicle.x);
34568
+ const y = Math.min(person.y, vehicle.y);
34569
+ const right = Math.max(person.x + person.w, vehicle.x + vehicle.w);
34570
+ const bottom = Math.max(person.y + person.h, vehicle.y + vehicle.h);
34571
+ return {
34572
+ x,
34573
+ y,
34574
+ w: right - x,
34575
+ h: bottom - y
34576
+ };
34577
+ }
34547
34578
  /** True when `vehicle` is a two-wheeler that `person` is riding. */
34548
34579
  function isRiderPair(person, vehicle) {
34549
34580
  if (person.macroClass !== "person") return false;
@@ -36310,17 +36341,64 @@ var FrameProcessor = class {
36310
36341
  const foldByVehicleBbox = /* @__PURE__ */ new Map();
36311
36342
  if (riderPairs.length > 0) {
36312
36343
  const riderIdx = new Set(riderPairs.map((p) => Number(p.personId)));
36344
+ /**
36345
+ * Move every bbox-OBJECT-keyed side table from the machine's box to the
36346
+ * folded subject's box (D407).
36347
+ *
36348
+ * This file carries frame-local facts across the tracker by bbox-object
36349
+ * REFERENCE (`labelsByBbox`, `embeddingByBbox`, `maskByBbox`, …); the
36350
+ * tracker then assigns `track.bbox = det.bbox`, and the emit block below
36351
+ * reads every one of those maps with `td.bbox`. Widening the subject
36352
+ * therefore means the widened box must inherit the machine's entries —
36353
+ * otherwise a folded rider silently loses its vehicle-classifier label
36354
+ * ("Motorcycle"), its mask, its plate read and its source id. One place,
36355
+ * exhaustive, next to the maps it moves.
36356
+ */
36357
+ const adoptSubjectBox = (from, to) => {
36358
+ const labels = labelsByBbox.get(from);
36359
+ if (labels !== void 0) labelsByBbox.set(to, labels);
36360
+ const tier = originalClassTierByBbox.get(from);
36361
+ if (tier !== void 0) originalClassTierByBbox.set(to, tier);
36362
+ const embedding = embeddingByBbox.get(from);
36363
+ if (embedding !== void 0) embeddingByBbox.set(to, embedding);
36364
+ const mask = maskByBbox.get(from);
36365
+ if (mask !== void 0) maskByBbox.set(to, mask);
36366
+ const plate = plateByBbox.get(from);
36367
+ if (plate !== void 0) plateByBbox.set(to, plate);
36368
+ const faceBbox = faceBboxByBbox.get(from);
36369
+ if (faceBbox !== void 0) faceBboxByBbox.set(to, faceBbox);
36370
+ const faceSize = nativeFaceSizeByBbox.get(from);
36371
+ if (faceSize !== void 0) nativeFaceSizeByBbox.set(to, faceSize);
36372
+ const alignedCrop = faceAlignedCropByBbox.get(from);
36373
+ if (alignedCrop !== void 0) faceAlignedCropByBbox.set(to, alignedCrop);
36374
+ const sourceId = sourceIdByBbox.get(from);
36375
+ if (sourceId !== void 0) {
36376
+ sourceIdByBbox.set(to, sourceId);
36377
+ firstLevelBboxById.set(sourceId, to);
36378
+ }
36379
+ };
36380
+ /** Folded vehicles, by their index in `filteredDetections`. */
36381
+ const foldedVehicles = /* @__PURE__ */ new Map();
36313
36382
  for (const p of riderPairs) {
36314
- const vehicle = filteredDetections[Number(p.vehicleId)];
36315
- if (vehicle === void 0) continue;
36316
- foldByVehicleBbox.set(vehicle.bbox, {
36383
+ const vehicleIdx = Number(p.vehicleId);
36384
+ const vehicle = filteredDetections[vehicleIdx];
36385
+ const person = filteredDetections[Number(p.personId)];
36386
+ if (vehicle === void 0 || person === void 0) continue;
36387
+ const subjectBox = riderSubjectBox(person.bbox, vehicle.bbox);
36388
+ adoptSubjectBox(vehicle.bbox, subjectBox);
36389
+ const folded = {
36390
+ ...vehicle,
36391
+ bbox: subjectBox
36392
+ };
36393
+ foldedVehicles.set(vehicleIdx, folded);
36394
+ foldByVehicleBbox.set(subjectBox, {
36317
36395
  overlap: p.overlap,
36318
- personScore: filteredDetections[Number(p.personId)]?.score ?? 0,
36396
+ personScore: person.score,
36319
36397
  vehicleScore: vehicle.score,
36320
36398
  vehicleClass: vehicle.originalClass ?? "two-wheeler"
36321
36399
  });
36322
36400
  }
36323
- filteredDetections = filteredDetections.filter((_, i) => !riderIdx.has(i));
36401
+ filteredDetections = filteredDetections.map((d, i) => foldedVehicles.get(i) ?? d).filter((_, i) => !riderIdx.has(i));
36324
36402
  }
36325
36403
  const gate = this.stationaryGate ? this.stationaryGate.filter({
36326
36404
  detections: filteredDetections,
@@ -48994,10 +49072,9 @@ var FaceRecognizer = class {
48994
49072
  margin: settings.margin,
48995
49073
  minIdentitySamples: settings.minIdentitySamples
48996
49074
  }) : /* @__PURE__ */ new Map();
48997
- if (this.deps.onSemanticMatch !== void 0) for (const c of matchCandidates) {
49075
+ if (this.deps.onSemanticFace !== void 0) for (const c of matchCandidates) {
48998
49076
  const match = matches.get(c.trackId);
48999
- if (match === void 0) continue;
49000
- this.deps.onSemanticMatch({
49077
+ this.deps.onSemanticFace({
49001
49078
  deviceId: input.deviceId,
49002
49079
  trackId: c.trackId,
49003
49080
  timestamp: input.timestamp,
@@ -49005,8 +49082,10 @@ var FaceRecognizer = class {
49005
49082
  frameHeight: input.frameHeight,
49006
49083
  bbox: c.bbox,
49007
49084
  confidence: c.confidence,
49008
- matchScore: match.score,
49009
- identityId: match.identityId,
49085
+ ...match !== void 0 ? {
49086
+ matchScore: match.score,
49087
+ identityId: match.identityId
49088
+ } : {},
49010
49089
  ...input.frameHandle !== void 0 ? { frameHandle: input.frameHandle } : {}
49011
49090
  });
49012
49091
  }
@@ -53353,6 +53432,19 @@ var DeferredBirthRegistry = class {
53353
53432
  }
53354
53433
  };
53355
53434
  //#endregion
53435
+ //#region src/pipeline-analytics/pipeline/detail-semantic-gate.ts
53436
+ /** The detail `className` whose result IS the recognition. */
53437
+ var PLATE_CLASS = "plate";
53438
+ /**
53439
+ * The moment `className`'s detail result earns the semantic tier.
53440
+ *
53441
+ * `recognition` — promote as soon as the result exists (the plate read).
53442
+ * `label-write` — promote only once the track ACCEPTED the label.
53443
+ */
53444
+ function detailSemanticTrigger(className) {
53445
+ return className === PLATE_CLASS ? "recognition" : "label-write";
53446
+ }
53447
+ //#endregion
53356
53448
  //#region src/pipeline-analytics/pipeline/dropout-skip.ts
53357
53449
  function shouldSkipDropoutFrame(activeTrackCount, detectionCount, consecutiveSkips, cfg) {
53358
53450
  if (!cfg.enabled) return false;
@@ -71052,18 +71144,18 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
71052
71144
  },
71053
71145
  getKeyFrameMediaKey: (trackId) => this.residents.keyFrameKey(trackId),
71054
71146
  emitFaceGalleryChanged: (payload) => this.emitFaceGalleryChanged(payload),
71055
- onSemanticMatch: (match) => this.promoteSemanticRecognition({
71056
- deviceId: match.deviceId,
71057
- trackId: match.trackId,
71058
- timestamp: match.timestamp,
71059
- bbox: match.bbox,
71060
- frameWidth: match.frameWidth,
71061
- frameHeight: match.frameHeight,
71062
- kind: "face",
71063
- score: match.matchScore,
71064
- confidence: match.confidence,
71065
- ...match.frameHandle !== void 0 ? { frameHandle: match.frameHandle } : {}
71066
- }),
71147
+ onSemanticFace: (face) => this.promoteSemanticRecognition({
71148
+ deviceId: face.deviceId,
71149
+ trackId: face.trackId,
71150
+ timestamp: face.timestamp,
71151
+ bbox: face.bbox,
71152
+ frameWidth: face.frameWidth,
71153
+ frameHeight: face.frameHeight,
71154
+ kind: face.identityId !== void 0 ? "face" : "face-visible",
71155
+ score: face.matchScore ?? face.confidence,
71156
+ confidence: face.confidence,
71157
+ ...face.frameHandle !== void 0 ? { frameHandle: face.frameHandle } : {}
71158
+ }, face.identityId !== void 0 ? "identified" : "evidence"),
71067
71159
  onLiveHierarchy: (input) => this.requestTrackHierarchy(input),
71068
71160
  logger: logger.child("FaceRecognizer")
71069
71161
  });
@@ -74079,10 +74171,12 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
74079
74171
  } } : {}
74080
74172
  });
74081
74173
  }
74174
+ if (detailSemanticTrigger(d.className) === "recognition") this.promoteSemanticRecognition(semanticFrameForDetailAttribution(deviceId, trackId, d, frame), "identified");
74082
74175
  }
74083
74176
  const resolved = d.className === "plate" ? this.plateRecognizer?.resolveLabelWithVehicle(d.label, d.score) ?? null : { text: d.label };
74084
74177
  if (resolved !== null && resolved.text !== void 0) {
74085
- if ((await this.applyTrackEnrichmentLabel(deviceId, trackId, resolved.text, d, frame.timestamp, resolved.vehicleId)).trackWritten) this.promoteSemanticRecognition(semanticFrameForDetailAttribution(deviceId, trackId, d, frame));
74178
+ const applied = await this.applyTrackEnrichmentLabel(deviceId, trackId, resolved.text, d, frame.timestamp, resolved.vehicleId);
74179
+ if (detailSemanticTrigger(d.className) === "label-write" && applied.trackWritten) this.promoteSemanticRecognition(semanticFrameForDetailAttribution(deviceId, trackId, d, frame), "identified");
74086
74180
  }
74087
74181
  } else if (d.className === "plate") this.failureReport.notePlateRead(deviceId, d.bbox === void 0 ? REASON_NO_BBOX : REASON_EMPTY_READ);
74088
74182
  } catch (err) {
@@ -74523,8 +74617,8 @@ var PipelineAnalyticsAddon = class PipelineAnalyticsAddon extends BaseAddon {
74523
74617
  * when it wins and the frame's pixels are still reachable. Callers: the
74524
74618
  * FaceRecognizer's per-frame auto-match, and `routeDetailResults`' plate /
74525
74619
  * fine-classification attribution. */
74526
- promoteSemanticRecognition(input) {
74527
- this.recognizedTrackIds.add(input.trackId);
74620
+ promoteSemanticRecognition(input, origin) {
74621
+ if (origin === "identified") this.recognizedTrackIds.add(input.trackId);
74528
74622
  const ctx = this.ctxIfReady;
74529
74623
  if (ctx === null) return;
74530
74624
  promoteSemanticBestFrame({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-post-analysis",
3
- "version": "1.2.210",
3
+ "version": "1.2.211",
4
4
  "description": "Post-Analysis bundle — enrichment, embedding-encoder, pipeline-analytics. Multi-entry npm package shipping addons that consume pipeline output.",
5
5
  "keywords": [
6
6
  "camstack",