diff --git a/build.gradle.kts b/build.gradle.kts index d16acd4..c9ec96c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { implementation("org.jfree:jfreechart:1.5.4") implementation("com.google.inject:guice:7.0.0") implementation("com.google.guava:guava:33.4.0-jre") + implementation("org.openpnp:opencv:4.9.0-0") testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 78cb65c..eb73a46 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -1,6 +1,7 @@ import app.AppModule; import com.google.inject.Guice; import com.google.inject.Injector; +import nu.pattern.OpenCV; import ui.implementation.components.nav.NavBar; import ui.implementation.views.MainView; import ui.interfaces.IMainView; @@ -13,6 +14,7 @@ import javax.swing.*; public class Main { public static void main(String[] args) { + OpenCV.loadLocally(); Injector injector = Guice.createInjector(new AppModule()); SwingUtilities.invokeLater(() -> { diff --git a/src/main/java/app/AppModule.java b/src/main/java/app/AppModule.java index 8774c4a..4c1ba76 100644 --- a/src/main/java/app/AppModule.java +++ b/src/main/java/app/AppModule.java @@ -6,13 +6,16 @@ import com.google.common.eventbus.EventBus; import com.google.inject.AbstractModule; import com.google.inject.Scopes; import presenters.DoubleMatrixPresenter; +import presenters.FilteringPresenter; import presenters.MainPresenter; import presenters.NavPresenter; import services.ImageService; import ui.implementation.components.nav.NavBar; import ui.implementation.views.DoubleMatrix; +import ui.implementation.views.FilteringView; import ui.implementation.views.MainView; import ui.interfaces.IDoubleMatrix; +import ui.interfaces.IFilteringView; import ui.interfaces.IMainView; import ui.interfaces.INavBar; @@ -37,11 +40,13 @@ public class AppModule extends AbstractModule { bind(INavBar.class).to(NavBar.class); bind(IMainView.class).to(MainView.class); bind(IDoubleMatrix.class).to(DoubleMatrix.class); + bind(IFilteringView.class).to(FilteringView.class); //--- Presentation layer --- bind(MainPresenter.class); bind(NavPresenter.class); bind(DoubleMatrixPresenter.class); + bind(FilteringPresenter.class); //--- Service layer layer --- bind(ImageService.class); diff --git a/src/main/java/app/INavigator.java b/src/main/java/app/INavigator.java index dd3d26e..fbc94b4 100644 --- a/src/main/java/app/INavigator.java +++ b/src/main/java/app/INavigator.java @@ -10,5 +10,6 @@ public interface INavigator { * Displays Fourier view */ void showFourier(); + void showFiltering(); } diff --git a/src/main/java/app/SwingNavigator.java b/src/main/java/app/SwingNavigator.java index ab48e46..2f81574 100644 --- a/src/main/java/app/SwingNavigator.java +++ b/src/main/java/app/SwingNavigator.java @@ -3,6 +3,7 @@ package app; import com.google.inject.Inject; import com.google.inject.Provider; import ui.implementation.views.DoubleMatrix; +import ui.implementation.views.FilteringView; /** * Concrete implement of the INavigator interface. @@ -12,12 +13,15 @@ import ui.implementation.views.DoubleMatrix; public class SwingNavigator implements INavigator { private final Provider doubleMatrixProvider; + private final Provider filteringProvider; @Inject public SwingNavigator( - Provider doubleMatrixProvider + Provider doubleMatrixProvider, + Provider filteringProvider ) { this.doubleMatrixProvider = doubleMatrixProvider; + this.filteringProvider = filteringProvider; } @Override @@ -25,4 +29,10 @@ public class SwingNavigator implements INavigator { DoubleMatrix doubleMatrix = doubleMatrixProvider.get(); doubleMatrix.setVisible(true); } + + @Override + public void showFiltering() { + FilteringView filteringView = filteringProvider.get(); + filteringView.setVisible(true); + } } diff --git a/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java b/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java new file mode 100644 index 0000000..a7f5d92 --- /dev/null +++ b/src/main/java/domain/image/processing/filtering/GlobalLinearFiltering.java @@ -0,0 +1,191 @@ +package domain.image.processing.filtering; + +import domain.image.GrayScaleMatrix; +import domain.image.Image; +import domain.image.Pixel; +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; + +import java.util.ArrayList; +import java.util.List; + +public class GlobalLinearFiltering { + + public static GrayScaleMatrix lowPassFiltering(GrayScaleMatrix matrix, int cutoff) { + + int width = matrix.getWidth(); + int height = matrix.getHeight(); + + double minCutoff = 0; + double maxCutoff = Math.sqrt(Math.pow(width / 2.0, 2) + Math.pow(height / 2.0, 2)); + + if(cutoff < minCutoff || cutoff > maxCutoff){ + throw new IllegalArgumentException( + String.format("Cutoff value is out of bond (%f - %f)", minCutoff, maxCutoff) + ); + } + + Mat mat = new Mat(height, width, CvType.CV_64F); + + double[][] data = matrix.getRawData(); + + for (int y = 0; y < height; y++) { + mat.put(y, 0, data[y]); + } + + // Fourier transform + Mat spectrum = new Mat(); + Core.dft(mat, spectrum, Core.DFT_COMPLEX_OUTPUT); + + // Shift zero frequency to the center + 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); + + 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)); + + if (distance > cutoff) { + spectrum.put(y, x, 0, 0); + } + } + } + + // Shift back + q0.copyTo(tmp); + q3.copyTo(q0); + tmp.copyTo(q3); + + q1.copyTo(tmp); + q2.copyTo(q1); + tmp.copyTo(q2); + + // Inverse Fourier transform + Mat filtered = new Mat(); + + Core.idft( + spectrum, + filtered, + Core.DFT_REAL_OUTPUT | Core.DFT_SCALE + ); + + // Convert result back to your Image + 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); + } + } + + return new GrayScaleMatrix(pixels); + } + + public static GrayScaleMatrix highPassFiltering(GrayScaleMatrix matrix, int cutoff){ + int width = matrix.getWidth(); + int height = matrix.getHeight(); + + double minCutoff = 0; + double maxCutoff = Math.sqrt(Math.pow(width / 2.0, 2) + Math.pow(height / 2.0, 2)); + + if(cutoff < minCutoff || cutoff > maxCutoff){ + throw new IllegalArgumentException( + String.format("Cutoff value is out of bond (%f - %f)", minCutoff, maxCutoff) + ); + } + + Mat mat = new Mat(height, width, CvType.CV_64F); + + double[][] data = matrix.getRawData(); + + for (int y = 0; y < height; y++) { + mat.put(y, 0, data[y]); + } + + // Fourier transform + Mat spectrum = new Mat(); + Core.dft(mat, spectrum, Core.DFT_COMPLEX_OUTPUT); + + // Shift zero frequency to the center + 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); + + 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)); + + if (distance <= cutoff) { + spectrum.put(y, x, 0, 0); + } + } + } + + // Shift back + q0.copyTo(tmp); + q3.copyTo(q0); + tmp.copyTo(q3); + + q1.copyTo(tmp); + q2.copyTo(q1); + tmp.copyTo(q2); + + // Inverse Fourier transform + Mat filtered = new Mat(); + + Core.idft( + spectrum, + filtered, + Core.DFT_REAL_OUTPUT | Core.DFT_SCALE + ); + + // Convert result back to your Image + 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); + } + } + + return new GrayScaleMatrix(pixels); + } + + public static void butterworthLowPassFiltering(){ + + } + + public static void butterworthHighPassFiltering(){ + + } +} diff --git a/src/main/java/domain/image/processing/lineaire/FiltrageLinaireGlobal.java b/src/main/java/domain/image/processing/lineaire/FiltrageLinaireGlobal.java deleted file mode 100644 index 14c95ea..0000000 --- a/src/main/java/domain/image/processing/lineaire/FiltrageLinaireGlobal.java +++ /dev/null @@ -1,20 +0,0 @@ -package domain.image.processing.lineaire; - -import jdk.jshell.spi.ExecutionControl; - -public class FiltrageLinaireGlobal { - - public static int[][] filtrePasseBasIdeal(int[][] image,int frequenceCoupure){ - return null; - } - public static int[][] filtrePasseHautIdeal(int[][] image,int frequenceCoupure){ - return null; - } - public static int[][] filtrePasseBasButterworth(int[][] image,int frequenceCoupure,int ordre){ - return null; - } - public static int[][] filtrePasseHautButterworth(int[][] image,int frequenceCoupure, int ordre) { - return null; - } - -} diff --git a/src/main/java/presenters/FilteringPresenter.java b/src/main/java/presenters/FilteringPresenter.java new file mode 100644 index 0000000..2b656ce --- /dev/null +++ b/src/main/java/presenters/FilteringPresenter.java @@ -0,0 +1,40 @@ +package presenters; + +import com.google.inject.Inject; +import domain.image.GrayScaleMatrix; +import domain.image.Image; +import services.FilteringService; +import services.ImageService; +import ui.implementation.views.FilteringView; +import ui.interfaces.IFilteringView; + +public class FilteringPresenter { + + private final ImageService imageService; + private final FilteringService filteringService; + private IFilteringView view; + + @Inject + public FilteringPresenter(ImageService imageService, FilteringService filteringService) { + this.imageService = imageService; + this.filteringService = filteringService; + } + + public void setView(IFilteringView view){ + this.view = view; + } + + public Image computeLowPassFiltering(){ + GrayScaleMatrix matrix = imageService.getGrayScale(); + return filteringService.lowPassFiltering(matrix); + } + + public Image computeHighPassFiltering(){ + GrayScaleMatrix matrix = imageService.getGrayScale(); + return filteringService.highPassFiltering(matrix); + } + + public Image getImage(){ + return imageService.getImage(); + } +} diff --git a/src/main/java/presenters/NavPresenter.java b/src/main/java/presenters/NavPresenter.java index 9d1162b..01e1b8b 100644 --- a/src/main/java/presenters/NavPresenter.java +++ b/src/main/java/presenters/NavPresenter.java @@ -44,6 +44,10 @@ public class NavPresenter { navigator.showFourier(); } + public void goToFiltering(){ + navigator.showFiltering(); + } + public GrayScaleMatrix getImageGrayMatrix() { //return ((CImageNG)appState.getImage()).getMatrice(); return imageService.getImage().toGrayScale(); diff --git a/src/main/java/services/FilteringService.java b/src/main/java/services/FilteringService.java new file mode 100644 index 0000000..f966c26 --- /dev/null +++ b/src/main/java/services/FilteringService.java @@ -0,0 +1,19 @@ +package services; + +import domain.image.GrayScaleMatrix; +import domain.image.Image; +import domain.image.processing.filtering.GlobalLinearFiltering; + +public class FilteringService { + + public Image lowPassFiltering(GrayScaleMatrix matrix) { + GrayScaleMatrix filteredMatrix = GlobalLinearFiltering.lowPassFiltering(matrix, 10); + return filteredMatrix.toImage(); + } + + public Image highPassFiltering(GrayScaleMatrix matrix) { + GrayScaleMatrix filteredMatrix = GlobalLinearFiltering.highPassFiltering(matrix, 10); + return filteredMatrix.toImage(); + } + +} diff --git a/src/main/java/ui/implementation/components/nav/NavBar.java b/src/main/java/ui/implementation/components/nav/NavBar.java index 6b14a40..a0ea0c4 100644 --- a/src/main/java/ui/implementation/components/nav/NavBar.java +++ b/src/main/java/ui/implementation/components/nav/NavBar.java @@ -4,6 +4,7 @@ import domain.image.GrayScaleMatrix; import domain.image.processing.histogram.Histogramme; import domain.common.Mode; import jakarta.inject.Inject; +import jdk.jshell.spi.ExecutionControl; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartFrame; import org.jfree.chart.JFreeChart; @@ -67,8 +68,12 @@ public class NavBar extends JMenuBar implements INavBar { ) ), new Menu("Convertir", - new MenuItem("RGB", e -> {}), - new MenuItem("NG", e -> {}) + new MenuItem("RGB", e -> { + displayErrorMessage("Oups !", "Not implemented."); + }), + new MenuItem("NG", e -> { + displayErrorMessage("Oups !", "Not implemented."); + }) ) ); @@ -79,10 +84,10 @@ public class NavBar extends JMenuBar implements INavBar { linearMenu = new Menu("Linéaire","/cp_51_p1.jpg", new Menu("Global", new Menu("Formes", - new MenuItem("PasseBasIdeal", e -> this.setMode(e, Mode.DRAW_PIXEL)), - new MenuItem("PasseHautIdeal", e -> this.setMode(e, Mode.DRAW_LINE)), - new MenuItem("PasseBasButterworth",e -> this.setMode(e, Mode.DRAW_RECTANGLE)), - new MenuItem("PasseHautButterworth", e -> this.setMode(e, Mode.DRAW_CIRCLE)) + new MenuItem("PasseBasIdeal", this::displayFilter), + new MenuItem("PasseHautIdeal", this::displayFilter), + new MenuItem("PasseBasButterworth", this::displayFilter), + new MenuItem("PasseHautButterworth", this::displayFilter) ) ) ); @@ -206,96 +211,28 @@ public class NavBar extends JMenuBar implements INavBar { frame.setVisible(true); } - private void jMenuItemFourierAfficherPartieImaginaireActionPerformed(ActionEvent e) { -// try -// { -// int f_int[][] = imageNG.getMatrice(); -// double f[][] = new double[imageNG.getLargeur()][imageNG.getHauteur()]; -// for(int i=0 ; i