diff --git a/lib/features/analysis/analysis_provider.dart b/lib/features/analysis/analysis_provider.dart index 81031a9e..e74ab727 100644 --- a/lib/features/analysis/analysis_provider.dart +++ b/lib/features/analysis/analysis_provider.dart @@ -363,47 +363,6 @@ class AnalysisProvider extends ChangeNotifier { return detectedImpacts.length; } - /// Auto-detect impacts using YOLOv8 model - Future autoDetectImpactsWithYOLO({bool clearExisting = false}) async { - if (_imagePath == null || _targetType == null) return 0; - - try { - final detectedImpacts = await _detectionService.detectImpactsWithYOLO( - _imagePath!, - _targetType!, - _targetCenterX, - _targetCenterY, - _targetRadius, - _ringCount, - ); - - if (clearExisting) { - _shots.clear(); - } - - // Add detected impacts as shots - for (final impact in detectedImpacts) { - final shot = Shot( - id: _uuid.v4(), - x: impact.x, - y: impact.y, - score: impact.suggestedScore, - sessionId: '', - ); - _shots.add(shot); - } - - _recalculateScores(); - _recalculateGrouping(); - notifyListeners(); - - return detectedImpacts.length; - } catch (e) { - print('Error in YOLO auto-detection: $e'); - return 0; - } - } - /// Detect impacts with OpenCV using reference points Future detectFromReferencesWithOpenCV({ double tolerance = 2.0, diff --git a/lib/features/analysis/analysis_screen.dart b/lib/features/analysis/analysis_screen.dart index 8cd72952..485bdbb6 100644 --- a/lib/features/analysis/analysis_screen.dart +++ b/lib/features/analysis/analysis_screen.dart @@ -1050,72 +1050,6 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - // YOLO option button - Card( - color: AppTheme.primaryColor.withAlpha(25), - child: ListTile( - leading: const Icon(Icons.psychology, color: AppTheme.primaryColor), - title: const Text('IA Detection (YOLOv8)', style: TextStyle(fontWeight: FontWeight.bold)), - subtitle: const Text('Détection intelligente via modèle entraîné'), - onTap: () async { - Navigator.pop(context); - - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Row( - children: [ - SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator( - strokeWidth: 2, - color: Colors.white, - ), - ), - SizedBox(width: 12), - Text('Détection IA en cours...'), - ], - ), - duration: Duration(seconds: 10), - ), - ); - - final count = await provider.autoDetectImpactsWithYOLO( - clearExisting: clearExisting, - ); - - if (context.mounted) { - ScaffoldMessenger.of(context).hideCurrentSnackBar(); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - count > 0 - ? '$count impact(s) détecté(s) par l\'IA' - : 'Aucun impact détecté par l\'IA.', - ), - backgroundColor: count > 0 - ? AppTheme.successColor - : AppTheme.warningColor, - ), - ); - } - }, - ), - ), - - const Padding( - padding: EdgeInsets.symmetric(vertical: 12), - child: Row( - children: [ - Expanded(child: Divider()), - Padding( - padding: EdgeInsets.symmetric(horizontal: 8), - child: Text('OU', style: TextStyle(color: Colors.grey, fontSize: 12)), - ), - Expanded(child: Divider()), - ], - ), - ), const Text( 'Détection Classique (Paramétrable):', diff --git a/lib/main.dart b/lib/main.dart index 3f80ed52..220190d8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,7 +10,6 @@ import 'services/target_detection_service.dart'; import 'services/score_calculator_service.dart'; import 'services/grouping_analyzer_service.dart'; import 'services/image_processing_service.dart'; -import 'services/yolo_impact_detection_service.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -34,13 +33,9 @@ void main() async { Provider( create: (_) => ImageProcessingService(), ), - Provider( - create: (_) => YOLOImpactDetectionService(), - ), Provider( create: (context) => TargetDetectionService( imageProcessingService: context.read(), - yoloService: context.read(), ), ), Provider( diff --git a/lib/services/target_detection_service.dart b/lib/services/target_detection_service.dart index 75541448..a0fe6470 100644 --- a/lib/services/target_detection_service.dart +++ b/lib/services/target_detection_service.dart @@ -2,8 +2,6 @@ import 'dart:math' as math; import '../data/models/target_type.dart'; import 'image_processing_service.dart'; import 'opencv_impact_detection_service.dart'; -import 'yolo_impact_detection_service.dart'; - export 'image_processing_service.dart' show ImpactDetectionSettings, ReferenceImpact, ImpactCharacteristics; export 'opencv_impact_detection_service.dart' @@ -55,16 +53,13 @@ class DetectedImpactResult { class TargetDetectionService { final ImageProcessingService _imageProcessingService; final OpenCVImpactDetectionService _opencvService; - final YOLOImpactDetectionService _yoloService; TargetDetectionService({ ImageProcessingService? imageProcessingService, OpenCVImpactDetectionService? opencvService, - YOLOImpactDetectionService? yoloService, }) : _imageProcessingService = imageProcessingService ?? ImageProcessingService(), - _opencvService = opencvService ?? OpenCVImpactDetectionService(), - _yoloService = yoloService ?? YOLOImpactDetectionService(); + _opencvService = opencvService ?? OpenCVImpactDetectionService(); /// Detect target and impacts from an image file TargetDetectionResult detectTarget(String imagePath, TargetType targetType) { @@ -379,45 +374,4 @@ class TargetDetectionService { } } - /// Détecte les impacts en utilisant YOLOv8 - Future> detectImpactsWithYOLO( - String imagePath, - TargetType targetType, - double centerX, - double centerY, - double radius, - int ringCount, - ) async { - try { - final impacts = await _yoloService.detectImpacts(imagePath); - - return impacts.map((impact) { - // Use YOLO-detected score (valeur) if available, otherwise calculate it - int score = impact.suggestedScore; - - if (score <= 0) { - score = targetType == TargetType.concentric - ? _calculateConcentricScoreWithRings( - impact.x, - impact.y, - centerX, - centerY, - radius, - ringCount, - ) - : _calculateSilhouetteScore(impact.x, impact.y, centerX, centerY); - } - - return DetectedImpactResult( - x: impact.x, - y: impact.y, - radius: impact.radius, - suggestedScore: score, - ); - }).toList(); - } catch (e) { - print('Erreur détection YOLOv8: $e'); - return []; - } - } } diff --git a/lib/services/yolo_impact_detection_service.dart b/lib/services/yolo_impact_detection_service.dart deleted file mode 100644 index ae24a7fa..00000000 --- a/lib/services/yolo_impact_detection_service.dart +++ /dev/null @@ -1,217 +0,0 @@ -import 'dart:io'; -import 'dart:math' as math; -import 'dart:typed_data'; -import 'package:tflite_flutter/tflite_flutter.dart'; -import 'package:image/image.dart' as img; -import 'target_detection_service.dart'; - -class YOLOImpactDetectionService { - Interpreter? _interpreter; - int _inputSize = 640; - List _outputShape = [1, 17, 8400]; - int _numClasses = 13; - - static const String modelPath = 'assets/models/yolov8n_32.tflite'; - static const String labelsPath = 'assets/models/labels.txt'; - - Future init() async { - if (_interpreter != null) return; - - try { - _interpreter = await Interpreter.fromAsset(modelPath); - - // Get model metadata - final inputTensors = _interpreter!.getInputTensors(); - if (inputTensors.isNotEmpty) { - // [1, 640, 640, 3] or [1, 3, 640, 640] - final shape = inputTensors[0].shape; - if (shape.length == 4) { - _inputSize = shape[1] == 3 ? shape[2] : shape[1]; - } - } - - final outputTensors = _interpreter!.getOutputTensors(); - if (outputTensors.isNotEmpty) { - _outputShape = outputTensors[0].shape; - // Output is usually [1, 4 + num_classes, num_boxes] - if (_outputShape.length == 3) { - _numClasses = _outputShape[1] - 4; - } - } - - print('YOLO Interpreter loaded successfully'); - print('Model Input Size: $_inputSize'); - print('Model Output Shape: $_outputShape (Classes: $_numClasses)'); - } catch (e) { - print('Error loading YOLO model: $e'); - } - } - - Future> detectImpacts(String imagePath) async { - if (_interpreter == null) await init(); - if (_interpreter == null) return []; - - try { - final bytes = File(imagePath).readAsBytesSync(); - final decodedImage = img.decodeImage(bytes); - if (decodedImage == null) return []; - - final originalImage = img.bakeOrientation(decodedImage); - - final resizedImage = img.copyResize( - originalImage, - width: _inputSize, - height: _inputSize, - ); - - var input = _imageToByteListFloat32(resizedImage, _inputSize); - - // Allocate output buffer dynamically - var output = List.filled( - _outputShape.fold(1, (a, b) => a * b), - 0 - ).reshape(_outputShape); - - _interpreter!.run(input, output); - - final results = _processOutput( - output[0], - originalImage.width, - originalImage.height, - ); - - print('YOLO Detection result count: ${results.length}'); - return results; - } catch (e) { - print('Error during YOLO inference: $e'); - return []; - } - } - - List _processOutput( - List> output, - int imgWidth, - int imgHeight, - ) { - final List<_Detection> candidates = []; - const double threshold = 0.25; - - // output is typically [4 + numClasses, numBoxes] - final int rows = output.length; - final int numBoxes = output[0].length; - - for (int i = 0; i < numBoxes; i++) { - double maxConfidence = 0; - int bestClass = 4; - - // Find best class among all classes (indices 4 to rows-1) - for (int c = 4; c < rows; c++) { - if (output[c][i] > maxConfidence) { - maxConfidence = output[c][i]; - bestClass = c; - } - } - - if (maxConfidence > threshold) { - final int classIndex = bestClass - 4; - - candidates.add( - _Detection( - // Use dynamic mapping: 0,1,2,3 are typically x,y,w,h - // We'll keep the current mapping for now as it matches user's previous model - y: output[0][i], - x: output[1][i], - w: output[2][i], - h: output[3][i], - confidence: maxConfidence, - classIndex: classIndex, - ), - ); - } - } - - final List<_Detection> suppressed = _nms(candidates); - - return suppressed - .map( - (det) { - // Score = ClassIndex (0 to 10) for impact models - int score = det.classIndex; - - return DetectedImpactResult( - x: det.x, - y: det.y, - radius: 5.0, - suggestedScore: score, - ); - }, - ) - .toList(); - } - - List<_Detection> _nms(List<_Detection> detections) { - if (detections.isEmpty) return []; - - detections.sort((a, b) => b.confidence.compareTo(a.confidence)); - - final List<_Detection> selected = []; - final List active = List.filled(detections.length, true); - - for (int i = 0; i < detections.length; i++) { - if (!active[i]) continue; - - selected.add(detections[i]); - - for (int j = i + 1; j < detections.length; j++) { - if (!active[j]) continue; - - if (_iou(detections[i], detections[j]) > 0.45) { - active[j] = false; - } - } - } - - return selected; - } - - double _iou(_Detection a, _Detection b) { - final double areaA = a.w * a.h; - final double areaB = b.w * b.h; - - final double x1 = math.max(a.x - a.w / 2, b.x - b.w / 2); - final double y1 = math.max(a.y - a.h / 2, b.y - b.h / 2); - final double x2 = math.min(a.x + a.w / 2, b.x + b.w / 2); - final double y2 = math.min(a.y + a.h / 2, b.y + b.h / 2); - - final double intersection = math.max(0.0, x2 - x1) * math.max(0.0, y2 - y1); - return intersection / (areaA + areaB - intersection); - } - - Uint8List _imageToByteListFloat32(img.Image image, int inputSize) { - var convertedBytes = Float32List(1 * inputSize * inputSize * 3); - var buffer = Float32List.view(convertedBytes.buffer); - int pixelIndex = 0; - for (int i = 0; i < inputSize; i++) { - for (int j = 0; j < inputSize; j++) { - var pixel = image.getPixel(j, i); - buffer[pixelIndex++] = (pixel.r / 255.0); - buffer[pixelIndex++] = (pixel.g / 255.0); - buffer[pixelIndex++] = (pixel.b / 255.0); - } - } - return convertedBytes.buffer.asUint8List(); - } -} - -class _Detection { - final double x, y, w, h, confidence; - final int classIndex; - _Detection({ - required this.x, - required this.y, - required this.w, - required this.h, - required this.confidence, - required this.classIndex, - }); -} diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 3a71460f..2db3c22a 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -7,7 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST - tflite_flutter ) set(PLUGIN_BUNDLED_LIBRARIES) diff --git a/pubspec.lock b/pubspec.lock index 1a439698..a76930fb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -536,14 +536,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" - quiver: - dependency: transitive - description: - name: quiver - sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2 - url: "https://pub.dev" - source: hosted - version: "3.2.2" sky_engine: dependency: transitive description: flutter @@ -661,14 +653,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.9" - tflite_flutter: - dependency: "direct main" - description: - name: tflite_flutter - sha256: "48e6fde2ad97162bb66a16a142f4c4698add9e8cd397ce9d1cc7451b55537ac1" - url: "https://pub.dev" - source: hosted - version: "0.11.0" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b52ede12..3ba046da 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -65,7 +65,7 @@ dependencies: image: ^4.1.7 # Machine Learning for YOLOv8 - tflite_flutter: ^0.11.0 + # tflite_flutter: ^0.11.0 dev_dependencies: flutter_test: @@ -90,8 +90,8 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - assets: - - assets/models/yolov8n_32.tflite + # assets: + # - assets/models/yolov8n_32.tflite # - images/a_dot_burr.jpeg # - images/a_dot_ham.jpeg diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 00cdb12c..a423a024 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -7,7 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST ) list(APPEND FLUTTER_FFI_PLUGIN_LIST - tflite_flutter ) set(PLUGIN_BUNDLED_LIBRARIES)