coin-recog-demo/lib/services/coin_cropper.dart

81 lines
2.2 KiB
Dart
Raw Normal View History

import 'dart:io';
import 'dart:math' as math;
import 'dart:typed_data';
import 'dart:ui';
import 'package:image/image.dart' as img;
import 'coin_detector.dart';
class CropResult {
const CropResult({
required this.bytes,
required this.width,
required this.height,
required this.usedDetection,
});
final Uint8List bytes;
final int width;
final int height;
final bool usedDetection;
}
/// Circular crop of the detected coin (square output with transparent-ish bg).
class CoinCropper {
static Future<CropResult> cropAndCorrect({
required String imagePath,
CoinBox? box,
double padding = 0.12,
}) async {
final raw = await File(imagePath).readAsBytes();
var decoded = img.decodeImage(raw);
if (decoded == null) {
throw StateError('Failed to decode image: $imagePath');
}
decoded = img.bakeOrientation(decoded);
final usedDetection = box != null;
final center = box?.effectiveCenter ??
Offset(decoded.width / 2, decoded.height / 2);
var radius = box?.effectiveRadius ??
math.min(decoded.width, decoded.height) * 0.35;
radius *= (1 + padding);
final size = (radius * 2).round().clamp(32, 2048);
final out = img.Image(width: size, height: size, numChannels: 4);
img.fill(out, color: img.ColorRgba8(0, 0, 0, 0));
final r2 = radius * radius;
for (var y = 0; y < size; y++) {
for (var x = 0; x < size; x++) {
final dx = x + 0.5 - size / 2;
final dy = y + 0.5 - size / 2;
if (dx * dx + dy * dy > r2) continue;
final sx = (center.dx + dx).round();
final sy = (center.dy + dy).round();
if (sx < 0 || sy < 0 || sx >= decoded.width || sy >= decoded.height) {
continue;
}
final p = decoded.getPixel(sx, sy);
out.setPixelRgba(x, y, p.r.toInt(), p.g.toInt(), p.b.toInt(), 255);
}
}
const outSize = 320;
final resized = img.copyResize(
out,
width: outSize,
height: outSize,
interpolation: img.Interpolation.cubic,
);
final bytes = Uint8List.fromList(img.encodePng(resized));
return CropResult(
bytes: bytes,
width: resized.width,
height: resized.height,
usedDetection: usedDetection,
);
}
}