updateTracker method

List<TrackedBarcode> updateTracker(
  1. List<BarcodeResult> frameBarcodes
)

Updates multi-barcode spatial tracker with newly detected frame barcodes.

Enhanced v3.0: uses exponential smoothing for bounding box positions to reduce jitter in live camera feeds.

Implementation

List<TrackedBarcode> updateTracker(List<BarcodeResult> frameBarcodes) {
  final now = DateTime.now();
  final updatedKeys = <String>{};

  for (final b in frameBarcodes) {
    final bBox = b.boundingBox ?? Rect.zero;
    final centerB = bBox.center;

    String? matchedId;
    double minCenterDist = double.infinity;

    // Find closest existing track with matching payload
    for (final track in _activeTracks.values) {
      if (track.rawValue == b.rawValue) {
        final dist = (track.boundingBox.center - centerB).distance;
        if (dist < 150.0 && dist < minCenterDist) {
          minCenterDist = dist;
          matchedId = track.id;
        }
      }
    }

    if (matchedId != null) {
      final existing = _activeTracks[matchedId]!;

      // Exponential smoothing for bounding box (α = 0.3)
      const alpha = 0.3;
      final smoothedBox = Rect.fromLTRB(
        existing.boundingBox.left * (1 - alpha) + bBox.left * alpha,
        existing.boundingBox.top * (1 - alpha) + bBox.top * alpha,
        existing.boundingBox.right * (1 - alpha) + bBox.right * alpha,
        existing.boundingBox.bottom * (1 - alpha) + bBox.bottom * alpha,
      );

      _activeTracks[matchedId] = existing.copyWith(
        boundingBox: smoothedBox,
        corners: b.corners ?? existing.corners,
        lastSeenTime: now,
        hitCount: existing.hitCount + 1,
      );
      updatedKeys.add(matchedId);
    } else {
      final newId = 'track_${_nextId++}';
      _activeTracks[newId] = TrackedBarcode(
        id: newId,
        rawValue: b.rawValue,
        format: b.format,
        boundingBox: bBox,
        corners: b.corners ?? [],
        firstDetectedTime: now,
        lastSeenTime: now,
      );
      updatedKeys.add(newId);
    }
  }

  // Prune tracks not seen in last 2 seconds (up from 1.5s for better stability)
  _activeTracks.removeWhere((id, track) {
    return !updatedKeys.contains(id) &&
        now.difference(track.lastSeenTime).inMilliseconds > 2000;
  });

  return _activeTracks.values.toList();
}