import 'package:opencv_dart/opencv_dart.dart' as cv; import '../../domain/entities/quality_result.dart'; class QualityChecker { static double getBlurScore(cv.Mat mat) { final laplacian = cv.laplacian(mat, cv.MatType.CV_64F); final (_, stddev) = cv.meanStdDev(laplacian); laplacian.dispose(); return stddev.val1 * stddev.val1; } static double checkIllumination(cv.Mat mat) { final meanScalar = cv.mean(mat); return meanScalar.val1; } static bool checkPosition(cv.Mat mat) { final (_, threshMat) = cv.threshold(mat, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU); final (contours, hierarchy) = cv.findContours(threshMat, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); threshMat.dispose(); hierarchy.dispose(); if (contours.isEmpty) return false; double maxArea = 0; cv.Rect? maxRect; for (int i = 0; i < contours.length; i++) { final contour = contours[i]; final area = cv.contourArea(contour); if (area > maxArea) { maxArea = area; maxRect = cv.boundingRect(contour); } } if (maxRect == null) return false; final imgW = mat.cols; final imgH = mat.rows; final guideX = imgW ~/ 4; final guideY = imgH ~/ 4; final guideW = imgW ~/ 2; final guideH = imgH ~/ 2; final objCx = maxRect.x + maxRect.width / 2; final objCy = maxRect.y + maxRect.height / 2; final bool isCentered = objCx >= guideX && objCx <= (guideX + guideW) && objCy >= guideY && objCy <= (guideY + guideH); final bool isBigEnough = maxArea > (imgW * imgH * 0.25); return isCentered && isBigEnough; } static QualityResult analyze(cv.Mat mat) { final blur = getBlurScore(mat); final brightness = checkIllumination(mat); final positionValid = checkPosition(mat); final bool isSharp = blur > 100.0; final bool isLit = brightness > 40.0 && brightness < 220.0; final normBlur = (blur / 500.0).clamp(0.0, 1.0); final normBright = 1.0 - ((brightness - 128).abs() / 128.0).clamp(0.0, 1.0); final score = (normBlur * 0.6) + (normBright * 0.4); return QualityResult( passed: isSharp && isLit && positionValid, score: score, blurScore: blur, brightness: brightness, isPositionValid: positionValid, ); } }