Clean some stuff

This commit is contained in:
2026-10-04 13:03:49 +02:00
parent 114da44ff4
commit 6373978aac
8 changed files with 249 additions and 363 deletions

View File

@@ -8,43 +8,34 @@ import org.opencv.core.Mat;
public class GlobalLinearFiltering { public class GlobalLinearFiltering {
public static GrayScaleMatrix lowPassFiltering(GrayScaleMatrix matrix, double cutoff) { public static GrayScaleMatrix lowPassFiltering(GrayScaleMatrix matrix, double cutoff) {
int width = matrix.getWidth(); validateCutoff(cutoff, matrix.getWidth(), matrix.getHeight());
int height = matrix.getHeight();
validateCutoff(cutoff, width, height); Mat spectrum = fourierTransform(toMat(matrix));
Mat mat = toMat(matrix);
Mat spectrum = fourierTransform(mat);
shiftSpectrum(spectrum); shiftSpectrum(spectrum);
applyIdealLowPassFilter(spectrum, cutoff); applyIdealLowPassFilter(spectrum, cutoff);
shiftSpectrum(spectrum); shiftSpectrum(spectrum);
Mat filtered = inverseFourierTransform(spectrum); Mat filtered = inverseFourierTransform(spectrum);
return toGrayScaleMatrix(filtered); return toGrayScaleMatrix(filtered);
} }
public static GrayScaleMatrix highPassFiltering(GrayScaleMatrix matrix, double cutoff) { public static GrayScaleMatrix highPassFiltering(GrayScaleMatrix matrix, double cutoff) {
int width = matrix.getWidth(); validateCutoff(cutoff, matrix.getWidth(), matrix.getHeight());
int height = matrix.getHeight();
validateCutoff(cutoff, width, height); Mat spectrum = fourierTransform(toMat(matrix));
Mat mat = toMat(matrix);
Mat spectrum = fourierTransform(mat);
shiftSpectrum(spectrum); shiftSpectrum(spectrum);
applyIdealHighPassFilter(spectrum, cutoff); applyIdealHighPassFilter(spectrum, cutoff);
shiftSpectrum(spectrum); shiftSpectrum(spectrum);
Mat filtered = inverseFourierTransform(spectrum); Mat filtered = inverseFourierTransform(spectrum);
return toGrayScaleMatrix(filtered); return toGrayScaleMatrix(filtered);
} }
public static GrayScaleMatrix butterworthLowPassFiltering(GrayScaleMatrix matrix, double cutoff, int order) { public static GrayScaleMatrix butterworthLowPassFiltering(GrayScaleMatrix matrix, double cutoff, int order) {
validateCutoff(cutoff, matrix.getWidth(), matrix.getHeight()); validateCutoff(cutoff, matrix.getWidth(), matrix.getHeight());
validateOrder(order);
Mat spectrum = fourierTransform(toMat(matrix)); Mat spectrum = fourierTransform(toMat(matrix));
@@ -52,11 +43,13 @@ public class GlobalLinearFiltering {
applyButterworthLowPassFilter(spectrum, cutoff, order); applyButterworthLowPassFilter(spectrum, cutoff, order);
shiftSpectrum(spectrum); shiftSpectrum(spectrum);
return toGrayScaleMatrix(inverseFourierTransform(spectrum)); Mat filtered = inverseFourierTransform(spectrum);
return toGrayScaleMatrix(filtered);
} }
public static GrayScaleMatrix butterworthHighPassFiltering(GrayScaleMatrix matrix, double cutoff, int order) { public static GrayScaleMatrix butterworthHighPassFiltering(GrayScaleMatrix matrix, double cutoff, int order) {
validateCutoff(cutoff, matrix.getWidth(), matrix.getHeight()); validateCutoff(cutoff, matrix.getWidth(), matrix.getHeight());
validateOrder(order);
Mat spectrum = fourierTransform(toMat(matrix)); Mat spectrum = fourierTransform(toMat(matrix));
@@ -64,25 +57,23 @@ public class GlobalLinearFiltering {
applyButterworthHighPassFilter(spectrum, cutoff, order); applyButterworthHighPassFilter(spectrum, cutoff, order);
shiftSpectrum(spectrum); shiftSpectrum(spectrum);
return toGrayScaleMatrix(inverseFourierTransform(spectrum)); Mat filtered = inverseFourierTransform(spectrum);
return toGrayScaleMatrix(filtered);
} }
private static void applyIdealLowPassFilter(Mat spectrum, double cutoff) { private static void applyIdealLowPassFilter(Mat spectrum, double cutoff) {
int width = spectrum.cols(); int width = spectrum.cols();
int height = spectrum.rows(); int height = spectrum.rows();
int cx = width / 2; double cx = width / 2.0;
int cy = height / 2; double cy = height / 2.0;
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { 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)
);
if (distance > cutoff) { 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 width = spectrum.cols();
int height = spectrum.rows(); int height = spectrum.rows();
int cx = width / 2; double cx = width / 2.0;
int cy = height / 2; double cy = height / 2.0;
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { 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));
if (distance <= cutoff) { 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 width = spectrum.cols();
int height = spectrum.rows(); int height = spectrum.rows();
int cx = width / 2; double cx = width / 2.0;
int cy = height / 2; double cy = height / 2.0;
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { 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 = 1.0 / (1.0 + Math.pow(distance / cutoff, 2.0 * order));
double coefficient = 1.0 / (1.0 + Math.pow(distance / cutoff, 2 * order));
double[] value = spectrum.get(y, x); double[] value = spectrum.get(y, x);
spectrum.put(y, x, value[0] * coefficient, value[1] * coefficient); spectrum.put(y, x, value[0] * coefficient, value[1] * coefficient);
} }
} }
@@ -132,45 +119,49 @@ public class GlobalLinearFiltering {
int width = spectrum.cols(); int width = spectrum.cols();
int height = spectrum.rows(); int height = spectrum.rows();
int cx = width / 2; double cx = width / 2.0;
int cy = height / 2; double cy = height / 2.0;
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { 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 = distance == 0.0
? 0.0
double coefficient; : 1.0 / (1.0 + Math.pow(cutoff / distance, 2.0 * order));
if (distance == 0) {
coefficient = 0;
} else {
coefficient = 1.0 / (1.0 + Math.pow(cutoff / distance, 2 * order));
}
double[] value = spectrum.get(y, x); double[] value = spectrum.get(y, x);
spectrum.put(y, x, value[0] * coefficient, value[1] * coefficient); spectrum.put(y, x, value[0] * coefficient, value[1] * coefficient);
} }
} }
} }
private static void validateCutoff(double cutoff, int width, int height) { private static double distance(double x, double y, double cx, double cy) {
double minCutoff = 0; double dx = x - cx;
double maxCutoff = Math.sqrt( double dy = y - cy;
Math.pow(width / 2.0, 2) + return Math.sqrt(dx * dx + dy * dy);
Math.pow(height / 2.0, 2) }
);
if (cutoff < minCutoff || cutoff > maxCutoff) { private static void shiftSpectrum(Mat spectrum) {
throw new IllegalArgumentException( int width = spectrum.cols();
String.format( int height = spectrum.rows();
"Cutoff value is out of bounds (%f - %f)",
minCutoff, Mat shifted = Mat.zeros(spectrum.size(), spectrum.type());
maxCutoff
) 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) { private static Mat fourierTransform(Mat mat) {
@@ -179,54 +170,20 @@ public class GlobalLinearFiltering {
return spectrum; 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) { private static Mat inverseFourierTransform(Mat spectrum) {
Mat filtered = new Mat(); 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; return filtered;
} }
private static GrayScaleMatrix toGrayScaleMatrix(Mat filtered) { private static GrayScaleMatrix toGrayScaleMatrix(Mat matrix) {
int width = filtered.cols(); int width = matrix.cols();
int height = filtered.rows(); int height = matrix.rows();
double[][] pixels = new double[height][width]; double[][] pixels = new double[height][width];
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
pixels[y][x] = Math.clamp( pixels[y][x] = Math.clamp(matrix.get(y, x)[0], 0.0, 255.0);
filtered.get(y, x)[0],
0.0,
255.0
);
} }
} }
@@ -236,7 +193,6 @@ public class GlobalLinearFiltering {
private static Mat toMat(GrayScaleMatrix matrix) { private static Mat toMat(GrayScaleMatrix matrix) {
int width = matrix.getWidth(); int width = matrix.getWidth();
int height = matrix.getHeight(); int height = matrix.getHeight();
Mat mat = new Mat(height, width, CvType.CV_64F); Mat mat = new Mat(height, width, CvType.CV_64F);
double[][] data = matrix.getRawData(); double[][] data = matrix.getRawData();
@@ -248,4 +204,22 @@ public class GlobalLinearFiltering {
return mat; 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");
}
}
} }

View File

@@ -8,7 +8,6 @@ import org.opencv.core.Point;
import org.opencv.imgproc.Imgproc; import org.opencv.imgproc.Imgproc;
public class LocalLinearFiltering { public class LocalLinearFiltering {
public static GrayScaleMatrix convolutionMask(GrayScaleMatrix matrix, double[][] mask) { public static GrayScaleMatrix convolutionMask(GrayScaleMatrix matrix, double[][] mask) {
validateMask(mask); validateMask(mask);
return applyMask(matrix, mask); return applyMask(matrix, mask);
@@ -16,91 +15,69 @@ public class LocalLinearFiltering {
public static GrayScaleMatrix meanFilter(GrayScaleMatrix matrix, int maskSize) { public static GrayScaleMatrix meanFilter(GrayScaleMatrix matrix, int maskSize) {
validateMaskSize(maskSize); validateMaskSize(maskSize);
double coefficient = 1.0 / (maskSize * maskSize); double coefficient = 1.0 / (maskSize * maskSize);
double[][] mask = new double[maskSize][maskSize]; double[][] mask = new double[maskSize][maskSize];
for (int y = 0; y < maskSize; y++) { for (int y = 0; y < maskSize; y++) {
for (int x = 0; x < maskSize; x++) { for (int x = 0; x < maskSize; x++) {
mask[y][x] = coefficient; mask[y][x] = coefficient;
} }
} }
return applyMask(matrix, mask); return applyMask(matrix, mask);
} }
private static GrayScaleMatrix applyMask(GrayScaleMatrix matrix, double[][] mask) { private static GrayScaleMatrix applyMask(GrayScaleMatrix matrix, double[][] mask) {
Mat source = toMat(matrix); Mat source = toMat(matrix);
int maskSize = mask.length; Mat kernel = toMat(mask);
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 destination = new Mat(); 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); GrayScaleMatrix result = fromMat(destination);
source.release(); source.release();
kernel.release(); kernel.release();
destination.release(); destination.release();
return result; return result;
} }
private static void validateMask(double[][] mask) { private static void validateMask(double[][] mask) {
if (mask == null || mask.length == 0 || mask.length % 2 == 0) { if (mask == null || mask.length == 0 || mask.length % 2 == 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException("Le masque doit être une matrice carrée de dimension impaire.");
"Le masque doit être une matrice carrée de dimension impaire."
);
} }
int size = mask.length; int size = mask.length;
for (double[] row : mask) { for (double[] row : mask) {
if (row == null || row.length != size) { if (row == null || row.length != size) {
throw new IllegalArgumentException( throw new IllegalArgumentException("Le masque doit être une matrice carrée.");
"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) { private static void validateMaskSize(int maskSize) {
if (maskSize <= 0 || maskSize % 2 == 0) { if (maskSize <= 0 || maskSize % 2 == 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException("La taille du masque doit être un entier positif impair.");
"La taille du masque doit être un entier positif impair."
);
} }
} }
private static Mat toMat(GrayScaleMatrix matrix) { private static Mat toMat(GrayScaleMatrix matrix) {
double[][] image = matrix.getRawData(); double[][] data = matrix.getRawData();
int height = image.length; int height = data.length;
int width = image[0].length; int width = data[0].length;
Mat mat = new Mat(height, width, CvType.CV_64F); Mat mat = new Mat(height, width, CvType.CV_64F);
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { mat.put(y, 0, data[y]);
mat.put(y, x, image[y][x]);
}
} }
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; return mat;
} }
@@ -108,13 +85,13 @@ public class LocalLinearFiltering {
int height = mat.rows(); int height = mat.rows();
int width = mat.cols(); int width = mat.cols();
double[][] result = new double[height][width]; double[][] result = new double[height][width];
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
double[] row = new double[width];
mat.get(y, 0, row);
for (int x = 0; x < width; x++) { 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); return new GrayScaleMatrix(result);
} }
} }

View File

@@ -8,94 +8,82 @@ import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc; import org.opencv.imgproc.Imgproc;
public class BasicMorphology { public class BasicMorphology {
public static GrayScaleMatrix erosion(Image image, int tailleMasque) { public static GrayScaleMatrix erosion(Image image, int tailleMasque) {
Mat input = toMat(image); Mat input = toMat(image);
Mat kernel = createKernel(tailleMasque); Mat kernel = createKernel(tailleMasque);
Mat result = new Mat(); Mat result = new Mat();
Imgproc.erode(input, result, kernel); Imgproc.erode(input, result, kernel);
GrayScaleMatrix output = toGrayScaleMatrix(result);
return toGrayScaleMatrix(result); input.release();
kernel.release();
result.release();
return output;
} }
public static GrayScaleMatrix dilation(Image image, int tailleMasque) { public static GrayScaleMatrix dilation(Image image, int tailleMasque) {
Mat input = toMat(image); Mat input = toMat(image);
Mat kernel = createKernel(tailleMasque); Mat kernel = createKernel(tailleMasque);
Mat result = new Mat(); Mat result = new Mat();
Imgproc.dilate(input, result, kernel); Imgproc.dilate(input, result, kernel);
GrayScaleMatrix output = toGrayScaleMatrix(result);
return toGrayScaleMatrix(result); input.release();
kernel.release();
result.release();
return output;
} }
public static GrayScaleMatrix opening(Image image, int tailleMasque) { public static GrayScaleMatrix opening(Image image, int tailleMasque) {
Mat input = toMat(image); Mat input = toMat(image);
Mat kernel = createKernel(tailleMasque); Mat kernel = createKernel(tailleMasque);
Mat result = new Mat(); Mat result = new Mat();
Imgproc.morphologyEx(input, result, Imgproc.MORPH_OPEN, kernel);
Imgproc.morphologyEx( GrayScaleMatrix output = toGrayScaleMatrix(result);
input, input.release();
result, kernel.release();
Imgproc.MORPH_OPEN, result.release();
kernel return output;
);
return toGrayScaleMatrix(result);
} }
public static GrayScaleMatrix closing(Image image, int tailleMasque) { public static GrayScaleMatrix closing(Image image, int tailleMasque) {
Mat input = toMat(image); Mat input = toMat(image);
Mat kernel = createKernel(tailleMasque); Mat kernel = createKernel(tailleMasque);
Mat result = new Mat(); Mat result = new Mat();
Imgproc.morphologyEx(input, result, Imgproc.MORPH_CLOSE, kernel);
Imgproc.morphologyEx( GrayScaleMatrix output = toGrayScaleMatrix(result);
input, input.release();
result, kernel.release();
Imgproc.MORPH_CLOSE, result.release();
kernel return output;
);
return toGrayScaleMatrix(result);
} }
private static Mat toMat(Image image) { private static Mat toMat(Image image) {
GrayScaleMatrix gray = image.toGrayScale(); GrayScaleMatrix gray = image.toGrayScale();
int width = gray.getWidth(); int width = gray.getWidth();
int height = gray.getHeight(); int height = gray.getHeight();
Mat mat = new Mat(height, width, CvType.CV_64F); Mat mat = new Mat(height, width, CvType.CV_64F);
double[][] data = gray.getRawData(); double[][] data = gray.getRawData();
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
mat.put(y, 0, data[y]); mat.put(y, 0, data[y]);
} }
return mat; return mat;
} }
private static Mat createKernel(int tailleMasque) { private static Mat createKernel(int tailleMasque) {
return Imgproc.getStructuringElement( if (tailleMasque <= 0 || tailleMasque % 2 == 0) {
Imgproc.MORPH_RECT, throw new IllegalArgumentException("La taille du masque doit être un entier positif impair.");
new Size(tailleMasque, tailleMasque) }
); return Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(tailleMasque, tailleMasque));
} }
private static GrayScaleMatrix toGrayScaleMatrix(Mat mat) { private static GrayScaleMatrix toGrayScaleMatrix(Mat mat) {
int width = mat.cols(); int width = mat.cols();
int height = mat.rows(); int height = mat.rows();
double[][] output = new double[height][width]; double[][] output = new double[height][width];
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { double[] row = new double[width];
output[y][x] = mat.get(y, x)[0]; mat.get(y, 0, row);
} System.arraycopy(row, 0, output[y], 0, width);
} }
return new GrayScaleMatrix(output); return new GrayScaleMatrix(output);
} }
} }

