From de677aad7ec6a9f90501972ef138060b7ed9ff99 Mon Sep 17 00:00:00 2001 From: streaper2 Date: Sun, 15 Feb 2026 22:07:35 +0100 Subject: [PATCH] =?UTF-8?q?d=C3=A9sactivation=20autocalibration,=20et=20co?= =?UTF-8?q?rrection=20de=20distortion=20dans=20l'=C3=A9cran=20de=20calibra?= =?UTF-8?q?tion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/features/analysis/analysis_screen.dart | 12 +- .../opencv_impact_detection_service.dart | 5 +- lib/services/opencv_target_service.dart | 194 +++++++++++++----- 3 files changed, 146 insertions(+), 65 deletions(-) diff --git a/lib/features/analysis/analysis_screen.dart b/lib/features/analysis/analysis_screen.dart index 4ac7feb..c5c4197 100644 --- a/lib/features/analysis/analysis_screen.dart +++ b/lib/features/analysis/analysis_screen.dart @@ -275,7 +275,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { child: Column( children: [ // Auto-calibrate button - SizedBox( + /*SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: () async { @@ -334,7 +334,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { foregroundColor: Colors.white, ), ), - ), + ),*/ const SizedBox(height: 16), // Ring count slider Row( @@ -438,7 +438,7 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { ), const Divider(color: Colors.white24, height: 16), // Distortion correction row - Row( + /*Row( children: [ const Icon( Icons.lens_blur, @@ -503,19 +503,19 @@ class _AnalysisScreenContentState extends State<_AnalysisScreenContent> { ), ), const SizedBox(width: 8), - Switch( + /*Switch( value: provider.distortionCorrectionEnabled, onChanged: (value) => provider .setDistortionCorrectionEnabled(value), activeTrackColor: AppTheme.primaryColor .withValues(alpha: 0.5), activeThumbColor: AppTheme.primaryColor, - ), + ),*/ ], ), ], ], - ), + ),*/ ], ), ), diff --git a/lib/services/opencv_impact_detection_service.dart b/lib/services/opencv_impact_detection_service.dart index a70bc12..cfa32c6 100644 --- a/lib/services/opencv_impact_detection_service.dart +++ b/lib/services/opencv_impact_detection_service.dart @@ -115,9 +115,10 @@ class OpenCVImpactDetectionService { ); if (circles.rows > 0 && circles.cols > 0) { + // Mat shape: (1, N, 3) usually for HoughCircles (CV_32FC3) + // We use at directly. + for (int i = 0; i < circles.cols; i++) { - // Access circle data: x, y, radius - // Assuming common Vec3f layout final vec = circles.at(0, i); final x = vec.val1; final y = vec.val2; diff --git a/lib/services/opencv_target_service.dart b/lib/services/opencv_target_service.dart index 0c94485..6b2f9c0 100644 --- a/lib/services/opencv_target_service.dart +++ b/lib/services/opencv_target_service.dart @@ -55,9 +55,22 @@ class OpenCVTargetService { maxRadius: img.cols ~/ 2, // maxRadius ); - // HoughCircles returns a Mat in opencv_dart? Or a specific object? - // Checking common bindings: usually returns a Mat (1, N, 3) of floats. - // If circles is empty or null, return failure. + // HoughCircles returns a Mat of shape (1, N, 3) where N is number of circles. + // In opencv_dart, we cannot iterate easily. + // However, we can access data via pointer if needed, or check if Vec3f is supported. + // Given the user report, `at` likely failed compilation or runtime. + // Let's use a safer approach: assume standard memory layout (x, y, r, x, y, r...). + // Or use `at` carefully. + + // Better yet: try to use `circles.data` if available, but it returns a Pointer. + // Let's stick to `at` but use `double` and manual offset if Vec3f fails. + // actually, let's try to trust `at` for flattened access OR `at`. + // NOTE: `at` was reported as "method at not defined for VecPoint2f" earlier, NOT for Mat. + // The user error was for `VecPoint2f`. `Mat` definitely has `at`. + // BUT `VecPoint2f` is a List-like structure in Dart wrapper. + // usage of `at` on `VecPoint2f` was the error. + // Here `circles` IS A MAT. So `at` IS defined. + // However, to be safe and robust, and to implement clustering... if (circles.isEmpty) { // Try with different parameters if first attempt fails (more lenient) @@ -75,81 +88,148 @@ class OpenCVTargetService { if (looseCircles.isEmpty) { return TargetDetectionResult.failure(); } - return _findBestCircle(looseCircles, img.cols, img.rows); + return _findBestConcentricCircles(looseCircles, img.cols, img.rows); } - return _findBestCircle(circles, img.cols, img.rows); + return _findBestConcentricCircles(circles, img.cols, img.rows); } catch (e) { // print('Error detecting target with OpenCV: $e'); return TargetDetectionResult.failure(); } } - TargetDetectionResult _findBestCircle(cv.Mat circles, int width, int height) { - // circles is a Mat of shape (1, N, 3) where N is number of circles - // Each circle is (x, y, radius) - - // We want the circle that is closest to the center of the image and reasonably large - double bestScore = -1.0; - double bestX = 0.5; - double bestY = 0.5; - double bestRadius = 0.4; - - // The shape is typically (1, N, 3) for HoughCircles - // We need to access the data. - // Assuming we can iterate. - // In opencv_dart 1.0+, Mat might not be directly iterable like a list. - // We can use circles.at(0, i) if available or similar. - // Or we might need to interpret the memory. - - // For now, let's assume a simplified access pattern or that we can get a list. - // If this fails to compile, we will fix it based on the error. - - // Attempting to access knowing standard layout: - // circles.rows is 1, circles.cols is N. + TargetDetectionResult _findBestConcentricCircles( + cv.Mat circles, + int width, + int height, + ) { + if (circles.rows == 0 || circles.cols == 0) { + return TargetDetectionResult.failure(); + } final int numCircles = circles.cols; + final List<({double x, double y, double r})> detected = []; + + // Extract circles safely + // We'll use `at` assuming the Mat is (1, N, 3) float32 (CV_32FC3 usually) + // Actually HoughCircles usually returns CV_32FC3. + // So we can access `at(0, i)`. + // If that fails, we can fall back. But since `Mat` has `at`, it should work unless generic is bad. + // Let's assume it works for Mat but checking boundaries. + + // NOTE: If this throws "at not defined" (unlikely for Mat), we'd need another way. + // But since the previous error was on `VecPoint2f` (which is NOT a Mat), this should be fine. for (int i = 0; i < numCircles; i++) { - // Use the generic 'at' or specific getter if known. - // Assuming Vec3f is returned as a specific type or List - // Note: in many dart bindings, we might get a list of points directly. - // But HoughCircles typically returns Mat. - - // Let's try to use `at(0, i)` which is common in C++ and some bindings. - // If not, we might need `ptr` access. - + // Access using Vec3f if possible, or try to interpret memory + // Using `at` is the standard way. final vec = circles.at(0, i); - final double x = vec.val1; - final double y = vec.val2; - final double r = vec.val3; + detected.add((x: vec.val1, y: vec.val2, r: vec.val3)); + } - final relX = x / width; - final relY = y / height; - final relR = r / math.min(width, height); + if (detected.isEmpty) return TargetDetectionResult.failure(); - // Score based on centrality and size - final distFromCenter = math.sqrt( - math.pow(relX - 0.5, 2) + math.pow(relY - 0.5, 2), - ); - final sizeScore = - relR; // Larger is usually better for the main target outer ring + // Cluster circles by center position + // We consider circles "concentric" if their centers are within 5% of image min dimension + final double tolerance = math.min(width, height) * 0.05; + final List> clusters = []; - // We penalize distance from center - final score = sizeScore - (distFromCenter * 0.5); + for (final circle in detected) { + bool added = false; + for (final cluster in clusters) { + // Check distance to cluster center (average of existing) + double clusterX = 0; + double clusterY = 0; + for (final c in cluster) { + clusterX += c.x; + clusterY += c.y; + } + clusterX /= cluster.length; + clusterY /= cluster.length; - if (score > bestScore) { - bestScore = score; - bestX = relX; - bestY = relY; - bestRadius = relR; + final dist = math.sqrt( + math.pow(circle.x - clusterX, 2) + math.pow(circle.y - clusterY, 2), + ); + + if (dist < tolerance) { + cluster.add(circle); + added = true; + break; + } + } + if (!added) { + clusters.add([circle]); } } + // Find the best cluster + // 1. Prefer clusters with more circles (concentric rings) + // 2. Tie-break: closest to image center + + List<({double x, double y, double r})> bestCluster = clusters.first; + double bestScore = -1.0; + + for (final cluster in clusters) { + // Score calculation + // Base score = number of circles * 10 + double score = cluster.length * 10.0; + + // Penalize distance from center + double cx = 0, cy = 0; + for (final c in cluster) { + cx += c.x; + cy += c.y; + } + cx /= cluster.length; + cy /= cluster.length; + + final distFromCenter = math.sqrt( + math.pow(cx - width / 2, 2) + math.pow(cy - height / 2, 2), + ); + final relDist = distFromCenter / math.min(width, height); + + score -= relDist * 5.0; // Moderate penalty for off-center + + // Penalize very small clusters if they are just noise + // (Optional: check if radii are somewhat distributed?) + + if (score > bestScore) { + bestScore = score; + bestCluster = cluster; + } + } + + // Compute final result from best cluster + // Center: Use the smallest circle (bullseye) for best precision + // Radius: Use the largest circle (outer edge) for full coverage + + double centerX = 0; + double centerY = 0; + double maxR = 0; + double minR = double.infinity; + + for (final c in bestCluster) { + if (c.r > maxR) { + maxR = c.r; + } + if (c.r < minR) { + minR = c.r; + centerX = c.x; + centerY = c.y; + } + } + + // Fallback if something went wrong (shouldn't happen with non-empty cluster) + if (minR == double.infinity) { + centerX = bestCluster.first.x; + centerY = bestCluster.first.y; + } + return TargetDetectionResult( - centerX: bestX, - centerY: bestY, - radius: bestRadius, + centerX: centerX / width, + centerY: centerY / height, + radius: maxR / math.min(width, height), + success: true, ); } }