blur_detection_service.dart 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import 'package:logging/logging.dart';
  2. import "package:photos/services/face_ml/blur_detection/blur_constants.dart";
  3. class BlurDetectionService {
  4. final _logger = Logger('BlurDetectionService');
  5. // singleton pattern
  6. BlurDetectionService._privateConstructor();
  7. static final instance = BlurDetectionService._privateConstructor();
  8. factory BlurDetectionService() => instance;
  9. Future<(bool, double)> predictIsBlurGrayLaplacian(
  10. List<List<int>> grayImage, {
  11. int threshold = kLaplacianThreshold,
  12. }) async {
  13. final List<List<int>> laplacian = _applyLaplacian(grayImage);
  14. final double variance = _calculateVariance(laplacian);
  15. _logger.info('Variance: $variance');
  16. return (variance < threshold, variance);
  17. }
  18. double _calculateVariance(List<List<int>> matrix) {
  19. final int numRows = matrix.length;
  20. final int numCols = matrix[0].length;
  21. final int totalElements = numRows * numCols;
  22. // Calculate the mean
  23. double mean = 0;
  24. for (var row in matrix) {
  25. for (var value in row) {
  26. mean += value;
  27. }
  28. }
  29. mean /= totalElements;
  30. // Calculate the variance
  31. double variance = 0;
  32. for (var row in matrix) {
  33. for (var value in row) {
  34. final double diff = value - mean;
  35. variance += diff * diff;
  36. }
  37. }
  38. variance /= totalElements;
  39. return variance;
  40. }
  41. List<List<int>> _padImage(List<List<int>> image) {
  42. final int numRows = image.length;
  43. final int numCols = image[0].length;
  44. // Create a new matrix with extra padding
  45. final List<List<int>> paddedImage = List.generate(
  46. numRows + 2,
  47. (i) => List.generate(numCols + 2, (j) => 0, growable: false),
  48. growable: false,
  49. );
  50. // Copy original image into the center of the padded image
  51. for (int i = 0; i < numRows; i++) {
  52. for (int j = 0; j < numCols; j++) {
  53. paddedImage[i + 1][j + 1] = image[i][j];
  54. }
  55. }
  56. // Reflect padding
  57. // Top and bottom rows
  58. for (int j = 1; j <= numCols; j++) {
  59. paddedImage[0][j] = paddedImage[2][j]; // Top row
  60. paddedImage[numRows + 1][j] = paddedImage[numRows - 1][j]; // Bottom row
  61. }
  62. // Left and right columns
  63. for (int i = 0; i < numRows + 2; i++) {
  64. paddedImage[i][0] = paddedImage[i][2]; // Left column
  65. paddedImage[i][numCols + 1] = paddedImage[i][numCols - 1]; // Right column
  66. }
  67. return paddedImage;
  68. }
  69. List<List<int>> _applyLaplacian(List<List<int>> image) {
  70. final List<List<int>> paddedImage = _padImage(image);
  71. final int numRows = image.length;
  72. final int numCols = image[0].length;
  73. final List<List<int>> outputImage = List.generate(
  74. numRows,
  75. (i) => List.generate(numCols, (j) => 0, growable: false),
  76. growable: false,
  77. );
  78. // Define the Laplacian kernel
  79. final List<List<int>> kernel = [
  80. [0, 1, 0],
  81. [1, -4, 1],
  82. [0, 1, 0],
  83. ];
  84. // Apply the kernel to each pixel
  85. for (int i = 0; i < numRows; i++) {
  86. for (int j = 0; j < numCols; j++) {
  87. int sum = 0;
  88. for (int ki = 0; ki < 3; ki++) {
  89. for (int kj = 0; kj < 3; kj++) {
  90. sum += paddedImage[i + ki][j + kj] * kernel[ki][kj];
  91. }
  92. }
  93. // Adjust the output value if necessary (e.g., clipping)
  94. outputImage[i][j] = sum; //.clamp(0, 255);
  95. }
  96. }
  97. return outputImage;
  98. }
  99. }