Compare commits
2 Commits
v.0.0.3
...
105eb1cab0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
105eb1cab0 | ||
|
|
fba3b41f2f |
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"avdmanager.executable": "c:\\Users\\streaper2\\AppData\\Local\\Android\\Sdk\\cmdline-tools\\latest\\bin\\avdmanager.bat",
|
||||
"avdmanager.sdkManager": "c:\\Users\\streaper2\\AppData\\Local\\Android\\Sdk\\cmdline-tools\\latest\\bin\\sdkmanager.bat"
|
||||
}
|
||||
BIN
analyze_output.txt
Normal file
BIN
analyze_output.txt
Normal file
Binary file not shown.
BIN
analyze_results_final.txt
Normal file
BIN
analyze_results_final.txt
Normal file
Binary file not shown.
37
analyze_results_fixed.txt
Normal file
37
analyze_results_fixed.txt
Normal file
@@ -0,0 +1,37 @@
|
||||
Analyzing bully...
|
||||
|
||||
info - Don't invoke 'print' in production code - lib\features\analysis\analysis_provider.dart:553:7 - avoid_print
|
||||
info - The private field _selectedType could be 'final' - lib\features\capture\capture_screen.dart:27:14 - prefer_final_fields
|
||||
info - 'scale' is deprecated and shouldn't be used. Use scaleByVector3, scaleByVector4, or scaleByDouble instead - lib\features\crop\crop_screen.dart:142:25 - deprecated_member_use
|
||||
info - Statements in an if should be enclosed in a block - lib\services\distortion_correction_service.dart:566:11 - curly_braces_in_flow_control_structures
|
||||
info - Don't invoke 'print' in production code - lib\services\distortion_correction_service.dart:639:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\distortion_correction_service.dart:764:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\distortion_correction_service.dart:825:9 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\distortion_correction_service.dart:953:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\distortion_correction_service.dart:1015:9 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\distortion_correction_service.dart:1063:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:192:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:239:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:246:9 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:278:9 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:289:11 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:292:11 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:297:9 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:332:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:336:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:683:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:725:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:736:7 - avoid_print
|
||||
warning - The declaration '_detectDarkSpotsAdaptive' isn't referenced - lib\services\image_processing_service.dart:780:15 - unused_element
|
||||
info - Don't invoke 'print' in production code - lib\services\target_detection_service.dart:328:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\target_detection_service.dart:377:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\target_detection_service.dart:414:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\yolo_impact_detection_service.dart:23:9 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\yolo_impact_detection_service.dart:29:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\yolo_impact_detection_service.dart:31:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\yolo_impact_detection_service.dart:67:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - tests\opencv_quad_test.dart:4:3 - avoid_print
|
||||
info - Don't invoke 'print' in production code - tests\opencv_quad_test.dart:5:3 - avoid_print
|
||||
info - Don't invoke 'print' in production code - tests\opencv_quad_test.dart:6:3 - avoid_print
|
||||
info - Don't invoke 'print' in production code - tests\test_homography.dart:4:3 - avoid_print
|
||||
|
||||
38
analyze_utf8.txt
Normal file
38
analyze_utf8.txt
Normal file
@@ -0,0 +1,38 @@
|
||||
Analyzing bully...
|
||||
|
||||
info - Don't invoke 'print' in production code - lib\features\analysis\analysis_provider.dart:392:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\features\analysis\analysis_provider.dart:596:7 - avoid_print
|
||||
info - The private field _selectedType could be 'final' - lib\features\capture\capture_screen.dart:27:14 - prefer_final_fields
|
||||
info - 'scale' is deprecated and shouldn't be used. Use scaleByVector3, scaleByVector4, or scaleByDouble instead - lib\features\crop\crop_screen.dart:142:25 - deprecated_member_use
|
||||
info - Statements in an if should be enclosed in a block - lib\services\distortion_correction_service.dart:566:11 - curly_braces_in_flow_control_structures
|
||||
info - Don't invoke 'print' in production code - lib\services\distortion_correction_service.dart:639:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\distortion_correction_service.dart:764:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\distortion_correction_service.dart:825:9 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\distortion_correction_service.dart:953:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\distortion_correction_service.dart:1015:9 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\distortion_correction_service.dart:1063:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:192:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:239:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:246:9 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:278:9 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:289:11 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:292:11 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:297:9 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:332:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:336:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:683:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:725:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\image_processing_service.dart:736:7 - avoid_print
|
||||
warning - The declaration '_detectDarkSpotsAdaptive' isn't referenced - lib\services\image_processing_service.dart:780:15 - unused_element
|
||||
info - Don't invoke 'print' in production code - lib\services\target_detection_service.dart:328:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\target_detection_service.dart:377:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\target_detection_service.dart:414:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\yolo_impact_detection_service.dart:23:9 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\yolo_impact_detection_service.dart:29:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\yolo_impact_detection_service.dart:31:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - lib\services\yolo_impact_detection_service.dart:67:7 - avoid_print
|
||||
info - Don't invoke 'print' in production code - tests\opencv_quad_test.dart:4:3 - avoid_print
|
||||
info - Don't invoke 'print' in production code - tests\opencv_quad_test.dart:5:3 - avoid_print
|
||||
info - Don't invoke 'print' in production code - tests\opencv_quad_test.dart:6:3 - avoid_print
|
||||
info - Don't invoke 'print' in production code - tests\test_homography.dart:4:3 - avoid_print
|
||||
|
||||
@@ -35,6 +35,7 @@ android {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
7
android/app/proguard-rules.pro
vendored
Normal file
7
android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# TensorFlow Lite
|
||||
-keep class org.tensorflow.lite.** { *; }
|
||||
-keep class com.google.android.gms.tflite.** { *; }
|
||||
-dontwarn org.tensorflow.lite.**
|
||||
|
||||
# Specifically for the GPU delegate error
|
||||
-dontwarn org.tensorflow.lite.gpu.GpuDelegateFactory$Options
|
||||
@@ -223,6 +223,16 @@ class AnalysisProvider extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Update a shot's score manually
|
||||
void updateShotScore(String shotId, int newScore) {
|
||||
final index = _shots.indexWhere((shot) => shot.id == shotId);
|
||||
if (index == -1) return;
|
||||
|
||||
_shots[index] = _shots[index].copyWith(score: newScore);
|
||||
_recalculateScores();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Auto-detect impacts using image processing
|
||||
Future<int> autoDetectImpacts({
|
||||
int darkThreshold = 80,
|
||||
@@ -293,6 +303,8 @@ class AnalysisProvider extends ChangeNotifier {
|
||||
double param2 = 30,
|
||||
int minRadius = 5,
|
||||
int maxRadius = 50,
|
||||
int minSize = 5,
|
||||
int maxSize = 1000,
|
||||
int blurSize = 5,
|
||||
bool useContourDetection = true,
|
||||
double minCircularity = 0.6,
|
||||
|
||||
@@ -274,68 +274,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// Auto-calibrate button
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () async {
|
||||
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('Auto-calibration en cours...'),
|
||||
],
|
||||
),
|
||||
duration: Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
|
||||
final success = await provider
|
||||
.autoCalibrateTarget();
|
||||
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).hideCurrentSnackBar();
|
||||
if (success) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Cible calibrée automatiquement',
|
||||
),
|
||||
backgroundColor: AppTheme.successColor,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Échec de la calibration auto',
|
||||
),
|
||||
backgroundColor: AppTheme.errorColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.auto_fix_high),
|
||||
label: const Text('Auto-Calibrer la Cible'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.deepPurple,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Ring count slider
|
||||
Row(
|
||||
children: [
|
||||
@@ -387,57 +326,6 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
),
|
||||
],
|
||||
),
|
||||
// Target size slider
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.zoom_out_map,
|
||||
color: Colors.white,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'Taille:',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value: provider.targetRadius.clamp(0.05, 3.0),
|
||||
min: 0.05,
|
||||
max: 3.0,
|
||||
label:
|
||||
'${(provider.targetRadius * 100).toStringAsFixed(0)}%',
|
||||
activeColor: AppTheme.warningColor,
|
||||
onChanged: (value) {
|
||||
provider.adjustTargetPosition(
|
||||
provider.targetCenterX,
|
||||
provider.targetCenterY,
|
||||
provider.targetInnerRadius,
|
||||
value,
|
||||
ringCount: provider.ringCount,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppTheme.warningColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
'${(provider.targetRadius * 100).toStringAsFixed(0)}%',
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(color: Colors.white24, height: 16),
|
||||
// Distortion correction row
|
||||
/*Row(
|
||||
@@ -793,6 +681,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
),
|
||||
// Overlay qui se transforme avec l'image
|
||||
TargetOverlay(
|
||||
showRings: true,
|
||||
shots: provider.shots,
|
||||
targetCenterX: provider.targetCenterX,
|
||||
targetCenterY: provider.targetCenterY,
|
||||
@@ -879,6 +768,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
),
|
||||
// Overlay qui se transforme avec l'image
|
||||
TargetOverlay(
|
||||
showRings: true,
|
||||
shots: provider.shots,
|
||||
targetCenterX: provider.targetCenterX,
|
||||
targetCenterY: provider.targetCenterY,
|
||||
@@ -991,107 +881,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
}
|
||||
|
||||
Widget _buildActionButtons(BuildContext context, AnalysisProvider provider) {
|
||||
return Column(
|
||||
children: [
|
||||
// Reference-based detection section
|
||||
if (_isSelectingReferences) ...[
|
||||
Card(
|
||||
color: Colors.deepPurple.withValues(alpha: 0.1),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.touch_app, color: Colors.deepPurple),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'${provider.referenceImpacts.length} reference(s) selectionnee(s)',
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
'Touchez l\'image pour marquer 3-4 impacts de reference. '
|
||||
'L\'algorithme apprendra leurs caracteristiques pour detecter les autres.',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton(
|
||||
onPressed: () {
|
||||
setState(() => _isSelectingReferences = false);
|
||||
provider.clearReferenceImpacts();
|
||||
},
|
||||
child: const Text('Annuler'),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: provider.referenceImpacts.length >= 2
|
||||
? () => _showCalibratedDetectionDialog(
|
||||
context,
|
||||
provider,
|
||||
)
|
||||
: null,
|
||||
icon: const Icon(Icons.auto_fix_high),
|
||||
label: const Text('Detecter'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.deepPurple,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
] else ...[
|
||||
// désactiver le temps de l'amelioration du scripts d'auto-detection
|
||||
// Auto-detect buttons row
|
||||
// Row(
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: ElevatedButton.icon(
|
||||
// onPressed: () => _showAutoDetectDialog(context, provider),
|
||||
// icon: const Icon(Icons.auto_fix_high),
|
||||
// label: const Text('Auto-Detection'),
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// backgroundColor: AppTheme.primaryColor,
|
||||
// foregroundColor: Colors.white,
|
||||
// padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(width: 12),
|
||||
// Expanded(
|
||||
// child: ElevatedButton.icon(
|
||||
// onPressed: () => setState(() => _isSelectingReferences = true),
|
||||
// icon: const Icon(Icons.touch_app),
|
||||
// label: const Text('Par Reference'),
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// backgroundColor: Colors.deepPurple,
|
||||
// foregroundColor: Colors.white,
|
||||
// padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
|
||||
// Manual actions
|
||||
],
|
||||
);
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
void _showHelpDialog(BuildContext context) {
|
||||
@@ -1129,21 +919,68 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
AnalysisProvider provider,
|
||||
String shotId,
|
||||
) {
|
||||
final shot = provider.shots.firstWhere((s) => s.id == shotId);
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||||
),
|
||||
builder: (context) => SafeArea(
|
||||
child: Wrap(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.delete, color: AppTheme.errorColor),
|
||||
title: const Text('Supprimer cet impact'),
|
||||
onTap: () {
|
||||
provider.removeShot(shotId);
|
||||
Text(
|
||||
'Modifier l\'impact',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Dropdown for score
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text('Valeur de l\'impact : ', style: TextStyle(fontSize: 16)),
|
||||
const SizedBox(width: 10),
|
||||
DropdownButton<int>(
|
||||
value: shot.score.clamp(0, 10),
|
||||
items: List.generate(11, (i) => i).map((i) {
|
||||
return DropdownMenuItem<int>(
|
||||
value: i,
|
||||
child: Text(i == 10 && provider.targetType == TargetType.concentric ? '10 (X)' : '$i'),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (newScore) {
|
||||
if (newScore != null) {
|
||||
provider.updateShotScore(shotId, newScore);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Delete button at the bottom
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
provider.removeShot(shotId);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
icon: const Icon(Icons.delete),
|
||||
label: const Text('SUPPRIMER L\'IMPACT'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppTheme.errorColor,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -1187,7 +1024,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
void _showAutoDetectDialog(BuildContext context, AnalysisProvider provider) {
|
||||
// Detection settings
|
||||
bool clearExisting = true;
|
||||
@@ -1197,9 +1034,6 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
int maxImpactSize = 500;
|
||||
double minFillRatio = 0.5;
|
||||
|
||||
// NOTE: OpenCV désactivé - problèmes de build Windows
|
||||
// Utilisation de la détection classique uniquement
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => StatefulBuilder(
|
||||
@@ -1216,11 +1050,12 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
const Text(
|
||||
'Ajustez les parametres de detection:',
|
||||
'Détection Classique (Paramétrable):',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Dark threshold slider
|
||||
Text('Seuil de detection (zones sombres): $darkThreshold'),
|
||||
@@ -1379,14 +1214,13 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> {
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.search),
|
||||
label: const Text('Detecter'),
|
||||
label: const Text('Détecter (Classique)'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
*/
|
||||
|
||||
void _showCalibratedDetectionDialog(
|
||||
BuildContext context,
|
||||
|
||||
@@ -25,6 +25,7 @@ class TargetOverlay extends StatelessWidget {
|
||||
final double? groupingDiameter;
|
||||
final List<Shot>? referenceImpacts;
|
||||
final double zoomScale;
|
||||
final bool showRings;
|
||||
|
||||
const TargetOverlay({
|
||||
super.key,
|
||||
@@ -42,6 +43,7 @@ class TargetOverlay extends StatelessWidget {
|
||||
this.groupingDiameter,
|
||||
this.referenceImpacts,
|
||||
this.zoomScale = 1.0,
|
||||
this.showRings = false,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -72,6 +74,7 @@ class TargetOverlay extends StatelessWidget {
|
||||
groupingDiameter: groupingDiameter,
|
||||
referenceImpacts: referenceImpacts,
|
||||
zoomScale: zoomScale,
|
||||
showRings: showRings,
|
||||
),
|
||||
child: Stack(
|
||||
children: shots.map((shot) {
|
||||
@@ -132,6 +135,7 @@ class _TargetOverlayPainter extends CustomPainter {
|
||||
final double? groupingDiameter;
|
||||
final List<Shot>? referenceImpacts;
|
||||
final double zoomScale;
|
||||
final bool showRings;
|
||||
|
||||
_TargetOverlayPainter({
|
||||
required this.shots,
|
||||
@@ -146,12 +150,15 @@ class _TargetOverlayPainter extends CustomPainter {
|
||||
this.groupingDiameter,
|
||||
this.referenceImpacts,
|
||||
this.zoomScale = 1.0,
|
||||
this.showRings = false,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
// Draw target center indicator
|
||||
if (showRings) {
|
||||
_drawTargetCenter(canvas, size);
|
||||
}
|
||||
|
||||
// Draw grouping circle
|
||||
if (groupingCenterX != null && groupingCenterY != null && groupingDiameter != null && shots.length > 1) {
|
||||
@@ -371,6 +378,7 @@ class _TargetOverlayPainter extends CustomPainter {
|
||||
groupingCenterY != oldDelegate.groupingCenterY ||
|
||||
groupingDiameter != oldDelegate.groupingDiameter ||
|
||||
referenceImpacts != oldDelegate.referenceImpacts ||
|
||||
zoomScale != oldDelegate.zoomScale;
|
||||
zoomScale != oldDelegate.zoomScale ||
|
||||
showRings != oldDelegate.showRings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ImageProcessingService>(
|
||||
create: (_) => ImageProcessingService(),
|
||||
),
|
||||
Provider<YOLOImpactDetectionService>(
|
||||
create: (_) => YOLOImpactDetectionService(),
|
||||
),
|
||||
Provider<TargetDetectionService>(
|
||||
create: (context) => TargetDetectionService(
|
||||
imageProcessingService: context.read<ImageProcessingService>(),
|
||||
yoloService: context.read<YOLOImpactDetectionService>(),
|
||||
),
|
||||
),
|
||||
Provider<ScoreCalculatorService>(
|
||||
|
||||
@@ -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,40 +374,4 @@ class TargetDetectionService {
|
||||
}
|
||||
}
|
||||
|
||||
/// Détecte les impacts en utilisant YOLOv8
|
||||
Future<List<DetectedImpactResult>> 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) {
|
||||
final 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 [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,174 +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;
|
||||
|
||||
static const String modelPath = 'assets/models/yolov11n_impact.tflite';
|
||||
static const String labelsPath = 'assets/models/labels.txt';
|
||||
|
||||
Future<void> init() async {
|
||||
if (_interpreter != null) return;
|
||||
|
||||
try {
|
||||
// Try loading the specific YOLOv11 model first, fallback to v8 if not found
|
||||
try {
|
||||
_interpreter = await Interpreter.fromAsset(modelPath);
|
||||
} catch (e) {
|
||||
print('YOLOv11 model not found at $modelPath, trying YOLOv8 fallback');
|
||||
_interpreter = await Interpreter.fromAsset(
|
||||
'assets/models/yolov8n_impact.tflite',
|
||||
);
|
||||
}
|
||||
|
||||
print('YOLO Interpreter loaded successfully');
|
||||
} catch (e) {
|
||||
print('Error loading YOLO model: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<DetectedImpactResult>> detectImpacts(String imagePath) async {
|
||||
if (_interpreter == null) await init();
|
||||
if (_interpreter == null) return [];
|
||||
|
||||
try {
|
||||
final bytes = File(imagePath).readAsBytesSync();
|
||||
final originalImage = img.decodeImage(bytes);
|
||||
if (originalImage == null) return [];
|
||||
|
||||
// YOLOv8/v11 usually takes 640x640
|
||||
const int inputSize = 640;
|
||||
final resizedImage = img.copyResize(
|
||||
originalImage,
|
||||
width: inputSize,
|
||||
height: inputSize,
|
||||
);
|
||||
|
||||
// Prepare input tensor
|
||||
var input = _imageToByteListFloat32(resizedImage, inputSize);
|
||||
|
||||
// Raw YOLO output shape usually [1, 4 + num_classes, 8400]
|
||||
// For single class "impact", it's [1, 5, 8400]
|
||||
var output = List<double>.filled(1 * 5 * 8400, 0).reshape([1, 5, 8400]);
|
||||
|
||||
_interpreter!.run(input, output);
|
||||
|
||||
return _processOutput(
|
||||
output[0],
|
||||
originalImage.width,
|
||||
originalImage.height,
|
||||
);
|
||||
} catch (e) {
|
||||
print('Error during YOLO inference: $e');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
List<DetectedImpactResult> _processOutput(
|
||||
List<List<double>> output,
|
||||
int imgWidth,
|
||||
int imgHeight,
|
||||
) {
|
||||
final List<_Detection> candidates = [];
|
||||
const double threshold = 0.25;
|
||||
|
||||
// output is [5, 8400] -> [x, y, w, h, conf]
|
||||
for (int i = 0; i < 8400; i++) {
|
||||
final double confidence = output[4][i];
|
||||
if (confidence > threshold) {
|
||||
candidates.add(
|
||||
_Detection(
|
||||
x: output[0][i],
|
||||
y: output[1][i],
|
||||
w: output[2][i],
|
||||
h: output[3][i],
|
||||
confidence: confidence,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply Non-Max Suppression (NMS)
|
||||
final List<_Detection> suppressed = _nms(candidates);
|
||||
|
||||
return suppressed
|
||||
.map(
|
||||
(det) => DetectedImpactResult(
|
||||
x: det.x / 640.0,
|
||||
y: det.y / 640.0,
|
||||
radius: 5.0,
|
||||
suggestedScore: 0,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<_Detection> _nms(List<_Detection> detections) {
|
||||
if (detections.isEmpty) return [];
|
||||
|
||||
// Sort by confidence descending
|
||||
detections.sort((a, b) => b.confidence.compareTo(a.confidence));
|
||||
|
||||
final List<_Detection> selected = [];
|
||||
final List<bool> 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;
|
||||
_Detection({
|
||||
required this.x,
|
||||
required this.y,
|
||||
required this.w,
|
||||
required this.h,
|
||||
required this.confidence,
|
||||
});
|
||||
}
|
||||
@@ -7,7 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
tflite_flutter
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
16
pubspec.lock
16
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: ffb8651fdb116ab0131d6dc47ff73883e0f634ad1ab12bb2852eef1bbeab4a6a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.4"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -65,7 +65,7 @@ dependencies:
|
||||
image: ^4.1.7
|
||||
|
||||
# Machine Learning for YOLOv8
|
||||
tflite_flutter: ^0.10.4
|
||||
# tflite_flutter: ^0.11.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@@ -91,6 +91,7 @@ flutter:
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - assets/models/yolov8n_32.tflite
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
)
|
||||
|
||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||
tflite_flutter
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
||||
Reference in New Issue
Block a user