229 lines
6.3 KiB
Dart
229 lines
6.3 KiB
Dart
/// Service de détection d'impacts utilisant OpenCV.
|
|
library;
|
|
|
|
import 'dart:math' as math;
|
|
import 'package:opencv_dart/opencv_dart.dart' as cv;
|
|
|
|
/// Paramètres de détection d'impacts OpenCV
|
|
class OpenCVDetectionSettings {
|
|
/// Seuil Canny bas pour la détection de contours
|
|
final double cannyThreshold1;
|
|
|
|
/// Seuil Canny haut pour la détection de contours
|
|
final double cannyThreshold2;
|
|
|
|
/// Distance minimale entre les centres des cercles détectés
|
|
final double minDist;
|
|
|
|
/// Paramètre 1 de HoughCircles (seuil Canny interne)
|
|
final double param1;
|
|
|
|
/// Paramètre 2 de HoughCircles (seuil d'accumulation)
|
|
final double param2;
|
|
|
|
/// Rayon minimum des cercles en pixels
|
|
final int minRadius;
|
|
|
|
/// Rayon maximum des cercles en pixels
|
|
final int maxRadius;
|
|
|
|
/// Taille du flou gaussien (doit être impair)
|
|
final int blurSize;
|
|
|
|
/// Utiliser la détection de contours en plus de Hough
|
|
final bool useContourDetection;
|
|
|
|
/// Circularité minimale pour la détection par contours (0-1)
|
|
final double minCircularity;
|
|
|
|
/// Surface minimale des contours
|
|
final double minContourArea;
|
|
|
|
/// Surface maximale des contours
|
|
final double maxContourArea;
|
|
|
|
const OpenCVDetectionSettings({
|
|
this.cannyThreshold1 = 50,
|
|
this.cannyThreshold2 = 150,
|
|
this.minDist = 20,
|
|
this.param1 = 100,
|
|
this.param2 = 30,
|
|
this.minRadius = 5,
|
|
this.maxRadius = 50,
|
|
this.blurSize = 5,
|
|
this.useContourDetection = true,
|
|
this.minCircularity = 0.6,
|
|
this.minContourArea = 50,
|
|
this.maxContourArea = 5000,
|
|
});
|
|
}
|
|
|
|
/// Résultat de détection d'impact
|
|
class OpenCVDetectedImpact {
|
|
/// Position X normalisée (0-1)
|
|
final double x;
|
|
|
|
/// Position Y normalisée (0-1)
|
|
final double y;
|
|
|
|
/// Rayon en pixels
|
|
final double radius;
|
|
|
|
/// Score de confiance (0-1)
|
|
final double confidence;
|
|
|
|
/// Méthode de détection utilisée
|
|
final String method;
|
|
|
|
const OpenCVDetectedImpact({
|
|
required this.x,
|
|
required this.y,
|
|
required this.radius,
|
|
this.confidence = 1.0,
|
|
this.method = 'unknown',
|
|
});
|
|
}
|
|
|
|
/// Service de détection d'impacts utilisant OpenCV
|
|
class OpenCVImpactDetectionService {
|
|
/// Détecte les impacts dans une image en utilisant OpenCV
|
|
List<OpenCVDetectedImpact> detectImpacts(
|
|
String imagePath, {
|
|
OpenCVDetectionSettings settings = const OpenCVDetectionSettings(),
|
|
}) {
|
|
try {
|
|
final img = cv.imread(imagePath, flags: cv.IMREAD_COLOR);
|
|
if (img.isEmpty) return [];
|
|
|
|
final gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY);
|
|
|
|
// Apply blur to reduce noise
|
|
final blurKSize = (settings.blurSize, settings.blurSize);
|
|
final blurred = cv.gaussianBlur(gray, blurKSize, 2, sigmaY: 2);
|
|
|
|
final List<OpenCVDetectedImpact> detectedImpacts = [];
|
|
|
|
final circles = cv.HoughCircles(
|
|
blurred,
|
|
cv.HOUGH_GRADIENT,
|
|
1,
|
|
settings.minDist,
|
|
param1: settings.param1,
|
|
param2: settings.param2,
|
|
minRadius: settings.minRadius,
|
|
maxRadius: settings.maxRadius,
|
|
);
|
|
|
|
if (circles.rows > 0 && circles.cols > 0) {
|
|
// Mat shape: (1, N, 3) usually for HoughCircles (CV_32FC3)
|
|
// We use at<Vec3f> directly.
|
|
|
|
for (int i = 0; i < circles.cols; i++) {
|
|
final vec = circles.at<cv.Vec3f>(0, i);
|
|
final x = vec.val1;
|
|
final y = vec.val2;
|
|
final r = vec.val3;
|
|
|
|
detectedImpacts.add(
|
|
OpenCVDetectedImpact(
|
|
x: x / img.cols,
|
|
y: y / img.rows,
|
|
radius: r,
|
|
confidence: 0.8,
|
|
method: 'hough',
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
// 2. Contour Detection (if enabled)
|
|
if (settings.useContourDetection) {
|
|
// Canny edge detection
|
|
final edges = cv.canny(
|
|
blurred,
|
|
settings.cannyThreshold1,
|
|
settings.cannyThreshold2,
|
|
);
|
|
|
|
// Find contours
|
|
final contoursResult = cv.findContours(
|
|
edges,
|
|
cv.RETR_EXTERNAL,
|
|
cv.CHAIN_APPROX_SIMPLE,
|
|
);
|
|
|
|
final contours = contoursResult.$1;
|
|
// hierarchy is $2
|
|
|
|
for (int i = 0; i < contours.length; i++) {
|
|
final contour = contours[i];
|
|
|
|
// Filter by area
|
|
final area = cv.contourArea(contour);
|
|
if (area < settings.minContourArea ||
|
|
area > settings.maxContourArea) {
|
|
continue;
|
|
}
|
|
|
|
// Filter by circularity
|
|
final perimeter = cv.arcLength(contour, true);
|
|
if (perimeter == 0) continue;
|
|
final circularity = 4 * math.pi * area / (perimeter * perimeter);
|
|
|
|
if (circularity < settings.minCircularity) continue;
|
|
|
|
// Get bounding circle
|
|
final enclosingCircle = cv.minEnclosingCircle(contour);
|
|
final center = enclosingCircle.$1;
|
|
final radius = enclosingCircle.$2;
|
|
|
|
// Avoid duplicates (simple distance check against Hough results)
|
|
bool isDuplicate = false;
|
|
for (final existing in detectedImpacts) {
|
|
final dx = existing.x * img.cols - center.x;
|
|
final dy = existing.y * img.rows - center.y;
|
|
final dist = math.sqrt(dx * dx + dy * dy);
|
|
if (dist < radius) {
|
|
isDuplicate = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isDuplicate) {
|
|
detectedImpacts.add(
|
|
OpenCVDetectedImpact(
|
|
x: center.x / img.cols,
|
|
y: center.y / img.rows,
|
|
radius: radius,
|
|
confidence: circularity, // Use circularity as confidence
|
|
method: 'contour',
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return detectedImpacts;
|
|
} catch (e) {
|
|
// print('OpenCV Error: $e');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/// Détecte les impacts en utilisant une image de référence
|
|
List<OpenCVDetectedImpact> detectFromReferences(
|
|
String imagePath,
|
|
List<({double x, double y})> referencePoints, {
|
|
double tolerance = 2.0,
|
|
}) {
|
|
// Basic implementation: use average color/brightness of reference points
|
|
// This is a placeholder for a more complex template matching or feature matching
|
|
|
|
// For now, we can just run the standard detection but filter results
|
|
// based on properties of the reference points (e.g. size/radius if we had it).
|
|
|
|
// Returning standard detection for now to enable the feature.
|
|
return detectImpacts(imagePath);
|
|
}
|
|
}
|