diff --git a/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java b/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java index d0f52b6..7e72372 100644 --- a/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java +++ b/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java @@ -8,43 +8,34 @@ import org.opencv.core.Mat; public class GlobalLinearFiltering { public static GrayScaleMatrix lowPassFiltering(GrayScaleMatrix matrix, double cutoff) { - int width = matrix.getWidth(); - int height = matrix.getHeight(); + validateCutoff(cutoff, matrix.getWidth(), matrix.getHeight()); - validateCutoff(cutoff, width, height); - - Mat mat = toMat(matrix); - Mat spectrum = fourierTransform(mat); + Mat spectrum = fourierTransform(toMat(matrix)); shiftSpectrum(spectrum); applyIdealLowPassFilter(spectrum, cutoff); shiftSpectrum(spectrum); Mat filtered = inverseFourierTransform(spectrum); - return toGrayScaleMatrix(filtered); } public static GrayScaleMatrix highPassFiltering(GrayScaleMatrix matrix, double cutoff) { - int width = matrix.getWidth(); - int height = matrix.getHeight(); + validateCutoff(cutoff, matrix.getWidth(), matrix.getHeight()); - validateCutoff(cutoff, width, height); - - Mat mat = toMat(matrix); - Mat spectrum = fourierTransform(mat); + Mat spectrum = fourierTransform(toMat(matrix)); shiftSpectrum(spectrum); applyIdealHighPassFilter(spectrum, cutoff); shiftSpectrum(spectrum); Mat filtered = inverseFourierTransform(spectrum); - return toGrayScaleMatrix(filtered); } public static GrayScaleMatrix butterworthLowPassFiltering(GrayScaleMatrix matrix, double cutoff, int order) { validateCutoff(cutoff, matrix.getWidth(), matrix.getHeight()); + validateOrder(order); Mat spectrum = fourierTransform(toMat(matrix)); @@ -52,11 +43,13 @@ public class GlobalLinearFiltering { applyButterworthLowPassFilter(spectrum, cutoff, order); shiftSpectrum(spectrum); - return toGrayScaleMatrix(inverseFourierTransform(spectrum)); + Mat filtered = inverseFourierTransform(spectrum); + return toGrayScaleMatrix(filtered); } public static GrayScaleMatrix butterworthHighPassFiltering(GrayScaleMatrix matrix, double cutoff, int order) { validateCutoff(cutoff, matrix.getWidth(), matrix.getHeight()); + validateOrder(order); Mat spectrum = fourierTransform(toMat(matrix)); @@ -64,25 +57,23 @@ public class GlobalLinearFiltering { applyButterworthHighPassFilter(spectrum, cutoff, order); shiftSpectrum(spectrum); - return toGrayScaleMatrix(inverseFourierTransform(spectrum)); + Mat filtered = inverseFourierTransform(spectrum); + return toGrayScaleMatrix(filtered); } private static void applyIdealLowPassFilter(Mat spectrum, double cutoff) { int width = spectrum.cols(); int height = spectrum.rows(); - int cx = width / 2; - int cy = height / 2; + double cx = width / 2.0; + double cy = height / 2.0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - - double distance = Math.sqrt( - Math.pow(x - cx, 2) + Math.pow(y - cy, 2) - ); + double distance = distance(x, y, cx, cy); if (distance > cutoff) { - spectrum.put(y, x, 0, 0); + spectrum.put(y, x, 0.0, 0.0); } } } @@ -92,16 +83,15 @@ public class GlobalLinearFiltering { int width = spectrum.cols(); int height = spectrum.rows(); - int cx = width / 2; - int cy = height / 2; + double cx = width / 2.0; + double cy = height / 2.0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - - double distance = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2)); + double distance = distance(x, y, cx, cy); if (distance <= cutoff) { - spectrum.put(y, x, 0, 0); + spectrum.put(y, x, 0.0, 0.0); } } } @@ -111,18 +101,15 @@ public class GlobalLinearFiltering { int width = spectrum.cols(); int height = spectrum.rows(); - int cx = width / 2; - int cy = height / 2; + double cx = width / 2.0; + double cy = height / 2.0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - - double distance = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2) ); - - double coefficient = 1.0 / (1.0 + Math.pow(distance / cutoff, 2 * order)); + double distance = distance(x, y, cx, cy); + double coefficient = 1.0 / (1.0 + Math.pow(distance / cutoff, 2.0 * order)); double[] value = spectrum.get(y, x); - spectrum.put(y, x, value[0] * coefficient, value[1] * coefficient); } } @@ -132,45 +119,49 @@ public class GlobalLinearFiltering { int width = spectrum.cols(); int height = spectrum.rows(); - int cx = width / 2; - int cy = height / 2; + double cx = width / 2.0; + double cy = height / 2.0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { + double distance = distance(x, y, cx, cy); - double distance = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2)); - - double coefficient; - - if (distance == 0) { - coefficient = 0; - } else { - coefficient = 1.0 / (1.0 + Math.pow(cutoff / distance, 2 * order)); - } + double coefficient = distance == 0.0 + ? 0.0 + : 1.0 / (1.0 + Math.pow(cutoff / distance, 2.0 * order)); double[] value = spectrum.get(y, x); - spectrum.put(y, x, value[0] * coefficient, value[1] * coefficient); } } } - private static void validateCutoff(double cutoff, int width, int height) { - double minCutoff = 0; - double maxCutoff = Math.sqrt( - Math.pow(width / 2.0, 2) + - Math.pow(height / 2.0, 2) - ); + private static double distance(double x, double y, double cx, double cy) { + double dx = x - cx; + double dy = y - cy; + return Math.sqrt(dx * dx + dy * dy); + } - if (cutoff < minCutoff || cutoff > maxCutoff) { - throw new IllegalArgumentException( - String.format( - "Cutoff value is out of bounds (%f - %f)", - minCutoff, - maxCutoff - ) - ); + private static void shiftSpectrum(Mat spectrum) { + int width = spectrum.cols(); + int height = spectrum.rows(); + + Mat shifted = Mat.zeros(spectrum.size(), spectrum.type()); + + int shiftX = width / 2; + int shiftY = height / 2; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int newX = (x + shiftX) % width; + int newY = (y + shiftY) % height; + + shifted.put(newY, newX, spectrum.get(y, x)); + } } + + shifted.copyTo(spectrum); + shifted.release(); } private static Mat fourierTransform(Mat mat) { @@ -179,54 +170,20 @@ public class GlobalLinearFiltering { return spectrum; } - private static void shiftSpectrum(Mat spectrum) { - int width = spectrum.cols(); - int height = spectrum.rows(); - - int cx = width / 2; - int cy = height / 2; - - Mat q0 = spectrum.submat(0, cy, 0, cx); - Mat q1 = spectrum.submat(0, cy, cx, width); - Mat q2 = spectrum.submat(cy, height, 0, cx); - Mat q3 = spectrum.submat(cy, height, cx, width); - - Mat tmp = new Mat(); - - q0.copyTo(tmp); - q3.copyTo(q0); - tmp.copyTo(q3); - - q1.copyTo(tmp); - q2.copyTo(q1); - tmp.copyTo(q2); - } - private static Mat inverseFourierTransform(Mat spectrum) { Mat filtered = new Mat(); - - Core.idft( - spectrum, - filtered, - Core.DFT_REAL_OUTPUT | Core.DFT_SCALE - ); - + Core.idft(spectrum, filtered, Core.DFT_REAL_OUTPUT | Core.DFT_SCALE); return filtered; } - private static GrayScaleMatrix toGrayScaleMatrix(Mat filtered) { - int width = filtered.cols(); - int height = filtered.rows(); - + private static GrayScaleMatrix toGrayScaleMatrix(Mat matrix) { + int width = matrix.cols(); + int height = matrix.rows(); double[][] pixels = new double[height][width]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - pixels[y][x] = Math.clamp( - filtered.get(y, x)[0], - 0.0, - 255.0 - ); + pixels[y][x] = Math.clamp(matrix.get(y, x)[0], 0.0, 255.0); } } @@ -236,7 +193,6 @@ public class GlobalLinearFiltering { private static Mat toMat(GrayScaleMatrix matrix) { int width = matrix.getWidth(); int height = matrix.getHeight(); - Mat mat = new Mat(height, width, CvType.CV_64F); double[][] data = matrix.getRawData(); @@ -248,4 +204,22 @@ public class GlobalLinearFiltering { return mat; } + private static void validateCutoff(double cutoff, int width, int height) { + double maxCutoff = Math.sqrt( + Math.pow(width / 2.0, 2) + + Math.pow(height / 2.0, 2) + ); + + if (cutoff <= 0.0 || cutoff > maxCutoff) { + throw new IllegalArgumentException( + String.format("Cutoff must be > 0 and <= %f, got %f", maxCutoff, cutoff) + ); + } + } + + private static void validateOrder(int order) { + if (order < 1) { + throw new IllegalArgumentException("Butterworth order must be >= 1"); + } + } } diff --git a/src/main/java/domain/image/processing/filtering/LocalLinearFiltering.java b/src/main/java/domain/image/processing/filtering/LocalLinearFiltering.java index 85ac310..3f8f0f1 100644 --- a/src/main/java/domain/image/processing/filtering/LocalLinearFiltering.java +++ b/src/main/java/domain/image/processing/filtering/LocalLinearFiltering.java @@ -8,7 +8,6 @@ import org.opencv.core.Point; import org.opencv.imgproc.Imgproc; public class LocalLinearFiltering { - public static GrayScaleMatrix convolutionMask(GrayScaleMatrix matrix, double[][] mask) { validateMask(mask); return applyMask(matrix, mask); @@ -16,91 +15,69 @@ public class LocalLinearFiltering { public static GrayScaleMatrix meanFilter(GrayScaleMatrix matrix, int maskSize) { validateMaskSize(maskSize); - double coefficient = 1.0 / (maskSize * maskSize); double[][] mask = new double[maskSize][maskSize]; - for (int y = 0; y < maskSize; y++) { for (int x = 0; x < maskSize; x++) { mask[y][x] = coefficient; } } - return applyMask(matrix, mask); } private static GrayScaleMatrix applyMask(GrayScaleMatrix matrix, double[][] mask) { Mat source = toMat(matrix); - int maskSize = mask.length; - - Mat kernel = new Mat(maskSize, maskSize, CvType.CV_64F); - - for (int y = 0; y < maskSize; y++) { - for (int x = 0; x < maskSize; x++) { - kernel.put(y, x, mask[y][x]); - } - } - + Mat kernel = toMat(mask); Mat destination = new Mat(); - - Imgproc.filter2D( - source, - destination, - CvType.CV_64F, - kernel, - new Point(-1, -1), - 0, - Core.BORDER_REPLICATE - ); - + Imgproc.filter2D(source, destination, CvType.CV_64F, kernel, new Point(-1, -1), 0, Core.BORDER_REPLICATE); GrayScaleMatrix result = fromMat(destination); - source.release(); kernel.release(); destination.release(); - return result; } private static void validateMask(double[][] mask) { if (mask == null || mask.length == 0 || mask.length % 2 == 0) { - throw new IllegalArgumentException( - "Le masque doit être une matrice carrée de dimension impaire." - ); + throw new IllegalArgumentException("Le masque doit être une matrice carrée de dimension impaire."); } - int size = mask.length; - for (double[] row : mask) { if (row == null || row.length != size) { - throw new IllegalArgumentException( - "Le masque doit être une matrice carrée." - ); + throw new IllegalArgumentException("Le masque doit être une matrice carrée."); + } + for (double value : row) { + if (!Double.isFinite(value)) { + throw new IllegalArgumentException("Le masque contient une valeur invalide."); + } } } } private static void validateMaskSize(int maskSize) { if (maskSize <= 0 || maskSize % 2 == 0) { - throw new IllegalArgumentException( - "La taille du masque doit être un entier positif impair." - ); + throw new IllegalArgumentException("La taille du masque doit être un entier positif impair."); } } private static Mat toMat(GrayScaleMatrix matrix) { - double[][] image = matrix.getRawData(); - int height = image.length; - int width = image[0].length; - + double[][] data = matrix.getRawData(); + int height = data.length; + int width = data[0].length; Mat mat = new Mat(height, width, CvType.CV_64F); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - mat.put(y, x, image[y][x]); - } + mat.put(y, 0, data[y]); } + return mat; + } + private static Mat toMat(double[][] data) { + int height = data.length; + int width = data[0].length; + Mat mat = new Mat(height, width, CvType.CV_64F); + for (int y = 0; y < height; y++) { + mat.put(y, 0, data[y]); + } return mat; } @@ -108,13 +85,13 @@ public class LocalLinearFiltering { int height = mat.rows(); int width = mat.cols(); double[][] result = new double[height][width]; - for (int y = 0; y < height; y++) { + double[] row = new double[width]; + mat.get(y, 0, row); for (int x = 0; x < width; x++) { - result[y][x] = Math.clamp(mat.get(y, x)[0], 0.0, 255.0); + result[y][x] = Math.clamp(row[x], 0.0, 255.0); } } - return new GrayScaleMatrix(result); } -} \ No newline at end of file +} diff --git a/src/main/java/domain/image/processing/morphology/BasicMorphology.java b/src/main/java/domain/image/processing/morphology/BasicMorphology.java index 1c45cdd..e514c57 100644 --- a/src/main/java/domain/image/processing/morphology/BasicMorphology.java +++ b/src/main/java/domain/image/processing/morphology/BasicMorphology.java @@ -8,94 +8,82 @@ import org.opencv.core.Size; import org.opencv.imgproc.Imgproc; public class BasicMorphology { - public static GrayScaleMatrix erosion(Image image, int tailleMasque) { Mat input = toMat(image); Mat kernel = createKernel(tailleMasque); Mat result = new Mat(); - Imgproc.erode(input, result, kernel); - - return toGrayScaleMatrix(result); + GrayScaleMatrix output = toGrayScaleMatrix(result); + input.release(); + kernel.release(); + result.release(); + return output; } public static GrayScaleMatrix dilation(Image image, int tailleMasque) { Mat input = toMat(image); Mat kernel = createKernel(tailleMasque); Mat result = new Mat(); - Imgproc.dilate(input, result, kernel); - - return toGrayScaleMatrix(result); + GrayScaleMatrix output = toGrayScaleMatrix(result); + input.release(); + kernel.release(); + result.release(); + return output; } public static GrayScaleMatrix opening(Image image, int tailleMasque) { Mat input = toMat(image); Mat kernel = createKernel(tailleMasque); Mat result = new Mat(); - - Imgproc.morphologyEx( - input, - result, - Imgproc.MORPH_OPEN, - kernel - ); - - return toGrayScaleMatrix(result); + Imgproc.morphologyEx(input, result, Imgproc.MORPH_OPEN, kernel); + GrayScaleMatrix output = toGrayScaleMatrix(result); + input.release(); + kernel.release(); + result.release(); + return output; } public static GrayScaleMatrix closing(Image image, int tailleMasque) { Mat input = toMat(image); Mat kernel = createKernel(tailleMasque); Mat result = new Mat(); - - Imgproc.morphologyEx( - input, - result, - Imgproc.MORPH_CLOSE, - kernel - ); - - return toGrayScaleMatrix(result); + Imgproc.morphologyEx(input, result, Imgproc.MORPH_CLOSE, kernel); + GrayScaleMatrix output = toGrayScaleMatrix(result); + input.release(); + kernel.release(); + result.release(); + return output; } private static Mat toMat(Image image) { GrayScaleMatrix gray = image.toGrayScale(); - int width = gray.getWidth(); int height = gray.getHeight(); - Mat mat = new Mat(height, width, CvType.CV_64F); - double[][] data = gray.getRawData(); - for (int y = 0; y < height; y++) { mat.put(y, 0, data[y]); } - return mat; } private static Mat createKernel(int tailleMasque) { - return Imgproc.getStructuringElement( - Imgproc.MORPH_RECT, - new Size(tailleMasque, tailleMasque) - ); + if (tailleMasque <= 0 || tailleMasque % 2 == 0) { + throw new IllegalArgumentException("La taille du masque doit être un entier positif impair."); + } + return Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(tailleMasque, tailleMasque)); } private static GrayScaleMatrix toGrayScaleMatrix(Mat mat) { int width = mat.cols(); int height = mat.rows(); - double[][] output = new double[height][width]; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - output[y][x] = mat.get(y, x)[0]; - } + double[] row = new double[width]; + mat.get(y, 0, row); + System.arraycopy(row, 0, output[y], 0, width); } - return new GrayScaleMatrix(output); } - } diff --git a/src/main/java/domain/image/processing/morphology/ComplexMorphology.java b/src/main/java/domain/image/processing/morphology/ComplexMorphology.java index 7278a2a..a769f51 100644 --- a/src/main/java/domain/image/processing/morphology/ComplexMorphology.java +++ b/src/main/java/domain/image/processing/morphology/ComplexMorphology.java @@ -11,23 +11,33 @@ import org.opencv.imgproc.Imgproc; public class ComplexMorphology { public static GrayScaleMatrix geodesicDilation(Image image, Image mask, int nbIter) { + if (nbIter < 0) { + throw new IllegalArgumentException("Number of iterations must be >= 0"); + } + Mat maskMat = toMat(mask); Mat current = toMat(image); Mat kernel = createKernel(3); for (int i = 0; i < nbIter; i++) { Mat dilated = new Mat(); - Imgproc.dilate(current, dilated, kernel); Mat result = new Mat(); - Core.min(dilated, maskMat, result); + current.release(); + dilated.release(); current = result; } - return toGrayScaleMatrix(current); + GrayScaleMatrix result = toGrayScaleMatrix(current); + + current.release(); + maskMat.release(); + kernel.release(); + + return result; } public static GrayScaleMatrix geodesicReconstruction(Image image, Image mask) { @@ -37,48 +47,51 @@ public class ComplexMorphology { while (true) { Mat dilated = new Mat(); - Imgproc.dilate(current, dilated, kernel); Mat next = new Mat(); - Core.min(dilated, maskMat, next); double difference = Core.norm(next, current, Core.NORM_INF); - if (difference == 0) { - return toGrayScaleMatrix(next); + current.release(); + dilated.release(); + + if (difference == 0.0) { + GrayScaleMatrix result = toGrayScaleMatrix(next); + + next.release(); + maskMat.release(); + kernel.release(); + + return result; } current = next; } } - public static GrayScaleMatrix medianFilter(Image image, int tailleMasque) { - if (tailleMasque < 1 || tailleMasque % 2 == 0) { - throw new IllegalArgumentException( - "Median filter size must be a positive odd number" - ); - } + public static GrayScaleMatrix medianFilter(Image image, int maskSize) { + validateMedianSize(maskSize); Mat input = to8BitMat(image); Mat result = new Mat(); - Imgproc.medianBlur(input, result, tailleMasque); + Imgproc.medianBlur(input, result, maskSize); - return toGrayScaleMatrix(result); + GrayScaleMatrix output = toGrayScaleMatrix(result); + + input.release(); + result.release(); + + return output; } - /** - * Converts an image to a CV_64F grayscale matrix. - * Used by geodesic morphology operations. - */ private static Mat toMat(Image image) { GrayScaleMatrix gray = image.toGrayScale(); int width = gray.getWidth(); int height = gray.getHeight(); - Mat mat = new Mat(height, width, CvType.CV_64F); double[][] data = gray.getRawData(); @@ -90,16 +103,11 @@ public class ComplexMorphology { return mat; } - /** - * Converts an image to an 8-bit grayscale matrix. - * Required by OpenCV medianBlur(). - */ private static Mat to8BitMat(Image image) { GrayScaleMatrix gray = image.toGrayScale(); int width = gray.getWidth(); int height = gray.getHeight(); - Mat mat = new Mat(height, width, CvType.CV_8UC1); double[][] data = gray.getRawData(); @@ -108,9 +116,7 @@ public class ComplexMorphology { byte[] row = new byte[width]; for (int x = 0; x < width; x++) { - int value = (int) Math.round(data[y][x]); - value = Math.clamp(value, 0, 255); - + int value = Math.clamp((int) Math.round(data[y][x]), 0, 255); row[x] = (byte) value; } @@ -121,14 +127,15 @@ public class ComplexMorphology { } private static Mat createKernel(int size) { - return Imgproc.getStructuringElement( Imgproc.MORPH_RECT, new Size(size, size) + return Imgproc.getStructuringElement( + Imgproc.MORPH_RECT, + new Size(size, size) ); } private static GrayScaleMatrix toGrayScaleMatrix(Mat mat) { int width = mat.cols(); int height = mat.rows(); - double[][] output = new double[height][width]; if (mat.type() == CvType.CV_8UC1) { @@ -150,4 +157,12 @@ public class ComplexMorphology { return new GrayScaleMatrix(output); } + + private static void validateMedianSize(int maskSize) { + if (maskSize < 1 || maskSize % 2 == 0) { + throw new IllegalArgumentException( + "Median filter size must be a positive odd number" + ); + } + } } \ No newline at end of file diff --git a/src/main/java/domain/image/processing/outline/LinearOutline.java b/src/main/java/domain/image/processing/outline/LinearOutline.java index 1ccecb5..b03d93d 100644 --- a/src/main/java/domain/image/processing/outline/LinearOutline.java +++ b/src/main/java/domain/image/processing/outline/LinearOutline.java @@ -7,121 +7,60 @@ import org.opencv.core.Mat; import org.opencv.imgproc.Imgproc; public class LinearOutline { - - /** - * - * @param image - * @param direction applied horizontally if 1, applied vertically if 2 - * @return - */ public static GrayScaleMatrix prewittGradient(Image image, int direction) { Mat kernel = new Mat(3, 3, CvType.CV_64F); - if (direction == 1) { - kernel.put(0, 0, - -1, -1, -1, - 0, 0, 0, - 1, 1, 1 - ); + kernel.put(0, 0, -1, -1, -1, 0, 0, 0, 1, 1, 1); } else if (direction == 2) { - kernel.put(0, 0, - -1, 0, 1, - -1, 0, 1, - -1, 0, 1 - ); + kernel.put(0, 0, -1, 0, 1, -1, 0, 1, -1, 0, 1); } else { + kernel.release(); throw new IllegalArgumentException("Direction must be 1 or 2"); } - return applyKernel(image, kernel); } - /** - * - * @param image - * @param direction applied horizontally if 1, applied vertically if 2 - * @return - */ public static GrayScaleMatrix sobelGradient(Image image, int direction) { Mat kernel = new Mat(3, 3, CvType.CV_64F); - if (direction == 1) { - kernel.put(0, 0, - -1, -2, -1, - 0, 0, 0, - 1, 2, 1 - ); + kernel.put(0, 0, -1, -2, -1, 0, 0, 0, 1, 2, 1); } else if (direction == 2) { - kernel.put(0, 0, - -1, 0, 1, - -2, 0, 2, - -1, 0, 1 - ); + kernel.put(0, 0, -1, 0, 1, -2, 0, 2, -1, 0, 1); } else { + kernel.release(); throw new IllegalArgumentException("Direction must be 1 or 2"); } - return applyKernel(image, kernel); } public static GrayScaleMatrix laplacian4(Image image) { Mat kernel = new Mat(3, 3, CvType.CV_64F); - - kernel.put(0, 0, - 0, -1, 0, - -1, 4, -1, - 0, -1, 0 - ); - + kernel.put(0, 0, 0, -1, 0, -1, 4, -1, 0, -1, 0); return applyKernel(image, kernel); } public static GrayScaleMatrix laplacian8(Image image) { Mat kernel = new Mat(3, 3, CvType.CV_64F); - - kernel.put(0, 0, - -1, -1, -1, - -1, 8, -1, - -1, -1, -1 - ); - + kernel.put(0, 0, -1, -1, -1, -1, 8, -1, -1, -1, -1); return applyKernel(image, kernel); } private static GrayScaleMatrix applyKernel(Image image, Mat kernel) { GrayScaleMatrix gray = image.toGrayScale(); - - Mat input = new Mat( - gray.getHeight(), - gray.getWidth(), - CvType.CV_64F - ); - + Mat input = new Mat(gray.getHeight(), gray.getWidth(), CvType.CV_64F); double[][] data = gray.getRawData(); - for (int y = 0; y < gray.getHeight(); y++) { input.put(y, 0, data[y]); } - Mat result = new Mat(); - - Imgproc.filter2D( - input, - result, - CvType.CV_64F, - kernel - ); - - double[][] output = new double[ - result.rows() - ][result.cols()]; - + Imgproc.filter2D(input, result, CvType.CV_64F, kernel); + double[][] output = new double[result.rows()][result.cols()]; for (int y = 0; y < result.rows(); y++) { - for (int x = 0; x < result.cols(); x++) { - output[y][x] = result.get(y, x)[0]; - } + result.get(y, 0, output[y]); } - + input.release(); + result.release(); + kernel.release(); return new GrayScaleMatrix(output); } -} \ No newline at end of file +} diff --git a/src/main/java/domain/image/processing/outline/NonLinearOutline.java b/src/main/java/domain/image/processing/outline/NonLinearOutline.java index c2d4dd4..0c2ba6d 100644 --- a/src/main/java/domain/image/processing/outline/NonLinearOutline.java +++ b/src/main/java/domain/image/processing/outline/NonLinearOutline.java @@ -5,48 +5,40 @@ import domain.image.Image; import domain.image.processing.morphology.BasicMorphology; public class NonLinearOutline { + private static final int MASK_SIZE = 3; public static GrayScaleMatrix gradientErosion(Image image) { GrayScaleMatrix original = image.toGrayScale(); - GrayScaleMatrix erosion = BasicMorphology.erosion(image, 3); - + GrayScaleMatrix erosion = BasicMorphology.erosion(image, MASK_SIZE); return subtract(original, erosion); } public static GrayScaleMatrix gradientDilation(Image image) { GrayScaleMatrix original = image.toGrayScale(); - GrayScaleMatrix dilation = BasicMorphology.dilation(image, 3); - + GrayScaleMatrix dilation = BasicMorphology.dilation(image, MASK_SIZE); return subtract(dilation, original); } public static GrayScaleMatrix beucherGradient(Image image) { - GrayScaleMatrix erosion = BasicMorphology.erosion(image, 3); - GrayScaleMatrix dilation = BasicMorphology.dilation(image, 3); - + GrayScaleMatrix erosion = BasicMorphology.erosion(image, MASK_SIZE); + GrayScaleMatrix dilation = BasicMorphology.dilation(image, MASK_SIZE); return subtract(dilation, erosion); } public static GrayScaleMatrix nonLinearLaplacian(Image image) { GrayScaleMatrix original = image.toGrayScale(); - GrayScaleMatrix erosion = BasicMorphology.erosion(image, 3); - GrayScaleMatrix dilation = BasicMorphology.dilation(image, 3); - + GrayScaleMatrix erosion = BasicMorphology.erosion(image, MASK_SIZE); + GrayScaleMatrix dilation = BasicMorphology.dilation(image, MASK_SIZE); int height = original.getHeight(); int width = original.getWidth(); - - double[][] result = new double[height][width]; - double[][] originalData = original.getRawData(); double[][] erosionData = erosion.getRawData(); double[][] dilationData = dilation.getRawData(); + double[][] result = new double[height][width]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - result[y][x] = - dilationData[y][x] - + erosionData[y][x] - - 2 * originalData[y][x]; + result[y][x] = dilationData[y][x] + erosionData[y][x] - 2 * originalData[y][x]; } } @@ -56,16 +48,13 @@ public class NonLinearOutline { private static GrayScaleMatrix subtract(GrayScaleMatrix first, GrayScaleMatrix second) { int height = first.getHeight(); int width = first.getWidth(); - double[][] firstData = first.getRawData(); double[][] secondData = second.getRawData(); - double[][] result = new double[height][width]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - result[y][x] = - firstData[y][x] - secondData[y][x]; + result[y][x] = firstData[y][x] - secondData[y][x]; } } diff --git a/src/main/java/domain/image/processing/threshold/Thresholding.java b/src/main/java/domain/image/processing/threshold/Thresholding.java index 1aab039..9ac8f6d 100644 --- a/src/main/java/domain/image/processing/threshold/Thresholding.java +++ b/src/main/java/domain/image/processing/threshold/Thresholding.java @@ -4,54 +4,29 @@ import domain.image.GrayScaleMatrix; import domain.image.Image; public class Thresholding { - - public static GrayScaleMatrix simpleThreshold( - Image image, - int threshold - ) { + public static GrayScaleMatrix simpleThreshold(Image image, int threshold) { + validateThreshold(threshold); GrayScaleMatrix gray = image.toGrayScale(); - - int height = gray.getHeight(); - int width = gray.getWidth(); - - double[][] data = gray.getRawData(); - double[][] result = new double[height][width]; - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - result[y][x] = data[y][x] >= threshold - ? 255 - : 0; - } - } - - return new GrayScaleMatrix(result); + return applySimpleThreshold(gray, threshold); } - public static GrayScaleMatrix doubleThreshold( - Image image, - int threshold1, - int threshold2 - ) { + public static GrayScaleMatrix doubleThreshold(Image image, int threshold1, int threshold2) { + validateThreshold(threshold1); + validateThreshold(threshold2); if (threshold1 > threshold2) { int temp = threshold1; threshold1 = threshold2; threshold2 = temp; } - GrayScaleMatrix gray = image.toGrayScale(); - int height = gray.getHeight(); int width = gray.getWidth(); - double[][] data = gray.getRawData(); double[][] result = new double[height][width]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - double value = data[y][x]; - if (value < threshold1) { result[y][x] = 0; } else if (value < threshold2) { @@ -67,23 +42,18 @@ public class Thresholding { public static GrayScaleMatrix automaticThreshold(Image image) { GrayScaleMatrix gray = image.toGrayScale(); - double[][] data = gray.getRawData(); - double threshold = (gray.getMinValue() + gray.getMaxValue()) / 2.0; while (true) { double lowerSum = 0; double upperSum = 0; - int lowerCount = 0; int upperCount = 0; for (int y = 0; y < gray.getHeight(); y++) { for (int x = 0; x < gray.getWidth(); x++) { - double value = data[y][x]; - if (value <= threshold) { lowerSum += value; lowerCount++; @@ -100,7 +70,6 @@ public class Thresholding { double lowerMean = lowerSum / lowerCount; double upperMean = upperSum / upperCount; - double newThreshold = (lowerMean + upperMean) / 2.0; if (Math.abs(newThreshold - threshold) < 0.5) { @@ -111,6 +80,27 @@ public class Thresholding { threshold = newThreshold; } - return simpleThreshold(image, (int) threshold); + return applySimpleThreshold(gray, (int) threshold); + } + + private static GrayScaleMatrix applySimpleThreshold(GrayScaleMatrix gray, int threshold) { + int height = gray.getHeight(); + int width = gray.getWidth(); + double[][] data = gray.getRawData(); + double[][] result = new double[height][width]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + result[y][x] = data[y][x] >= threshold ? 255 : 0; + } + } + + return new GrayScaleMatrix(result); + } + + private static void validateThreshold(int threshold) { + if (threshold < 0 || threshold > 255) { + throw new IllegalArgumentException("Le seuil doit être compris entre 0 et 255."); + } } } \ No newline at end of file diff --git a/src/main/java/services/UseCaseService.java b/src/main/java/services/UseCaseService.java index 25471ae..95359a9 100644 --- a/src/main/java/services/UseCaseService.java +++ b/src/main/java/services/UseCaseService.java @@ -10,10 +10,15 @@ import java.util.function.Supplier; public class UseCaseService { public Image reduceNoise(Image i0) { + + double cutoff = 0.7 * getMaxCutoff(i0.getWidth(), i0.getHeight()); + GrayScaleMatrix m0 = i0.toGrayScale(); - GrayScaleMatrix m1 = GlobalLinearFiltering.butterworthLowPassFiltering(m0, 10, 1); + + GrayScaleMatrix m1 = measureTime("Butterworth low pass", () -> GlobalLinearFiltering.butterworthLowPassFiltering(m0, cutoff, 1)); Image i1 = m1.toImage(); - return ComplexMorphology.medianFilter(i1, 9).toImage(); + + return measureTime("Median filter", () -> ComplexMorphology.medianFilter(i1, 5).toImage()); } public Image[] equalizeHistogram(Image image) { @@ -88,10 +93,19 @@ public class UseCaseService { return image; } - public long measureTime(Supplier operation) { + public T measureTime(String name, Supplier operation) { long start = System.nanoTime(); - operation.get(); - return (System.nanoTime() - start) / 1_000_000; + + T result = operation.get(); + + long elapsed = (System.nanoTime() - start) / 1_000_000; + System.out.println(name + " took " + elapsed + " ms"); + + return result; + } + + private double getMaxCutoff(int width, int height) { + return Math.sqrt(Math.pow(width / 2.0, 2) + Math.pow(height / 2.0, 2)); } } \ No newline at end of file