fix: freeze et deformation lors de la prise de la photo (rotation)
This commit is contained in:
@@ -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<CaptureScreen>
|
||||
ParallelismData? _parallelismData;
|
||||
StreamSubscription<ParallelismData>? _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<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.
|
||||
// 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<CaptureScreen>
|
||||
/// Baisser `maxSide` (ex. 900) ou `quality` (ex. 60) rend le chargement
|
||||
/// encore plus rapide au prix d'un peu de finesse à l'écran.
|
||||
Future<String> _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;
|
||||
});
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user