88 lines
2.6 KiB
Dart
88 lines
2.6 KiB
Dart
import 'dart:io';
|
|
import 'dart:ui' as ui;
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:image/image.dart' as img;
|
|
import 'package:path_provider/path_provider.dart';
|
|
|
|
import '../widgets/guide_frame.dart';
|
|
import 'card_detector.dart';
|
|
|
|
/// Quick post-capture gate: guide-crop + ML Kit. No box → ask user to retake.
|
|
class CardScanGate {
|
|
/// Returns true when a card-like border is found inside the shooting guide.
|
|
static Future<bool> hasDetectableBorder({
|
|
required String imagePath,
|
|
Size? captureViewport,
|
|
CardDetector? detector,
|
|
}) async {
|
|
final owned = detector == null;
|
|
final det = detector ?? CardDetector();
|
|
try {
|
|
final raw = await File(imagePath).readAsBytes();
|
|
final codec = await ui.instantiateImageCodec(raw);
|
|
final frame = await codec.getNextFrame();
|
|
final fullImage = frame.image;
|
|
|
|
final rgba = await fullImage.toByteData(format: ui.ImageByteFormat.rawRgba);
|
|
if (rgba == null) {
|
|
fullImage.dispose();
|
|
return false;
|
|
}
|
|
|
|
final fullRaster = img.Image.fromBytes(
|
|
width: fullImage.width,
|
|
height: fullImage.height,
|
|
bytes: rgba.buffer,
|
|
bytesOffset: rgba.offsetInBytes,
|
|
numChannels: 4,
|
|
order: img.ChannelOrder.rgba,
|
|
);
|
|
fullImage.dispose();
|
|
|
|
final imageSize = Size(
|
|
fullRaster.width.toDouble(),
|
|
fullRaster.height.toDouble(),
|
|
);
|
|
final guide = captureViewport != null
|
|
? GuideFrame.inCoverImage(
|
|
viewport: captureViewport,
|
|
imageSize: imageSize,
|
|
)
|
|
: GuideFrame.inImage(imageSize);
|
|
|
|
final gx = guide.left.round().clamp(0, fullRaster.width - 1);
|
|
final gy = guide.top.round().clamp(0, fullRaster.height - 1);
|
|
final gw = guide.width.round().clamp(1, fullRaster.width - gx);
|
|
final gh = guide.height.round().clamp(1, fullRaster.height - gy);
|
|
final raster = img.copyCrop(
|
|
fullRaster,
|
|
x: gx,
|
|
y: gy,
|
|
width: gw,
|
|
height: gh,
|
|
);
|
|
|
|
final dir = await getTemporaryDirectory();
|
|
final guidePath =
|
|
'${dir.path}/card_gate_${DateTime.now().millisecondsSinceEpoch}.jpg';
|
|
await File(guidePath).writeAsBytes(
|
|
img.encodeJpg(raster, quality: 95),
|
|
flush: true,
|
|
);
|
|
|
|
final boxes = await det.detectFile(guidePath);
|
|
final guideSize = Size(
|
|
raster.width.toDouble(),
|
|
raster.height.toDouble(),
|
|
);
|
|
final best = det.pickBest(boxes, imageSize: guideSize);
|
|
return best != null && boxes.isNotEmpty;
|
|
} finally {
|
|
if (owned) {
|
|
await det.dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|