From 2d417990ec2b6afe67353f278e29357815d23928 Mon Sep 17 00:00:00 2001 From: Laurent Date: Tue, 29 Sep 2026 12:06:44 +0200 Subject: [PATCH] Add non-linear outline --- src/main/java/app/AppModule.java | 6 + src/main/java/app/SwingNavigator.java | 10 +- .../java/app/state/NonLinearOutlineState.java | 74 +++++++++ .../processing/outline/NonLinearOutline.java | 74 +++++++++ .../outline/NonLinearOutlineListener.java | 14 ++ .../outline/NonLinearOutlinePresenter.java | 99 +++++++++++ src/main/java/services/OutlineService.java | 28 ++++ .../abstractions/INonLinearOutlineView.java | 14 ++ .../implementation/components/nav/NavBar.java | 5 +- .../views/outline/NonLinearOutlineView.java | 154 ++++++++++++++++++ 10 files changed, 473 insertions(+), 5 deletions(-) create mode 100644 src/main/java/app/state/NonLinearOutlineState.java create mode 100644 src/main/java/domain/image/processing/outline/NonLinearOutline.java create mode 100644 src/main/java/presenters/outline/NonLinearOutlineListener.java create mode 100644 src/main/java/presenters/outline/NonLinearOutlinePresenter.java create mode 100644 src/main/java/ui/abstractions/INonLinearOutlineView.java create mode 100644 src/main/java/ui/implementation/views/outline/NonLinearOutlineView.java diff --git a/src/main/java/app/AppModule.java b/src/main/java/app/AppModule.java index 19c9848..79d2ed7 100644 --- a/src/main/java/app/AppModule.java +++ b/src/main/java/app/AppModule.java @@ -12,6 +12,8 @@ import presenters.filtering.local.LocalFilteringPresenter; import presenters.morphology.BasicMorphologyPresenter; import presenters.morphology.ComplexMorphologyPresenter; import presenters.outline.LinearOutlinePresenter; +import presenters.outline.NonLinearOutlineListener; +import presenters.outline.NonLinearOutlinePresenter; import services.GlobalFilteringService; import services.ImageService; import services.MorphologyService; @@ -25,6 +27,7 @@ import ui.implementation.views.fitering.local.LocalFilteringView; import ui.implementation.views.morphology.BasicMorphologyView; import ui.implementation.views.morphology.ComplexMorphologyView; import ui.implementation.views.outline.LinearOutlineView; +import ui.implementation.views.outline.NonLinearOutlineView; /** * Gathers some configurations in order to set the dependency injection container up @@ -43,6 +46,7 @@ public class AppModule extends AbstractModule { bind(BasicMorphologyState.class); bind(ComplexMorphologyState.class); bind(LinearOutlineState.class); + bind(NonLinearOutlineState.class); //--- Application layer --- bind(EventBus.class).in(Scopes.SINGLETON); @@ -57,6 +61,7 @@ public class AppModule extends AbstractModule { bind(IBasicMorphologyView.class).to(BasicMorphologyView.class); bind(IComplexMorphologyView.class).to(ComplexMorphologyView.class); bind(ILinearOutlineView.class).to(LinearOutlineView.class); + bind(INonLinearOutlineView.class).to(NonLinearOutlineView.class); //--- Presentation layer --- bind(MainPresenter.class); @@ -67,6 +72,7 @@ public class AppModule extends AbstractModule { bind(BasicMorphologyPresenter.class); bind(ComplexMorphologyPresenter.class); bind(LinearOutlinePresenter.class); + bind(NonLinearOutlinePresenter.class); //--- Service layer layer --- bind(ImageService.class); diff --git a/src/main/java/app/SwingNavigator.java b/src/main/java/app/SwingNavigator.java index af9c6eb..5a1bb26 100644 --- a/src/main/java/app/SwingNavigator.java +++ b/src/main/java/app/SwingNavigator.java @@ -12,6 +12,7 @@ import ui.implementation.views.fitering.local.LocalFilteringView; import ui.implementation.views.morphology.BasicMorphologyView; import ui.implementation.views.morphology.ComplexMorphologyView; import ui.implementation.views.outline.LinearOutlineView; +import ui.implementation.views.outline.NonLinearOutlineView; /** * Concrete implement of the INavigator interface. @@ -26,7 +27,8 @@ public class SwingNavigator implements INavigator { private final Provider convolutionFilteringProvider; private final Provider basicMorphologyProvider; private final Provider complexMorphologyProvider; - private Provider linearOutlineProvider; + private final Provider linearOutlineProvider; + private final Provider nonLinearOutlineProvider; @Inject public SwingNavigator( @@ -36,7 +38,8 @@ public class SwingNavigator implements INavigator { Provider convolutionFilterProvider, Provider basicMorphologyProvider, Provider complexMorphologyProvider, - Provider linearOutlineProvider + Provider linearOutlineProvider, + Provider nonLinearOutlineProvider ) { this.doubleMatrixProvider = doubleMatrixProvider; this.filteringProvider = filteringProvider; @@ -45,6 +48,7 @@ public class SwingNavigator implements INavigator { this.basicMorphologyProvider = basicMorphologyProvider; this.complexMorphologyProvider = complexMorphologyProvider; this.linearOutlineProvider = linearOutlineProvider; + this.nonLinearOutlineProvider = nonLinearOutlineProvider; } @Override @@ -91,6 +95,8 @@ public class SwingNavigator implements INavigator { @Override public void showNonLinearOutline() { + NonLinearOutlineView view = nonLinearOutlineProvider.get(); + runView(view); } private void runView(AbstractView view) { diff --git a/src/main/java/app/state/NonLinearOutlineState.java b/src/main/java/app/state/NonLinearOutlineState.java new file mode 100644 index 0000000..c031afd --- /dev/null +++ b/src/main/java/app/state/NonLinearOutlineState.java @@ -0,0 +1,74 @@ +package app.state; + +import com.google.inject.Inject; +import domain.image.Image; +import presenters.outline.NonLinearOutlineListener; + +import java.util.ArrayList; +import java.util.List; + +public class NonLinearOutlineState extends State { + + private Image erosionGradient; + private Image dilationGradient; + private Image beucherGradient; + private Image nonLinearLaplacian; + + private final List listeners = new ArrayList<>(); + + @Inject + public NonLinearOutlineState() { + } + + public void subscribe(NonLinearOutlineListener listener) { + listeners.add(listener); + } + + public void unsubscribe(NonLinearOutlineListener listener) { + listeners.remove(listener); + } + + public void setErosionGradient(Image image) { + erosionGradient = image; + listeners.forEach(listener -> + listener.onErosionGradientChanged(image) + ); + } + + public void setDilationGradient(Image image) { + dilationGradient = image; + listeners.forEach(listener -> + listener.onDilationGradientChanged(image) + ); + } + + public void setBeucherGradient(Image image) { + beucherGradient = image; + listeners.forEach(listener -> + listener.onBeucherGradientChanged(image) + ); + } + + public void setNonLinearLaplacian(Image image) { + nonLinearLaplacian = image; + listeners.forEach(listener -> + listener.onNonLinearLaplacianChanged(image) + ); + } + + public Image getErosionGradient() { + return erosionGradient; + } + + public Image getDilationGradient() { + return dilationGradient; + } + + public Image getBeucherGradient() { + return beucherGradient; + } + + public Image getNonLinearLaplacian() { + return nonLinearLaplacian; + } +} \ 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 new file mode 100644 index 0000000..c2d4dd4 --- /dev/null +++ b/src/main/java/domain/image/processing/outline/NonLinearOutline.java @@ -0,0 +1,74 @@ +package domain.image.processing.outline; + +import domain.image.GrayScaleMatrix; +import domain.image.Image; +import domain.image.processing.morphology.BasicMorphology; + +public class NonLinearOutline { + + public static GrayScaleMatrix gradientErosion(Image image) { + GrayScaleMatrix original = image.toGrayScale(); + GrayScaleMatrix erosion = BasicMorphology.erosion(image, 3); + + return subtract(original, erosion); + } + + public static GrayScaleMatrix gradientDilation(Image image) { + GrayScaleMatrix original = image.toGrayScale(); + GrayScaleMatrix dilation = BasicMorphology.dilation(image, 3); + + return subtract(dilation, original); + } + + public static GrayScaleMatrix beucherGradient(Image image) { + GrayScaleMatrix erosion = BasicMorphology.erosion(image, 3); + GrayScaleMatrix dilation = BasicMorphology.dilation(image, 3); + + 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); + + 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(); + + 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]; + } + } + + return new GrayScaleMatrix(result); + } + + 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]; + } + } + + return new GrayScaleMatrix(result); + } +} \ No newline at end of file diff --git a/src/main/java/presenters/outline/NonLinearOutlineListener.java b/src/main/java/presenters/outline/NonLinearOutlineListener.java new file mode 100644 index 0000000..edaeaaf --- /dev/null +++ b/src/main/java/presenters/outline/NonLinearOutlineListener.java @@ -0,0 +1,14 @@ +package presenters.outline; + +import domain.image.Image; + +public interface NonLinearOutlineListener { + + void onErosionGradientChanged(Image image); + + void onDilationGradientChanged(Image image); + + void onBeucherGradientChanged(Image image); + + void onNonLinearLaplacianChanged(Image image); +} \ No newline at end of file diff --git a/src/main/java/presenters/outline/NonLinearOutlinePresenter.java b/src/main/java/presenters/outline/NonLinearOutlinePresenter.java new file mode 100644 index 0000000..9a99ea7 --- /dev/null +++ b/src/main/java/presenters/outline/NonLinearOutlinePresenter.java @@ -0,0 +1,99 @@ +package presenters.outline; + +import app.state.AppState; +import app.state.NonLinearOutlineState; +import domain.image.Image; +import jakarta.inject.Inject; +import services.OutlineService; +import ui.abstractions.INonLinearOutlineView; + +public class NonLinearOutlinePresenter implements NonLinearOutlineListener { + + private final AppState appState; + private final NonLinearOutlineState outlineState; + private final OutlineService outlineService; + + private INonLinearOutlineView view; + + @Inject + public NonLinearOutlinePresenter( + AppState appState, + NonLinearOutlineState outlineState, + OutlineService outlineService + ) { + this.appState = appState; + this.outlineState = outlineState; + this.outlineService = outlineService; + + outlineState.subscribe(this); + } + + public void setView(INonLinearOutlineView view) { + this.view = view; + } + + public void dispose() { + view = null; + outlineState.unsubscribe(this); + } + + public void computeErosionGradient() { + Image image = appState.getImage(); + + Image result = outlineService.gradientErosion(image); + + outlineState.setErosionGradient(result); + } + + public void computeDilationGradient() { + Image image = appState.getImage(); + + Image result = outlineService.gradientDilation(image); + + outlineState.setDilationGradient(result); + } + + public void computeBeucherGradient() { + Image image = appState.getImage(); + + Image result = outlineService.beucherGradient(image); + + outlineState.setBeucherGradient(result); + } + + public void computeNonLinearLaplacian() { + Image image = appState.getImage(); + + Image result = outlineService.nonLinearLaplacian(image); + + outlineState.setNonLinearLaplacian(result); + } + + @Override + public void onErosionGradientChanged(Image image) { + if (view != null) { + view.updateErosionGradient(image); + } + } + + @Override + public void onDilationGradientChanged(Image image) { + if (view != null) { + view.updateDilationGradient(image); + } + } + + @Override + public void onBeucherGradientChanged(Image image) { + if (view != null) { + view.updateBeucherGradient(image); + } + } + + @Override + public void onNonLinearLaplacianChanged(Image image) { + if (view != null) { + view.updateNonLinearLaplacian(image); + } + } +} \ No newline at end of file diff --git a/src/main/java/services/OutlineService.java b/src/main/java/services/OutlineService.java index 0e87277..6d3be81 100644 --- a/src/main/java/services/OutlineService.java +++ b/src/main/java/services/OutlineService.java @@ -2,6 +2,7 @@ package services; import domain.image.Image; import domain.image.processing.outline.LinearOutline; +import domain.image.processing.outline.NonLinearOutline; public class OutlineService { @@ -29,4 +30,31 @@ public class OutlineService { .toImage(); } + public Image gradientErosion(Image image) { + return NonLinearOutline + .gradientErosion(image) + .toImage(); + } + + public Image gradientDilation(Image image) { + return NonLinearOutline + .gradientDilation(image) + .toImage(); + } + + public Image beucherGradient(Image image) { + return NonLinearOutline + .beucherGradient(image) + .toImage(); + } + + public Image nonLinearLaplacian(Image image) { + return NonLinearOutline + .nonLinearLaplacian(image) + .toImage(); + } + + + + } diff --git a/src/main/java/ui/abstractions/INonLinearOutlineView.java b/src/main/java/ui/abstractions/INonLinearOutlineView.java new file mode 100644 index 0000000..ec73068 --- /dev/null +++ b/src/main/java/ui/abstractions/INonLinearOutlineView.java @@ -0,0 +1,14 @@ +package ui.abstractions; + +import domain.image.Image; + +public interface INonLinearOutlineView { + + void updateErosionGradient(Image image); + + void updateDilationGradient(Image image); + + void updateBeucherGradient(Image image); + + void updateNonLinearLaplacian(Image image); +} \ No newline at end of file diff --git a/src/main/java/ui/implementation/components/nav/NavBar.java b/src/main/java/ui/implementation/components/nav/NavBar.java index 95c1fff..50027ed 100644 --- a/src/main/java/ui/implementation/components/nav/NavBar.java +++ b/src/main/java/ui/implementation/components/nav/NavBar.java @@ -99,11 +99,10 @@ public class NavBar extends JMenuBar implements INavBar { ); outlineMenu = new Menu("Contours","/cp_51_p1.jpg", - new MenuItem("Linéair", this::displayLinearOutline), - new MenuItem("Non-linéair", this::displayNonLinearOutline) + new MenuItem("Linéaire", this::displayLinearOutline), + new MenuItem("Non-linéaire", this::displayNonLinearOutline) ); - histogramMenu = new Menu("Histogramme","/report_48_hot.jpg", new MenuItem("Afficher", "/report_32_hot.jpg",this::displayHistogram) ); diff --git a/src/main/java/ui/implementation/views/outline/NonLinearOutlineView.java b/src/main/java/ui/implementation/views/outline/NonLinearOutlineView.java new file mode 100644 index 0000000..a90259f --- /dev/null +++ b/src/main/java/ui/implementation/views/outline/NonLinearOutlineView.java @@ -0,0 +1,154 @@ +package ui.implementation.views.outline; + +import domain.image.Image; +import jakarta.inject.Inject; +import presenters.outline.NonLinearOutlinePresenter; +import ui.abstractions.AbstractView; +import ui.abstractions.INonLinearOutlineView; +import ui.implementation.components.controls.SaveButton; +import ui.implementation.components.image.TabbedImagePanel; + +import javax.swing.*; + +public class NonLinearOutlineView extends AbstractView implements INonLinearOutlineView { + + private static final String EROSION_GRADIENT_TAB_TITLE = "Gradient d'érosion"; + private static final String DILATION_GRADIENT_TAB_TITLE = "Gradient de dilatation"; + private static final String BEUCHER_GRADIENT_TAB_TITLE = "Gradient de Beucher"; + private static final String NON_LINEAR_LAPLACIAN_TAB_TITLE = "Laplacien non-linéaire"; + + private final NonLinearOutlinePresenter presenter; + + private TabbedImagePanel modeTab; + + @Inject + public NonLinearOutlineView(NonLinearOutlinePresenter presenter) { + this.presenter = presenter; + } + + @Override + public void initComponents() { + modeTab = new TabbedImagePanel(); + + modeTab.addPreviewTab(EROSION_GRADIENT_TAB_TITLE); + modeTab.addPreviewTab(DILATION_GRADIENT_TAB_TITLE); + modeTab.addPreviewTab(BEUCHER_GRADIENT_TAB_TITLE); + modeTab.addPreviewTab(NON_LINEAR_LAPLACIAN_TAB_TITLE); + + JButton applyButton = new JButton("Appliquer"); + applyButton.addActionListener(e -> applyOutline()); + + JButton saveButton = new SaveButton(); + + JPanel controlPanel = createControlPanel(applyButton); + + JPanel rightPanel = new JPanel(); + + GroupLayout rightLayout = new GroupLayout(rightPanel); + rightPanel.setLayout(rightLayout); + rightLayout.setAutoCreateGaps(true); + rightLayout.setAutoCreateContainerGaps(true); + + rightLayout.setHorizontalGroup( + rightLayout.createParallelGroup(GroupLayout.Alignment.CENTER) + .addComponent(controlPanel) + .addComponent(saveButton) + ); + + rightLayout.setVerticalGroup( + rightLayout.createSequentialGroup() + .addComponent(controlPanel) + .addComponent(saveButton) + ); + + JPanel mainPanel = new JPanel(); + + GroupLayout mainLayout = new GroupLayout(mainPanel); + mainPanel.setLayout(mainLayout); + mainLayout.setAutoCreateGaps(true); + mainLayout.setAutoCreateContainerGaps(true); + + mainLayout.setHorizontalGroup( + mainLayout.createSequentialGroup() + .addComponent(modeTab) + .addComponent(rightPanel) + ); + + mainLayout.setVerticalGroup( + mainLayout.createParallelGroup(GroupLayout.Alignment.CENTER) + .addComponent(modeTab) + .addComponent(rightPanel) + ); + + setContentPane(mainPanel); + + setTitle("Contours non-linéaires"); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + pack(); + } + + private JPanel createControlPanel(JButton applyButton) { + JPanel panel = new JPanel(); + + GroupLayout layout = new GroupLayout(panel); + panel.setLayout(layout); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + layout.setHorizontalGroup( + layout.createParallelGroup(GroupLayout.Alignment.CENTER) + .addComponent(applyButton) + ); + + layout.setVerticalGroup( + layout.createSequentialGroup() + .addComponent(applyButton) + ); + + return panel; + } + + @Override + public void initValues() { + presenter.setView(this); + + applyOutline(); + } + + private void applyOutline() { + int operation = modeTab.getSelectedIndex(); + + switch (operation) { + case 0 -> presenter.computeErosionGradient(); + case 1 -> presenter.computeDilationGradient(); + case 2 -> presenter.computeBeucherGradient(); + case 3 -> presenter.computeNonLinearLaplacian(); + } + } + + @Override + public void updateErosionGradient(Image image) { + modeTab.loadImage(EROSION_GRADIENT_TAB_TITLE, image); + } + + @Override + public void updateDilationGradient(Image image) { + modeTab.loadImage(DILATION_GRADIENT_TAB_TITLE, image); + } + + @Override + public void updateBeucherGradient(Image image) { + modeTab.loadImage(BEUCHER_GRADIENT_TAB_TITLE, image); + } + + @Override + public void updateNonLinearLaplacian(Image image) { + modeTab.loadImage(NON_LINEAR_LAPLACIAN_TAB_TITLE, image); + } + + @Override + public void disposeCore() { + presenter.setView(null); + presenter.dispose(); + } +} \ No newline at end of file