fix: legere baisse resolution pour augmenter fluidité de l appli

This commit is contained in:
qguillaume
2026-06-06 00:26:38 +02:00
parent 13cf5b70e0
commit 18e591f3fc
2 changed files with 44 additions and 154 deletions

View File

@@ -17,7 +17,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';
@@ -37,8 +36,8 @@ class _CaptureScreenState extends State<CaptureScreen>
// NOUVEAU : Service IMU de parallélisme
final ParallelismService _parallelismService = ParallelismService(
alignThreshold: 25.0, // Seuil pour passer au vert (permissif)
misalignThreshold: 32.0, // Seuil pour repasser à l'orange
alignThreshold: 15.0, // Seuil pour passer au vert
misalignThreshold: 22.0, // Seuil pour repasser à l'orange
);
String? _selectedImagePath;
@@ -54,9 +53,8 @@ class _CaptureScreenState extends State<CaptureScreen>
late AnimationController _scanAnimationController;
late Animation<double> _scanAnimation;
// Détection OpenCV (cible circulaire) — on garde le résultat COMPLET
// Détection OpenCV (inchangée)
bool? _alignmentStatus;
TargetDetectionResult? _targetResult; // NOUVEAU : centre + rayon de la cible
Timer? _detectionTimer;
bool _isAnalyzingFrame = false;
@@ -64,9 +62,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();
@@ -114,43 +109,25 @@ class _CaptureScreenState extends State<CaptureScreen>
}
// ─────────────────────────────────────────────────────────────────────────
// Calcule la couleur et le message depuis l'IMU + la détection de cible
// NOUVEAU : Calcule la couleur et le message depuis les données IMU
// ─────────────────────────────────────────────────────────────────────────
/// La cible circulaire est-elle détectée et raisonnablement cadrée ?
bool get _targetReady {
final t = _targetResult;
if (t == null || !t.success) return false;
final bool centered =
t.centerX > 0.15 && t.centerX < 0.85 && t.centerY > 0.15 && t.centerY < 0.85;
final bool bigEnough = t.radius > 0.12;
return centered && bigEnough;
}
/// Couleur du cadre : dépend UNIQUEMENT du parallélisme IMU.
/// (La détection de cible sert seulement à dessiner le cercle, elle ne
/// bloque jamais le passage au vert.)
/// Couleur du cadre selon le parallélisme détecté par l'IMU.
Color get _frameColor {
final bool imuAligned =
_parallelismData == null || _parallelismData!.isAligned;
return imuAligned ? const Color(0xFF00FF00) : Colors.orange;
if (_parallelismData == null) return const Color(0xFF00FF00);
return _parallelismData!.isAligned ? const Color(0xFF00FF00) : Colors.orange;
}
/// Message d'aide selon le parallélisme IMU.
/// Message d'aide selon le parallélisme détecté.
String get _alignmentMessage {
if (_parallelismData == null ||
_parallelismData!.status == ParallelismStatus.unknown) {
if (_parallelismData == null) {
return 'ALIGNEZ LA CIBLE DANS LE CADRE';
}
// Aligné → message de validation (avec bonus si la cible est détectée)
if (_parallelismData!.isAligned) {
return _targetReady
? 'PARFAIT — CIBLE DÉTECTÉE, PRÊT'
: 'PARALLÈLE OK — PRÊT À PHOTOGRAPHIER';
if (_parallelismData!.status == ParallelismStatus.aligned) {
return 'PARALLÈLE OK — PRÊT À PHOTOGRAPHIER';
}
// Mal aligné → message directif selon l'axe le plus dévié
// Message directif selon l'axe le plus dévié
final double pitch = _parallelismData!.pitchDegrees;
final double roll = _parallelismData!.rollDegrees;
@@ -202,7 +179,6 @@ class _CaptureScreenState extends State<CaptureScreen>
_isCameraInitialized = true;
_showLiveCamera = true;
_alignmentStatus = null;
_targetResult = null; // reset détection cible
_parallelismData = null; // NOUVEAU : reset IMU
});
@@ -228,9 +204,8 @@ class _CaptureScreenState extends State<CaptureScreen>
_cameraController!.startImageStream((CameraImage cameraImage) async {
if (_isAnalyzingFrame) return;
final now = DateTime.now();
// Cadence ~1 s : assez réactif pour suivre la cible sans saturer le CPU.
if (_lastAnalysis != null &&
now.difference(_lastAnalysis!).inMilliseconds < 1000) return;
now.difference(_lastAnalysis!).inSeconds < 3) return;
_lastAnalysis = now;
_isAnalyzingFrame = true;
@@ -254,9 +229,6 @@ class _CaptureScreenState extends State<CaptureScreen>
if (mounted && _showLiveCamera) {
setState(() {
// On garde le résultat complet pour dessiner le cercle.
_targetResult = result.success ? result : null;
if (!result.success) {
_alignmentStatus = null;
} else {
@@ -286,7 +258,6 @@ class _CaptureScreenState extends State<CaptureScreen>
}
} catch (_) {}
_alignmentStatus = null;
_targetResult = null;
}
img.Image? _convertCameraImage(CameraImage cameraImage) {
@@ -468,21 +439,6 @@ class _CaptureScreenState extends State<CaptureScreen>
),
),
// 1.bis Cercle dessiné autour de la cible circulaire détectée
if (_targetResult != null && _targetResult!.success)
Center(
child: AspectRatio(
aspectRatio: 3 / 4,
child: CustomPaint(
painter: _TargetCirclePainter(
target: _targetResult!,
color: _targetReady ? frameColor : Colors.white,
highlighted: _targetReady,
),
),
),
),
// 2. Cadre de visée avec coins et mire centrale
Center(
child: Container(
@@ -753,39 +709,8 @@ 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 = finalPath;
_selectedImagePath = photo.path;
_showLiveCamera = false;
});
@@ -891,69 +816,4 @@ class _CaptureScreenState extends State<CaptureScreen>
),
);
}
}
// ─────────────────────────────────────────────────────────────────────────
// Painter qui dessine un cercle autour de la cible circulaire détectée.
// Le centre et le rayon viennent d'OpenCV (coordonnées normalisées 0..1).
// Blanc tant que la cible n'est pas bien cadrée, couleur d'état sinon.
// ─────────────────────────────────────────────────────────────────────────
class _TargetCirclePainter extends CustomPainter {
final TargetDetectionResult target;
final Color color;
final bool highlighted;
_TargetCirclePainter({
required this.target,
required this.color,
required this.highlighted,
});
@override
void paint(Canvas canvas, Size size) {
// Centre en pixels dans la zone de dessin
final double cx = target.centerX * size.width;
final double cy = target.centerY * size.height;
// radius est normalisé par min(largeur, hauteur) côté OpenCV
final double r = target.radius * math.min(size.width, size.height);
final Paint stroke = Paint()
..style = PaintingStyle.stroke
..strokeWidth = highlighted ? 3.5 : 2.0
..color = color;
// Cercle principal autour de la cible
canvas.drawCircle(Offset(cx, cy), r, stroke);
// Petite croix au centre détecté
final Paint cross = Paint()
..strokeWidth = 2.0
..color = color;
const double k = 10;
canvas.drawLine(Offset(cx - k, cy), Offset(cx + k, cy), cross);
canvas.drawLine(Offset(cx, cy - k), Offset(cx, cy + k), cross);
// Étiquette "CIBLE" quand elle est bien cadrée
if (highlighted) {
final tp = TextPainter(
text: TextSpan(
text: ' CIBLE ',
style: TextStyle(
color: Colors.black,
fontSize: 12,
fontWeight: FontWeight.bold,
backgroundColor: color,
),
),
textDirection: TextDirection.ltr,
)..layout();
tp.paint(canvas, Offset(cx - tp.width / 2, (cy - r - 20).clamp(0.0, size.height)));
}
}
@override
bool shouldRepaint(_TargetCirclePainter old) =>
old.target != target ||
old.color != color ||
old.highlighted != highlighted;
}