predict method

Future<List<DetectionResult>> predict(
  1. Uint8List inputData, {
  2. double confThreshold = 0.25,
})

Implementation

Future<List<DetectionResult>> predict(
  Uint8List inputData, {
  double confThreshold = 0.25,
}) async {
  final tensorData = await PreProcessing.toTensorData(
    inputData,
    targetWidth: inputWidth,
    targetHeight: inputHeight,
  );

  // Executa a inferência
  final outputs = await model.forward([tensorData]);

  debugPrint('Inferência concluída no modelo de detecção');
  for (var output in outputs) {
    debugPrint('Output shape: ${output.shape}');
    debugPrint('Output type: ${output.dataType}');
    debugPrint('Output data length: ${output.data.length}');
    debugPrint(
      'Output data (first 10 values): ${output.data.take(10).toList()}',
    );
  }
  if (outputs.isEmpty) return <DetectionResult>[];

  final out = outputs[0];
  // Expecting shape like [1, channels, num_boxes]
  final shape = out.shape;
  if (shape.length < 3) return <DetectionResult>[];
  final channels = shape[1]!;
  final numBoxes = shape[2]!;

  // Convert bytes to float32 list (respecting offset)
  final floatData = out.data.buffer.asFloat32List(
    out.data.offsetInBytes,
    out.data.lengthInBytes ~/ 4,
  );
  debugPrint('Parsed float length: ${floatData.length}');
  try {
    debugPrint('First 10 floats: ${floatData.take(10).toList()}');
  } catch (e) {
    debugPrint('Could not print float sample: $e');
  }

  // Helpers
  double sigmoid(double x) => 1.0 / (1.0 + math.exp(-x));
  final List<DetectionResult> detections = [];

  final expectedWithObj = labels.length + 5;
  final hasObjectness = channels == expectedWithObj;
  final classCount = hasObjectness ? (channels - 5) : (channels - 4);
  debugPrint(
    'channels=$channels numBoxes=$numBoxes expectedWithObj=$expectedWithObj hasObjectness=$hasObjectness classCount=$classCount',
  );

  for (var i = 0; i < numBoxes; i++) {
    // index by channel-first layout: index = c * numBoxes + i
    double at(int c) => floatData[c * numBoxes + i];

    final x = at(0);
    final y = at(1);
    final w = at(2);
    final h = at(3);

    double objectness = 1.0;
    int classOffset = 4;
    if (hasObjectness) {
      objectness = sigmoid(at(4));
      classOffset = 5;
    }

    final classScores = List<double>.generate(
      classCount,
      (j) => at(classOffset + j),
    );
    if (i < 3)
      debugPrint(
        'box $i raw x,y,w,h: $x,$y,$w,$h objectness:$objectness classScoresSample:${classScores.take(6).toList()}',
      );
    final classProbs = classScores.map((s) => sigmoid(s)).toList();
    double maxClassProb = classProbs.reduce(math.max);
    final classId = classProbs.indexWhere((p) => p == maxClassProb);

    final conf = hasObjectness ? (objectness * maxClassProb) : maxClassProb;
    if (conf < confThreshold) continue;

    // Assume x,y,w,h are normalized center coords (0..1)
    final cx = x;
    final cy = y;
    final bw = w;
    final bh = h;

    double left = (cx - bw / 2.0) * inputWidth;
    double top = (cy - bh / 2.0) * inputHeight;
    double right = (cx + bw / 2.0) * inputWidth;
    double bottom = (cy + bh / 2.0) * inputHeight;

    // Clamp
    left = left.clamp(0.0, inputWidth.toDouble());
    top = top.clamp(0.0, inputHeight.toDouble());
    right = right.clamp(0.0, inputWidth.toDouble());
    bottom = bottom.clamp(0.0, inputHeight.toDouble());

    final label = (classId >= 0 && classId < labels.length)
        ? labels[classId]
        : 'class_$classId';

    detections.add(
      DetectionResult(
        classId: classId,
        label: label,
        confidence: conf,
        bbox: [left, top, right, bottom],
      ),
    );
  }

  // Sort by confidence descending
  detections.sort((a, b) => b.confidence.compareTo(a.confidence));
  return detections;
}