processInputImage method

Future<ScanResult> processInputImage(
  1. InputImage inputImage,
  2. ScanMode mode, {
  3. String? imagePath,
})

Processes an ML Kit InputImage for the specified ScanMode.

Implementation

Future<ScanResult> processInputImage(
  InputImage inputImage,
  ScanMode mode, {
  String? imagePath,
}) async {
  initialize();
  final stopwatch = Stopwatch()..start();

  final plugin = ScannerPluginRegistry.findForMode(mode);
  if (plugin != null) {
    try {
      final pluginResult = await plugin.processInputImage(inputImage);
      if (pluginResult != null) {
        stopwatch.stop();
        return pluginResult;
      }
    } catch (_) {}
  }

  ScanResult rawResult;
  switch (mode) {
    case ScanMode.qr:
    case ScanMode.barcode:
    case ScanMode.pdf417:
    case ScanMode.multiCode:
      rawResult = await _processBarcodes(
        inputImage,
        mode,
        imagePath: imagePath,
      );
      break;

    case ScanMode.passport:
    case ScanMode.aadhaar:
    case ScanMode.pan:
    case ScanMode.drivingLicense:
    case ScanMode.vin:
    case ScanMode.ocr:
    case ScanMode.invoice:
    case ScanMode.receipt:
    case ScanMode.businessCard:
    case ScanMode.cheque:
    case ScanMode.idCard:
    case ScanMode.licensePlate:
      rawResult = await _processTextAndDocuments(
        inputImage,
        mode,
        imagePath: imagePath,
      );
      break;

    case ScanMode.document:
      rawResult = await _processDocumentScanner(
        inputImage,
        imagePath: imagePath,
      );
      break;

    case ScanMode.face:
      rawResult = await _processFaces(inputImage, mode, imagePath: imagePath);
      break;
  }

  stopwatch.stop();
  final duration = stopwatch.elapsed;

  final width = inputImage.metadata?.size.width ?? 640.0;
  final height = inputImage.metadata?.size.height ?? 480.0;
  final imgSize = Size(width, height);

  Rect? bbox = rawResult.boundingBox;
  List<Offset>? corners = rawResult.corners;
  if (bbox == null && rawResult.isValid) {
    final docCorners = DocumentScannerService.detectDocumentEdges(imgSize);
    bbox = docCorners.toBoundingBox();
    corners = docCorners.toList();
  }

  // AI Classification pass
  final classification = DocumentClassifier.classify(
    rawResult.rawValue,
    fields: rawResult.fields,
    mode: mode,
  );

  debugPrint(
    '⏱️ [UniversalScanEngine] Scan completed in ${duration.inMilliseconds} ms | Mode: ${mode.name} | Category: ${classification.category.name} | Valid: ${rawResult.isValid}',
  );

  // v3.0: Apply result post-processing ONLY for barcode/QR modes
  // For OCR-parsed modes (aadhaar, passport, pan, etc.) the parser has already
  // processed and validated the text — running post-processing on the parser's
  // output would destroy the structured fields (e.g. strip names, dates).
  String correctedRawValue = rawResult.rawValue;
  List<String> corrections = [];
  double confidenceAdjustment = 0.0;

  final isBarcodeLikeMode = mode == ScanMode.barcode ||
      mode == ScanMode.qr ||
      mode == ScanMode.multiCode ||
      mode == ScanMode.pdf417;

  if (isBarcodeLikeMode) {
    final postProcessed =
        ResultPostProcessor.process(rawResult.rawValue, mode);
    correctedRawValue = postProcessed.text;
    corrections = postProcessed.corrections;
    confidenceAdjustment = postProcessed.confidenceAdjustment;
  }

  // v3.0: Determine detector name for tracing
  String detectorName = 'mlkit';
  if (rawResult.detectorName != null) {
    detectorName = rawResult.detectorName!;
  }

  // Adjust confidence based on post-processing corrections
  final adjustedConfidence =
      (rawResult.confidence + confidenceAdjustment).clamp(0.0, 1.0);

  return ScanResult(
    mode: rawResult.mode,
    rawValue: correctedRawValue,
    fields: rawResult.fields,
    isValid: rawResult.isValid,
    confidence: adjustedConfidence,
    timestamp: rawResult.timestamp,
    imagePath: rawResult.imagePath,
    rawBytes: rawResult.rawBytes,
    format: rawResult.format ?? rawResult.metadata['format'] as String?,
    documentCategory: classification.category.name,
    roi: rawResult.roi,
    enhancementsApplied: rawResult.enhancementsApplied,
    corners: corners,
    boundingBox: bbox,
    imageSize: imgSize,
    scanDuration: duration,
    metadata: {
      ...rawResult.metadata,
      'aiClassification': classification.toJson(),
    },
    multiResults: rawResult.multiResults,
    detectedBarcodes: rawResult.detectedBarcodes,
    detectorName: detectorName,
    postProcessingCorrections: corrections,
    processingPipeline: rawResult.processingPipeline,
  );
}