From 331fda0ffb908e11e5792344694156d01d522561 Mon Sep 17 00:00:00 2001 From: qguillaume Date: Thu, 11 Jun 2026 20:04:25 +0200 Subject: [PATCH] fix: freeze et deformation lors de la prise de la photo (rotation) --- lib/features/capture/capture_screen.dart | 84 +++++++++--------------- 1 file changed, 31 insertions(+), 53 deletions(-) diff --git a/lib/features/capture/capture_screen.dart b/lib/features/capture/capture_screen.dart index 0a6103d3..d13e3a37 100644 --- a/lib/features/capture/capture_screen.dart +++ b/lib/features/capture/capture_screen.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'dart:math' as math; import 'dart:async'; +import 'dart:isolate'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -17,7 +18,6 @@ import 'widgets/image_source_button.dart'; import '../../services/image_crop_service.dart'; import '../../services/opencv_target_service.dart'; import '../../services/parallelism_service.dart'; // NOUVEAU -import '../../services/target_rectify_service.dart'; // NOUVEAU : redressement import 'package:image/image.dart' as img; import 'package:path_provider/path_provider.dart'; @@ -64,9 +64,6 @@ class _CaptureScreenState extends State ParallelismData? _parallelismData; StreamSubscription? _parallelismSubscription; - // Service de redressement de cible (warp perspective) - final TargetRectifyService _rectifyService = TargetRectifyService(); - @override void initState() { super.initState(); @@ -735,36 +732,11 @@ class _CaptureScreenState extends State 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. + // Pas de redressement perspective automatique ici : il déformait l'image + // (écrasement en carré + rotation de l'axe de l'ellipse) et gelait le + // thread UI. Le recentrage ET la rotation se font manuellement à l'écran + // de centrage suivant, qui est précis et non destructeur. 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 - } // PERF : on dégrade fortement la photo juste après la capture. // Une résolution réduite suffit pour la calibration et le plotting, @@ -795,29 +767,35 @@ class _CaptureScreenState extends State /// Baisser `maxSide` (ex. 900) ou `quality` (ex. 60) rend le chargement /// encore plus rapide au prix d'un peu de finesse à l'écran. Future _downscaleForPipeline(String sourcePath) async { - final bytes = await File(sourcePath).readAsBytes(); - final decoded = img.decodeImage(bytes); - if (decoded == null) return sourcePath; - - const int maxSide = 1080; - final int longest = math.max(decoded.width, decoded.height); - if (longest <= maxSide) { - return sourcePath; // déjà assez petite, rien à faire - } - - final double ratio = maxSide / longest; - final resized = img.copyResize( - decoded, - width: (decoded.width * ratio).round(), - height: (decoded.height * ratio).round(), - interpolation: img.Interpolation.linear, // rapide - ); - + // On résout le chemin de sortie sur le thread UI (path_provider a besoin + // du canal de plateforme), puis tout le travail lourd (décodage, resize, + // réencodage JPEG) part dans un Isolate → aucun gel du thread UI. final tempDir = await getTemporaryDirectory(); final outPath = '${tempDir.path}/downscaled_${DateTime.now().millisecondsSinceEpoch}.jpg'; - await File(outPath).writeAsBytes(img.encodeJpg(resized, quality: 70)); - return outPath; + + return Isolate.run(() { + final bytes = File(sourcePath).readAsBytesSync(); + final decoded = img.decodeImage(bytes); + if (decoded == null) return sourcePath; + + const int maxSide = 1080; + final int longest = math.max(decoded.width, decoded.height); + if (longest <= maxSide) { + return sourcePath; // déjà assez petite, rien à faire + } + + final double ratio = maxSide / longest; + final resized = img.copyResize( + decoded, + width: (decoded.width * ratio).round(), + height: (decoded.height * ratio).round(), + interpolation: img.Interpolation.linear, // rapide + ); + + File(outPath).writeAsBytesSync(img.encodeJpg(resized, quality: 70)); + return outPath; + }); } // ─────────────────────────────────────────────────────────────────────────