processInputImage method
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,
);
}