View File

@@ -11,23 +11,33 @@ import org.opencv.imgproc.Imgproc;
public class ComplexMorphology { public class ComplexMorphology {
public static GrayScaleMatrix geodesicDilation(Image image, Image mask, int nbIter) { 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 maskMat = toMat(mask);
Mat current = toMat(image); Mat current = toMat(image);
Mat kernel = createKernel(3); Mat kernel = createKernel(3);
for (int i = 0; i < nbIter; i++) { for (int i = 0; i < nbIter; i++) {
Mat dilated = new Mat(); Mat dilated = new Mat();
Imgproc.dilate(current, dilated, kernel); Imgproc.dilate(current, dilated, kernel);
Mat result = new Mat(); Mat result = new Mat();
Core.min(dilated, maskMat, result); Core.min(dilated, maskMat, result);
current.release();
dilated.release();
current = result; 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) { public static GrayScaleMatrix geodesicReconstruction(Image image, Image mask) {
@@ -37,48 +47,51 @@ public class ComplexMorphology {
while (true) { while (true) {
Mat dilated = new Mat(); Mat dilated = new Mat();
Imgproc.dilate(current, dilated, kernel); Imgproc.dilate(current, dilated, kernel);
Mat next = new Mat(); Mat next = new Mat();
Core.min(dilated, maskMat, next); Core.min(dilated, maskMat, next);
double difference = Core.norm(next, current, Core.NORM_INF); double difference = Core.norm(next, current, Core.NORM_INF);
if (difference == 0) { current.release();
return toGrayScaleMatrix(next); dilated.release();
if (difference == 0.0) {
GrayScaleMatrix result = toGrayScaleMatrix(next);
next.release();
maskMat.release();
kernel.release();
return result;
} }
current = next; current = next;
} }
} }
public static GrayScaleMatrix medianFilter(Image image, int tailleMasque) { public static GrayScaleMatrix medianFilter(Image image, int maskSize) {
if (tailleMasque < 1 || tailleMasque % 2 == 0) { validateMedianSize(maskSize);
throw new IllegalArgumentException(
"Median filter size must be a positive odd number"
);
}
Mat input = to8BitMat(image); Mat input = to8BitMat(image);
Mat result = new Mat(); 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) { private static Mat toMat(Image image) {
GrayScaleMatrix gray = image.toGrayScale(); GrayScaleMatrix gray = image.toGrayScale();
int width = gray.getWidth(); int width = gray.getWidth();
int height = gray.getHeight(); int height = gray.getHeight();
Mat mat = new Mat(height, width, CvType.CV_64F); Mat mat = new Mat(height, width, CvType.CV_64F);
double[][] data = gray.getRawData(); double[][] data = gray.getRawData();
@@ -90,16 +103,11 @@ public class ComplexMorphology {
return mat; return mat;
} }
/**
* Converts an image to an 8-bit grayscale matrix.
* Required by OpenCV medianBlur().
*/
private static Mat to8BitMat(Image image) { private static Mat to8BitMat(Image image) {
GrayScaleMatrix gray = image.toGrayScale(); GrayScaleMatrix gray = image.toGrayScale();
int width = gray.getWidth(); int width = gray.getWidth();
int height = gray.getHeight(); int height = gray.getHeight();
Mat mat = new Mat(height, width, CvType.CV_8UC1); Mat mat = new Mat(height, width, CvType.CV_8UC1);
double[][] data = gray.getRawData(); double[][] data = gray.getRawData();
@@ -108,9 +116,7 @@ public class ComplexMorphology {
byte[] row = new byte[width]; byte[] row = new byte[width];
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
int value = (int) Math.round(data[y][x]); int value = Math.clamp((int) Math.round(data[y][x]), 0, 255);
value = Math.clamp(value, 0, 255);
row[x] = (byte) value; row[x] = (byte) value;
} }
@@ -121,14 +127,15 @@ public class ComplexMorphology {
} }
private static Mat createKernel(int size) { 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) { private static GrayScaleMatrix toGrayScaleMatrix(Mat mat) {
int width = mat.cols(); int width = mat.cols();
int height = mat.rows(); int height = mat.rows();
double[][] output = new double[height][width]; double[][] output = new double[height][width];
if (mat.type() == CvType.CV_8UC1) { if (mat.type() == CvType.CV_8UC1) {
@@ -150,4 +157,12 @@ public class ComplexMorphology {
return new GrayScaleMatrix(output); 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"
);
}
}
} }

View File

@@ -7,121 +7,60 @@ import org.opencv.core.Mat;
import org.opencv.imgproc.Imgproc; import org.opencv.imgproc.Imgproc;
public class LinearOutline { public class LinearOutline {
/**
*
* @param image
* @param direction applied horizontally if 1, applied vertically if 2
* @return
*/
public static GrayScaleMatrix prewittGradient(Image image, int direction) { public static GrayScaleMatrix prewittGradient(Image image, int direction) {
Mat kernel = new Mat(3, 3, CvType.CV_64F); Mat kernel = new Mat(3, 3, CvType.CV_64F);
if (direction == 1) { if (direction == 1) {
kernel.put(0, 0, kernel.put(0, 0, -1, -1, -1, 0, 0, 0, 1, 1, 1);
-1, -1, -1,
0, 0, 0,
1, 1, 1
);
} else if (direction == 2) { } else if (direction == 2) {
kernel.put(0, 0, kernel.put(0, 0, -1, 0, 1, -1, 0, 1, -1, 0, 1);
-1, 0, 1,
-1, 0, 1,
-1, 0, 1
);
} else { } else {
kernel.release();
throw new IllegalArgumentException("Direction must be 1 or 2"); throw new IllegalArgumentException("Direction must be 1 or 2");
} }
return applyKernel(image, kernel); 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) { public static GrayScaleMatrix sobelGradient(Image image, int direction) {
Mat kernel = new Mat(3, 3, CvType.CV_64F); Mat kernel = new Mat(3, 3, CvType.CV_64F);
if (direction == 1) { if (direction == 1) {
kernel.put(0, 0, kernel.put(0, 0, -1, -2, -1, 0, 0, 0, 1, 2, 1);
-1, -2, -1,
0, 0, 0,
1, 2, 1
);
} else if (direction == 2) { } else if (direction == 2) {
kernel.put(0, 0, kernel.put(0, 0, -1, 0, 1, -2, 0, 2, -1, 0, 1);
-1, 0, 1,
-2, 0, 2,
-1, 0, 1
);
} else { } else {
kernel.release();
throw new IllegalArgumentException("Direction must be 1 or 2"); throw new IllegalArgumentException("Direction must be 1 or 2");
} }
return applyKernel(image, kernel); return applyKernel(image, kernel);
} }
public static GrayScaleMatrix laplacian4(Image image) { public static GrayScaleMatrix laplacian4(Image image) {
Mat kernel = new Mat(3, 3, CvType.CV_64F); 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); return applyKernel(image, kernel);
} }
public static GrayScaleMatrix laplacian8(Image image) { public static GrayScaleMatrix laplacian8(Image image) {
Mat kernel = new Mat(3, 3, CvType.CV_64F); 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); return applyKernel(image, kernel);
} }
private static GrayScaleMatrix applyKernel(Image image, Mat kernel) { private static GrayScaleMatrix applyKernel(Image image, Mat kernel) {
GrayScaleMatrix gray = image.toGrayScale(); 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(); double[][] data = gray.getRawData();
for (int y = 0; y < gray.getHeight(); y++) { for (int y = 0; y < gray.getHeight(); y++) {
input.put(y, 0, data[y]); input.put(y, 0, data[y]);
} }
Mat result = new Mat(); Mat result = new Mat();
Imgproc.filter2D(input, result, CvType.CV_64F, kernel);
Imgproc.filter2D( double[][] output = new double[result.rows()][result.cols()];
input,
result,
CvType.CV_64F,
kernel
);
double[][] output = new double[
result.rows()
][result.cols()];
for (int y = 0; y < result.rows(); y++) { for (int y = 0; y < result.rows(); y++) {
for (int x = 0; x < result.cols(); x++) { result.get(y, 0, output[y]);
output[y][x] = result.get(y, x)[0];
}
} }
input.release();
result.release();
kernel.release();
return new GrayScaleMatrix(output); return new GrayScaleMatrix(output);
} }
} }

View File

@@ -5,48 +5,40 @@ import domain.image.Image;
import domain.image.processing.morphology.BasicMorphology; import domain.image.processing.morphology.BasicMorphology;
public class NonLinearOutline { public class NonLinearOutline {
private static final int MASK_SIZE = 3;
public static GrayScaleMatrix gradientErosion(Image image) { public static GrayScaleMatrix gradientErosion(Image image) {
GrayScaleMatrix original = image.toGrayScale(); GrayScaleMatrix original = image.toGrayScale();
GrayScaleMatrix erosion = BasicMorphology.erosion(image, 3); GrayScaleMatrix erosion = BasicMorphology.erosion(image, MASK_SIZE);
return subtract(original, erosion); return subtract(original, erosion);
} }
public static GrayScaleMatrix gradientDilation(Image image) { public static GrayScaleMatrix gradientDilation(Image image) {
GrayScaleMatrix original = image.toGrayScale(); GrayScaleMatrix original = image.toGrayScale();
GrayScaleMatrix dilation = BasicMorphology.dilation(image, 3); GrayScaleMatrix dilation = BasicMorphology.dilation(image, MASK_SIZE);
return subtract(dilation, original); return subtract(dilation, original);
} }
public static GrayScaleMatrix beucherGradient(Image image) { public static GrayScaleMatrix beucherGradient(Image image) {
GrayScaleMatrix erosion = BasicMorphology.erosion(image, 3); GrayScaleMatrix erosion = BasicMorphology.erosion(image, MASK_SIZE);
GrayScaleMatrix dilation = BasicMorphology.dilation(image, 3); GrayScaleMatrix dilation = BasicMorphology.dilation(image, MASK_SIZE);
return subtract(dilation, erosion); return subtract(dilation, erosion);
} }
public static GrayScaleMatrix nonLinearLaplacian(Image image) { public static GrayScaleMatrix nonLinearLaplacian(Image image) {
GrayScaleMatrix original = image.toGrayScale(); GrayScaleMatrix original = image.toGrayScale();
GrayScaleMatrix erosion = BasicMorphology.erosion(image, 3); GrayScaleMatrix erosion = BasicMorphology.erosion(image, MASK_SIZE);
GrayScaleMatrix dilation = BasicMorphology.dilation(image, 3); GrayScaleMatrix dilation = BasicMorphology.dilation(image, MASK_SIZE);
int height = original.getHeight(); int height = original.getHeight();
int width = original.getWidth(); int width = original.getWidth();
double[][] result = new double[height][width];
double[][] originalData = original.getRawData(); double[][] originalData = original.getRawData();
double[][] erosionData = erosion.getRawData(); double[][] erosionData = erosion.getRawData();
double[][] dilationData = dilation.getRawData(); double[][] dilationData = dilation.getRawData();
double[][] result = new double[height][width];
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
result[y][x] = result[y][x] = dilationData[y][x] + erosionData[y][x] - 2 * originalData[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) { private static GrayScaleMatrix subtract(GrayScaleMatrix first, GrayScaleMatrix second) {
int height = first.getHeight(); int height = first.getHeight();
int width = first.getWidth(); int width = first.getWidth();
double[][] firstData = first.getRawData(); double[][] firstData = first.getRawData();
double[][] secondData = second.getRawData(); double[][] secondData = second.getRawData();
double[][] result = new double[height][width]; double[][] result = new double[height][width];
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
result[y][x] = result[y][x] = firstData[y][x] - secondData[y][x];
firstData[y][x] - secondData[y][x];
} }
} }

View File

@@ -4,54 +4,29 @@ import domain.image.GrayScaleMatrix;
import domain.image.Image; import domain.image.Image;
public class Thresholding { public class Thresholding {
public static GrayScaleMatrix simpleThreshold(Image image, int threshold) {
public static GrayScaleMatrix simpleThreshold( validateThreshold(threshold);
Image image,
int threshold
) {
GrayScaleMatrix gray = image.toGrayScale(); GrayScaleMatrix gray = image.toGrayScale();
return applySimpleThreshold(gray, 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);
} }
public static GrayScaleMatrix doubleThreshold( public static GrayScaleMatrix doubleThreshold(Image image, int threshold1, int threshold2) {
Image image, validateThreshold(threshold1);
int threshold1, validateThreshold(threshold2);
int threshold2
) {
if (threshold1 > threshold2) { if (threshold1 > threshold2) {
int temp = threshold1; int temp = threshold1;
threshold1 = threshold2; threshold1 = threshold2;
threshold2 = temp; threshold2 = temp;
} }
GrayScaleMatrix gray = image.toGrayScale(); GrayScaleMatrix gray = image.toGrayScale();
int height = gray.getHeight(); int height = gray.getHeight();
int width = gray.getWidth(); int width = gray.getWidth();
double[][] data = gray.getRawData(); double[][] data = gray.getRawData();
double[][] result = new double[height][width]; double[][] result = new double[height][width];
for (int y = 0; y < height; y++) { for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) { for (int x = 0; x < width; x++) {
double value = data[y][x]; double value = data[y][x];
if (value < threshold1) { if (value < threshold1) {
result[y][x] = 0; result[y][x] = 0;
} else if (value < threshold2) { } else if (value < threshold2) {
@@ -67,23 +42,18 @@ public class Thresholding {
public static GrayScaleMatrix automaticThreshold(Image image) { public static GrayScaleMatrix automaticThreshold(Image image) {
GrayScaleMatrix gray = image.toGrayScale(); GrayScaleMatrix gray = image.toGrayScale();
double[][] data = gray.getRawData(); double[][] data = gray.getRawData();
double threshold = (gray.getMinValue() + gray.getMaxValue()) / 2.0; double threshold = (gray.getMinValue() + gray.getMaxValue()) / 2.0;
while (true) { while (true) {
double lowerSum = 0; double lowerSum = 0;
double upperSum = 0; double upperSum = 0;
int lowerCount = 0; int lowerCount = 0;
int upperCount = 0; int upperCount = 0;
for (int y = 0; y < gray.getHeight(); y++) { for (int y = 0; y < gray.getHeight(); y++) {
for (int x = 0; x < gray.getWidth(); x++) { for (int x = 0; x < gray.getWidth(); x++) {
double value = data[y][x]; double value = data[y][x];
if (value <= threshold) { if (value <= threshold) {
lowerSum += value; lowerSum += value;
lowerCount++; lowerCount++;
@@ -100,7 +70,6 @@ public class Thresholding {
double lowerMean = lowerSum / lowerCount; double lowerMean = lowerSum / lowerCount;
double upperMean = upperSum / upperCount; double upperMean = upperSum / upperCount;
double newThreshold = (lowerMean + upperMean) / 2.0; double newThreshold = (lowerMean + upperMean) / 2.0;
if (Math.abs(newThreshold - threshold) < 0.5) { if (Math.abs(newThreshold - threshold) < 0.5) {
@@ -111,6 +80,27 @@ public class Thresholding {
threshold = newThreshold; 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.");
}
} }
} }

View File

@@ -10,10 +10,15 @@ import java.util.function.Supplier;
public class UseCaseService { public class UseCaseService {
public Image reduceNoise(Image i0) { public Image reduceNoise(Image i0) {
double cutoff = 0.7 * getMaxCutoff(i0.getWidth(), i0.getHeight());
GrayScaleMatrix m0 = i0.toGrayScale(); 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(); Image i1 = m1.toImage();
return ComplexMorphology.medianFilter(i1, 9).toImage();
return measureTime("Median filter", () -> ComplexMorphology.medianFilter(i1, 5).toImage());
} }
public Image[] equalizeHistogram(Image image) { public Image[] equalizeHistogram(Image image) {
@@ -88,10 +93,19 @@ public class UseCaseService {
return image; return image;
} }
public <T> long measureTime(Supplier<T> operation) { public <T> T measureTime(String name, Supplier<T> operation) {
long start = System.nanoTime(); 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));
} }
} }