feat: target rectify service pour perfectionner la prise de photo avec detection
This commit is contained in:
@@ -19,6 +19,7 @@ import '../../services/image_crop_service.dart';
|
||||
import '../../services/opencv_target_service.dart';
|
||||
import '../../services/parallelism_service.dart'; // NOUVEAU
|
||||
import '../../services/object_detection_service.dart'; // NOUVEAU : ML Kit
|
||||
import '../../services/target_rectify_service.dart'; // NOUVEAU : redressement
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
@@ -69,6 +70,9 @@ class _CaptureScreenState extends State<CaptureScreen>
|
||||
StreamSubscription<List<DetectedObject2D>>? _objectSubscription;
|
||||
List<DetectedObject2D> _detectedObjects = [];
|
||||
|
||||
// NOUVEAU : Service de redressement de cible (warp perspective)
|
||||
final TargetRectifyService _rectifyService = TargetRectifyService();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -506,14 +510,15 @@ class _CaptureScreenState extends State<CaptureScreen>
|
||||
),
|
||||
),
|
||||
|
||||
// NOUVEAU : 1.bis Cadre dessiné autour de l'objet détecté
|
||||
if (_primaryObject != null)
|
||||
// NOUVEAU : 1.bis Cadre dessiné autour de CHAQUE objet détecté
|
||||
if (_detectedObjects.isNotEmpty)
|
||||
Center(
|
||||
child: AspectRatio(
|
||||
aspectRatio: 3 / 4,
|
||||
child: CustomPaint(
|
||||
painter: _ObjectBoxPainter(
|
||||
object: _primaryObject!,
|
||||
objects: _detectedObjects,
|
||||
primary: _primaryObject,
|
||||
color: frameColor,
|
||||
),
|
||||
),
|
||||
@@ -792,8 +797,39 @@ class _CaptureScreenState extends State<CaptureScreen>
|
||||
|
||||
final XFile photo = await _cameraController!.takePicture();
|
||||
|
||||
// NOUVEAU : redressement automatique de la cible (warp perspective).
|
||||
// On écrit le résultat dans un fichier dédié, à côté de l'original.
|
||||
String finalPath = photo.path;
|
||||
try {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
final rectifiedPath =
|
||||
'${tempDir.path}/rectified_${DateTime.now().millisecondsSinceEpoch}.jpg';
|
||||
|
||||
final result = await _rectifyService.rectify(
|
||||
inputPath: photo.path,
|
||||
outputPath: rectifiedPath,
|
||||
);
|
||||
|
||||
finalPath = result.outputPath;
|
||||
|
||||
if (mounted && result.rectified) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
duration: const Duration(seconds: 2),
|
||||
content: Text(
|
||||
'Cible redressée automatiquement '
|
||||
'(inclinaison ${result.estimatedTiltDegrees.toStringAsFixed(1)}° corrigée)',
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('Redressement ignoré: $e');
|
||||
finalPath = photo.path; // on garde la photo originale en secours
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_selectedImagePath = photo.path;
|
||||
_selectedImagePath = finalPath;
|
||||
_showLiveCamera = false;
|
||||
});
|
||||
|
||||
@@ -902,58 +938,66 @@ class _CaptureScreenState extends State<CaptureScreen>
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// NOUVEAU : Painter qui dessine la boîte englobante de l'objet détecté.
|
||||
// Les coordonnées de l'objet sont normalisées (0..1) → on les multiplie
|
||||
// par la taille réelle de la zone de dessin (qui suit l'AspectRatio 3/4).
|
||||
// Painter qui dessine la boîte englobante de CHAQUE objet détecté.
|
||||
// L'objet principal (le plus grand et centré) est mis en évidence avec
|
||||
// la couleur d'état (vert/orange) ; les autres en blanc translucide.
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
class _ObjectBoxPainter extends CustomPainter {
|
||||
final DetectedObject2D object;
|
||||
final List<DetectedObject2D> objects;
|
||||
final DetectedObject2D? primary;
|
||||
final Color color;
|
||||
|
||||
_ObjectBoxPainter({required this.object, required this.color});
|
||||
_ObjectBoxPainter({
|
||||
required this.objects,
|
||||
required this.primary,
|
||||
required this.color,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final Paint stroke = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 3.0
|
||||
..color = color;
|
||||
for (final obj in objects) {
|
||||
final bool isPrimary = identical(obj, primary);
|
||||
final Color boxColor =
|
||||
isPrimary ? color : Colors.white.withValues(alpha: 0.6);
|
||||
|
||||
final Rect rect = Rect.fromLTRB(
|
||||
object.left * size.width,
|
||||
object.top * size.height,
|
||||
object.right * size.width,
|
||||
object.bottom * size.height,
|
||||
);
|
||||
final Paint stroke = Paint()
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = isPrimary ? 3.0 : 1.5
|
||||
..color = boxColor;
|
||||
|
||||
// Boîte à coins arrondis
|
||||
final RRect rrect =
|
||||
RRect.fromRectAndRadius(rect, const Radius.circular(8));
|
||||
canvas.drawRRect(rrect, stroke);
|
||||
|
||||
// Petit label si disponible
|
||||
if (object.label.isNotEmpty) {
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(
|
||||
text: ' ${object.label} '
|
||||
'${(object.confidence * 100).toStringAsFixed(0)}% ',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
backgroundColor: color,
|
||||
),
|
||||
),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
textPainter.paint(
|
||||
canvas,
|
||||
Offset(rect.left, (rect.top - 18).clamp(0.0, size.height)),
|
||||
final Rect rect = Rect.fromLTRB(
|
||||
obj.left * size.width,
|
||||
obj.top * size.height,
|
||||
obj.right * size.width,
|
||||
obj.bottom * size.height,
|
||||
);
|
||||
final RRect rrect =
|
||||
RRect.fromRectAndRadius(rect, const Radius.circular(8));
|
||||
canvas.drawRRect(rrect, stroke);
|
||||
|
||||
if (obj.label.isNotEmpty) {
|
||||
final textPainter = TextPainter(
|
||||
text: TextSpan(
|
||||
text: ' ${obj.label} '
|
||||
'${(obj.confidence * 100).toStringAsFixed(0)}% ',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
backgroundColor: boxColor,
|
||||
),
|
||||
),
|
||||
textDirection: TextDirection.ltr,
|
||||
)..layout();
|
||||
textPainter.paint(
|
||||
canvas,
|
||||
Offset(rect.left, (rect.top - 18).clamp(0.0, size.height)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(_ObjectBoxPainter old) =>
|
||||
old.object != object || old.color != color;
|
||||
}
|
||||
old.objects != objects || old.color != color || old.primary != primary;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user