diff --git a/src/main/java/app/AppModule.java b/src/main/java/app/AppModule.java index 3272329..050fbf2 100644 --- a/src/main/java/app/AppModule.java +++ b/src/main/java/app/AppModule.java @@ -1,7 +1,6 @@ package app; -import app.state.AppState; -import app.state.DoubleMatrixState; +import app.state.*; import com.google.common.eventbus.EventBus; import com.google.inject.AbstractModule; import com.google.inject.Scopes; @@ -10,13 +9,18 @@ import presenters.filtering.global.GlobalFilteringPresenter; import presenters.MainPresenter; import presenters.NavPresenter; import presenters.filtering.local.LocalFilteringPresenter; +import presenters.morphology.BasicMorphologyPresenter; +import services.GlobalFilteringService; import services.ImageService; +import services.MorphologyService; import ui.abstractions.*; import ui.implementation.components.nav.NavBar; import ui.implementation.views.DoubleMatrix; import ui.implementation.views.fitering.global.IdealFilteringView; import ui.implementation.views.MainView; import ui.implementation.views.fitering.local.LocalFilteringView; +import ui.implementation.views.morphology.BasicMorphologyView; +import ui.implementation.views.morphology.ComplexMorphologyView; /** * Gathers some configurations in order to set the dependency injection container up @@ -30,6 +34,10 @@ public class AppModule extends AbstractModule { //--- States --- bind(AppState.class).in(Scopes.SINGLETON); bind(DoubleMatrixState.class); + bind(LocalFilteringState.class); + bind(GlobalFilteringState.class); + bind(BasicMorphologyState.class); + bind(ComplexMorphologyState.class); //--- Application layer --- bind(EventBus.class).in(Scopes.SINGLETON); @@ -41,6 +49,8 @@ public class AppModule extends AbstractModule { bind(IDoubleMatrix.class).to(DoubleMatrix.class); bind(IGlobalFilteringView.class).to(IdealFilteringView.class); bind(ILocalFilteringView.class).to(LocalFilteringView.class); + bind(IBasicMorphologyView.class).to(BasicMorphologyView.class); + bind(IComplexMorphologyView.class).to(ComplexMorphologyView.class); //--- Presentation layer --- bind(MainPresenter.class); @@ -48,9 +58,14 @@ public class AppModule extends AbstractModule { bind(DoubleMatrixPresenter.class); bind(GlobalFilteringPresenter.class); bind(LocalFilteringPresenter.class); + bind(BasicMorphologyPresenter.class); + bind(ComplexMorphologyView.class); //--- Service layer layer --- bind(ImageService.class); + bind(LocalFilteringView.class); + bind(GlobalFilteringService.class); + bind(MorphologyService.class); } } \ No newline at end of file diff --git a/src/main/java/app/INavigator.java b/src/main/java/app/INavigator.java index a95c857..8f6b447 100644 --- a/src/main/java/app/INavigator.java +++ b/src/main/java/app/INavigator.java @@ -14,4 +14,5 @@ public interface INavigator { void showButterworth(); void showConvolutionFiltering(); void showBasicMorphology(); + void showComplexMorphology(); } diff --git a/src/main/java/app/SwingNavigator.java b/src/main/java/app/SwingNavigator.java index 47f7065..fc51123 100644 --- a/src/main/java/app/SwingNavigator.java +++ b/src/main/java/app/SwingNavigator.java @@ -2,6 +2,7 @@ package app; import com.google.inject.Inject; import com.google.inject.Provider; +import domain.image.processing.complex.ComplexMatrix; import ui.abstractions.AbstractView; import ui.implementation.views.DoubleMatrix; import ui.implementation.views.fitering.global.IdealFilteringView; @@ -9,6 +10,7 @@ import ui.implementation.views.fitering.global.NonIdealFilteringView; import ui.implementation.views.ViewInitializer; import ui.implementation.views.fitering.local.LocalFilteringView; import ui.implementation.views.morphology.BasicMorphologyView; +import ui.implementation.views.morphology.ComplexMorphologyView; /** * Concrete implement of the INavigator interface. @@ -22,6 +24,7 @@ public class SwingNavigator implements INavigator { private final Provider nonIdealFilteringProvider; private final Provider convolutionFilteringProvider; private final Provider basicMorphologyProvider; + private final Provider complexMorphologyProvider; @Inject public SwingNavigator( @@ -29,13 +32,15 @@ public class SwingNavigator implements INavigator { Provider filteringProvider, Provider nonIdealFilteringProvider, Provider convolutionFilterProvider, - Provider basicMorphologyProvider + Provider basicMorphologyProvider, + Provider complexMorphologyProvider ) { this.doubleMatrixProvider = doubleMatrixProvider; this.filteringProvider = filteringProvider; this.nonIdealFilteringProvider = nonIdealFilteringProvider; this.convolutionFilteringProvider = convolutionFilterProvider; this.basicMorphologyProvider = basicMorphologyProvider; + this.complexMorphologyProvider = complexMorphologyProvider; } @Override @@ -68,6 +73,12 @@ public class SwingNavigator implements INavigator { runView(view); } + @Override + public void showComplexMorphology() { + ComplexMorphologyView view = complexMorphologyProvider.get(); + runView(view); + } + private void runView(AbstractView view) { ViewInitializer.Initialize(view); view.setVisible(true); diff --git a/src/main/java/app/state/MorphologyState.java b/src/main/java/app/state/BasicMorphologyState.java similarity index 77% rename from src/main/java/app/state/MorphologyState.java rename to src/main/java/app/state/BasicMorphologyState.java index 1aecd50..8785e9a 100644 --- a/src/main/java/app/state/MorphologyState.java +++ b/src/main/java/app/state/BasicMorphologyState.java @@ -2,28 +2,28 @@ package app.state; import com.google.inject.Inject; import domain.image.Image; -import presenters.morphology.MorphologyListener; +import presenters.morphology.BasicMorphologyListener; import java.util.ArrayList; import java.util.List; -public class MorphologyState extends State { +public class BasicMorphologyState extends State { private Image erosion; private Image dilation; private Image opening; private Image closing; - private final List listeners = new ArrayList<>(); + private final List listeners = new ArrayList<>(); @Inject - public MorphologyState() {} + public BasicMorphologyState() {} - public void subscribe(MorphologyListener listener) { + public void subscribe(BasicMorphologyListener listener) { listeners.add(listener); } - public void unsubscribe(MorphologyListener listener) { + public void unsubscribe(BasicMorphologyListener listener) { listeners.remove(listener); } diff --git a/src/main/java/app/state/ComplexMorphologyState.java b/src/main/java/app/state/ComplexMorphologyState.java new file mode 100644 index 0000000..93eb093 --- /dev/null +++ b/src/main/java/app/state/ComplexMorphologyState.java @@ -0,0 +1,64 @@ +package app.state; + +import com.google.inject.Inject; +import domain.image.Image; +import presenters.morphology.ComplexMorphologyListener; + +import java.util.ArrayList; +import java.util.List; + +public class ComplexMorphologyState extends State { + + private Image mask; + private Image dilation; + private Image reconstruction; + private Image mean; + + private final List listeners = new ArrayList<>(); + + @Inject + public ComplexMorphologyState() {} + + public void subscribe(ComplexMorphologyListener listener) { + listeners.add(listener); + } + + public void unsubscribe(ComplexMorphologyListener listener) { + listeners.remove(listener); + } + + public void setMask(Image mask) { + this.mask = mask; + listeners.forEach(listeners -> listeners.onMaskChanged(mask)); + } + + public void setDilation(Image image) { + dilation = image; + listeners.forEach(listener -> listener.onGeodesicDilationChanged(image)); + } + + public void setReconstruction(Image image) { + reconstruction = image; + listeners.forEach(listener -> listener.onGeodesicReconstructionChanged(image)); + } + + public void setMedian(Image image) { + mean = image; + listeners.forEach(listener -> listener.onMedianChanged(image)); + } + + public Image getMask() { return mask; } + + public Image getDilation() { + return dilation; + } + + public Image getReconstruction() { + return reconstruction; + } + + public Image getMean() { + return mean; + } + +} \ No newline at end of file diff --git a/src/main/java/domain/image/processing/morphology/ComplexMorphology.java b/src/main/java/domain/image/processing/morphology/ComplexMorphology.java index c5fcc7e..965b268 100644 --- a/src/main/java/domain/image/processing/morphology/ComplexMorphology.java +++ b/src/main/java/domain/image/processing/morphology/ComplexMorphology.java @@ -10,26 +10,30 @@ import org.opencv.imgproc.Imgproc; public class ComplexMorphology { - public static GrayScaleMatrix geodesicDilation(Image image, Image mask, int size) { - Mat input = toMat(image); + public static GrayScaleMatrix geodesicDilation(Image image, Image mask, int nbIter) { Mat maskMat = toMat(mask); - Mat kernel = createKernel(size); + Mat current = toMat(image); + Mat kernel = createKernel(3); - Mat dilated = new Mat(); - Imgproc.dilate(input, dilated, kernel); + for (int i = 0; i < nbIter; i++) { + Mat dilated = new Mat(); - Mat result = new Mat(); - Core.min(dilated, maskMat, result); + Imgproc.dilate(current, dilated, kernel); - return toGrayScaleMatrix(result); + Mat result = new Mat(); + + Core.min(dilated, maskMat, result); + + current = result; + } + + return toGrayScaleMatrix(current); } - public static GrayScaleMatrix geodesicReconstruction(Image marker, Image mask, int size) { - Mat markerMat = toMat(marker); + public static GrayScaleMatrix geodesicReconstruction(Image image, Image mask) { Mat maskMat = toMat(mask); - Mat kernel = createKernel(size); - - Mat current = markerMat.clone(); + Mat current = toMat(image); + Mat kernel = createKernel(3); while (true) { Mat dilated = new Mat(); @@ -38,7 +42,9 @@ public class ComplexMorphology { Mat next = new Mat(); Core.min(dilated, maskMat, next); - if (Core.norm(next, current, Core.NORM_INF) == 0) { + double difference = Core.norm(next, current, Core.NORM_INF); + + if (difference == 0) { return toGrayScaleMatrix(next); } @@ -46,11 +52,11 @@ public class ComplexMorphology { } } - public static GrayScaleMatrix meanFilter(Image image, int size) { + public static GrayScaleMatrix medianFilter(Image image, int tailleMasque) { Mat input = toMat(image); Mat result = new Mat(); - Imgproc.blur(input, result, new Size(size, size)); + Imgproc.medianBlur(input, result, tailleMasque); return toGrayScaleMatrix(result); } @@ -73,10 +79,7 @@ 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) { diff --git a/src/main/java/presenters/NavPresenter.java b/src/main/java/presenters/NavPresenter.java index eb02cb6..fe5bdd8 100644 --- a/src/main/java/presenters/NavPresenter.java +++ b/src/main/java/presenters/NavPresenter.java @@ -52,6 +52,8 @@ public class NavPresenter { public void goToBasicMorphology(){ navigator.showBasicMorphology(); } + public void goToComplexMorphology(){ navigator.showComplexMorphology(); } + public GrayScaleMatrix getImageGrayMatrix() { return appState.getImage().toGrayScale(); } public void setMode(Mode mode) { modeService.setMode(mode); } diff --git a/src/main/java/presenters/morphology/MorphologyListener.java b/src/main/java/presenters/morphology/BasicMorphologyListener.java similarity index 83% rename from src/main/java/presenters/morphology/MorphologyListener.java rename to src/main/java/presenters/morphology/BasicMorphologyListener.java index 9fc9db2..4538113 100644 --- a/src/main/java/presenters/morphology/MorphologyListener.java +++ b/src/main/java/presenters/morphology/BasicMorphologyListener.java @@ -2,7 +2,7 @@ package presenters.morphology; import domain.image.Image; -public interface MorphologyListener { +public interface BasicMorphologyListener { void onErosionChanged(Image image); void onDilationChanged(Image image); @@ -10,4 +10,6 @@ public interface MorphologyListener { void onOpeningChanged(Image image); void onClosingChanged(Image image); + + } diff --git a/src/main/java/presenters/morphology/MorphologyPresenter.java b/src/main/java/presenters/morphology/BasicMorphologyPresenter.java similarity index 90% rename from src/main/java/presenters/morphology/MorphologyPresenter.java rename to src/main/java/presenters/morphology/BasicMorphologyPresenter.java index c3a279c..123ca6f 100644 --- a/src/main/java/presenters/morphology/MorphologyPresenter.java +++ b/src/main/java/presenters/morphology/BasicMorphologyPresenter.java @@ -1,23 +1,23 @@ package presenters.morphology; import app.state.AppState; -import app.state.MorphologyState; +import app.state.BasicMorphologyState; import com.google.inject.Inject; import domain.image.Image; import services.MorphologyService; import ui.abstractions.IBasicMorphologyView; -public class MorphologyPresenter implements MorphologyListener { +public class BasicMorphologyPresenter implements BasicMorphologyListener { private final AppState appState; - private final MorphologyState morphologyState; + private final BasicMorphologyState morphologyState; private final MorphologyService morphologyService; private IBasicMorphologyView view; @Inject - public MorphologyPresenter( + public BasicMorphologyPresenter( AppState appState, - MorphologyState morphologyState, + BasicMorphologyState morphologyState, MorphologyService morphologyService ) { this.appState = appState; diff --git a/src/main/java/presenters/morphology/ComplexMorphologyListener.java b/src/main/java/presenters/morphology/ComplexMorphologyListener.java new file mode 100644 index 0000000..f6cd55c --- /dev/null +++ b/src/main/java/presenters/morphology/ComplexMorphologyListener.java @@ -0,0 +1,10 @@ +package presenters.morphology; + +import domain.image.Image; + +public interface ComplexMorphologyListener { + void onMaskChanged(Image mask); + void onGeodesicDilationChanged(Image image); + void onGeodesicReconstructionChanged(Image image); + void onMedianChanged(Image image); +} diff --git a/src/main/java/presenters/morphology/ComplexMorphologyPresenter.java b/src/main/java/presenters/morphology/ComplexMorphologyPresenter.java new file mode 100644 index 0000000..c1774d2 --- /dev/null +++ b/src/main/java/presenters/morphology/ComplexMorphologyPresenter.java @@ -0,0 +1,126 @@ +package presenters.morphology; + +import app.state.AppState; +import app.state.ComplexMorphologyState; +import com.google.inject.Inject; +import domain.image.Image; +import services.MorphologyService; +import ui.abstractions.IComplexMorphologyView; + +public class ComplexMorphologyPresenter implements ComplexMorphologyListener { + + private final AppState appState; + private final ComplexMorphologyState morphologyState; + private final MorphologyService morphologyService; + + private IComplexMorphologyView view; + + @Inject + public ComplexMorphologyPresenter( + AppState appState, + ComplexMorphologyState morphologyState, + MorphologyService morphologyService + ) { + this.appState = appState; + this.morphologyState = morphologyState; + this.morphologyService = morphologyService; + + morphologyState.subscribe(this); + } + + public void setView(IComplexMorphologyView view) { + this.view = view; + } + + public void setMask(Image mask) { + this.morphologyState.setMask(mask); + } + + public void computeGeodesicDilation(int iterations) { + Image image = appState.getImage(); + Image mask = morphologyState.getMask(); + + Image result = morphologyService.geodesicDilation(image, mask, iterations); + + morphologyState.setDilation(result); + } + + public void computeGeodesicReconstruction() { + Image image = appState.getImage(); + Image mask = morphologyState.getMask(); + + Image result = morphologyService.geodesicReconstruction(image, mask); + + + System.out.println("IMAGE: " + + image.getWidth() + "x" + image.getHeight()); + + System.out.println("MASK: " + + mask.getWidth() + "x" + mask.getHeight()); + + System.out.println( + "Same object? " + (image == mask) + ); + + System.out.println("Image : " + image.getPixel(0, 0).grayValue()); + + System.out.println("Mask : " + mask.getPixel(0, 0).grayValue()); + System.out.println("Result : " + result.getPixel(0, 0).grayValue()); + + double[][] imageData = image.toGrayScale().getRawData(); + double[][] maskData = mask.toGrayScale().getRawData(); + + int violations = 0; + + for (int y = 0; y < image.getHeight(); y++) { + for (int x = 0; x < image.getWidth(); x++) { + if (imageData[y][x] > maskData[y][x]) { + violations++; + } + } + } + + System.out.println("Marker > mask pixels: " + violations); + + morphologyState.setReconstruction(result); + } + + public void computeMedianFilter(int size) { + Image image = appState.getImage(); + + Image result = morphologyService.medianFilter(image, size); + + morphologyState.setMedian(result); + } + + public void dispose() { + view = null; + morphologyState.unsubscribe(this); + } + + @Override + public void onMaskChanged(Image mask) { + + } + + @Override + public void onGeodesicDilationChanged(Image image) { + if (view != null) { + view.updateDilation(image); + } + } + + @Override + public void onGeodesicReconstructionChanged(Image image) { + if (view != null) { + view.updateReconstruction(image); + } + } + + @Override + public void onMedianChanged(Image image) { + if (view != null) { + view.updateMedian(image); + } + } +} \ No newline at end of file diff --git a/src/main/java/services/MorphologyService.java b/src/main/java/services/MorphologyService.java index 0594767..aba9977 100644 --- a/src/main/java/services/MorphologyService.java +++ b/src/main/java/services/MorphologyService.java @@ -22,22 +22,21 @@ public class MorphologyService { return BasicMorphology.closing(image, size).toImage(); } - public Image geodesicDilation(Image image, Image mask, int size) { + public Image geodesicDilation(Image image, Image mask, int iterations) { return ComplexMorphology - .geodesicDilation(image, mask, size) + .geodesicDilation(image, mask, iterations) .toImage(); } - public Image geodesicReconstruction(Image marker, Image mask, int size) { + public Image geodesicReconstruction(Image image, Image mask) { return ComplexMorphology - .geodesicReconstruction(marker, mask, size) + .geodesicReconstruction(image, mask) .toImage(); } - public Image meanFilter(Image image, int size) { + public Image medianFilter(Image image, int size) { return ComplexMorphology - .meanFilter(image, size) + .medianFilter(image, size) .toImage(); } - } \ No newline at end of file diff --git a/src/main/java/ui/abstractions/IComplexMorphologyView.java b/src/main/java/ui/abstractions/IComplexMorphologyView.java new file mode 100644 index 0000000..628a91c --- /dev/null +++ b/src/main/java/ui/abstractions/IComplexMorphologyView.java @@ -0,0 +1,10 @@ +package ui.abstractions; + +import domain.image.Image; + +public interface IComplexMorphologyView { + void updateMask(Image mask); + void updateDilation(Image image); + void updateReconstruction(Image image); + void updateMedian(Image image); +} diff --git a/src/main/java/ui/implementation/components/nav/NavBar.java b/src/main/java/ui/implementation/components/nav/NavBar.java index 9816a79..9bfada7 100644 --- a/src/main/java/ui/implementation/components/nav/NavBar.java +++ b/src/main/java/ui/implementation/components/nav/NavBar.java @@ -254,6 +254,5 @@ public class NavBar extends JMenuBar implements INavBar { private void displayBasicMorphology(ActionEvent actionEvent) { presenter.goToBasicMorphology(); } - private void displayComplexeMorphology(ActionEvent actionEvent) { - } + private void displayComplexeMorphology(ActionEvent actionEvent) { presenter.goToComplexMorphology(); } } diff --git a/src/main/java/ui/implementation/views/morphology/BasicMorphologyView.java b/src/main/java/ui/implementation/views/morphology/BasicMorphologyView.java index cb96585..0842e99 100644 --- a/src/main/java/ui/implementation/views/morphology/BasicMorphologyView.java +++ b/src/main/java/ui/implementation/views/morphology/BasicMorphologyView.java @@ -2,17 +2,13 @@ package ui.implementation.views.morphology; import domain.image.Image; import jakarta.inject.Inject; -import presenters.morphology.MorphologyPresenter; +import presenters.morphology.BasicMorphologyPresenter; import ui.abstractions.AbstractView; import ui.abstractions.IBasicMorphologyView; import ui.implementation.components.controls.SaveButton; import ui.implementation.components.image.TabbedImagePanel; import javax.swing.*; -import javax.swing.text.AbstractDocument; -import javax.swing.text.AttributeSet; -import javax.swing.text.BadLocationException; -import javax.swing.text.DocumentFilter; import java.awt.*; public class BasicMorphologyView extends AbstractView implements IBasicMorphologyView { @@ -22,13 +18,13 @@ public class BasicMorphologyView extends AbstractView implements IBasicMorpholog private static final String OPENING_TAB_TITLE = "Ouverture"; private static final String CLOSING_TAB_TITLE = "Fermeture"; - private final MorphologyPresenter presenter; + private final BasicMorphologyPresenter presenter; private TabbedImagePanel modeTab; - private JTextField maskSizeField; + private JSpinner maskSizeSpinner; @Inject - public BasicMorphologyView(MorphologyPresenter presenter) { + public BasicMorphologyView(BasicMorphologyPresenter presenter) { this.presenter = presenter; } @@ -41,7 +37,9 @@ public class BasicMorphologyView extends AbstractView implements IBasicMorpholog modeTab.addPreviewTab(OPENING_TAB_TITLE); modeTab.addPreviewTab(CLOSING_TAB_TITLE); - maskSizeField = createPositiveIntegerField(); + maskSizeSpinner = new JSpinner( + new SpinnerNumberModel(1, 1, Integer.MAX_VALUE, 2) + ); JButton applyButton = new JButton("Appliquer"); applyButton.addActionListener(e -> applyMorphology()); @@ -49,7 +47,7 @@ public class BasicMorphologyView extends AbstractView implements IBasicMorpholog JButton saveButton = new SaveButton(); JPanel controlPanel = createControlPanel( - maskSizeField, + maskSizeSpinner, applyButton ); @@ -100,12 +98,17 @@ public class BasicMorphologyView extends AbstractView implements IBasicMorpholog @Override public void initValues() { - this.modeTab.addChangeListener(e -> applyMorphology()); + modeTab.addChangeListener(e -> applyMorphology()); + presenter.setView(this); + applyMorphology(); } - private JPanel createControlPanel(JTextField sizeField, JButton applyButton) { + private JPanel createControlPanel( + JSpinner sizeSpinner, + JButton applyButton + ) { JPanel panel = new JPanel(); GroupLayout layout = new GroupLayout(panel); @@ -119,9 +122,9 @@ public class BasicMorphologyView extends AbstractView implements IBasicMorpholog layout.createParallelGroup(GroupLayout.Alignment.CENTER) .addComponent(label) .addComponent( - sizeField, + sizeSpinner, GroupLayout.PREFERRED_SIZE, - 60, + 70, GroupLayout.PREFERRED_SIZE ) .addComponent(applyButton) @@ -131,7 +134,7 @@ public class BasicMorphologyView extends AbstractView implements IBasicMorpholog layout.createSequentialGroup() .addComponent(label) .addComponent( - sizeField, + sizeSpinner, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE @@ -142,18 +145,8 @@ public class BasicMorphologyView extends AbstractView implements IBasicMorpholog return panel; } - private JTextField createPositiveIntegerField() { - JTextField field = new JTextField("1", 5); - ((AbstractDocument) field.getDocument()).setDocumentFilter(new PositiveIntegerFilter()); - return field; - } - private void applyMorphology() { - Integer size = getMaskSize(); - - if (size == null || size % 2 == 0) { - return; - } + int size = (Integer) maskSizeSpinner.getValue(); switch (modeTab.getSelectedIndex()) { case 0 -> presenter.computeErosion(size); @@ -163,32 +156,22 @@ public class BasicMorphologyView extends AbstractView implements IBasicMorpholog } } - private Integer getMaskSize() { - try { - int size = Integer.parseInt(maskSizeField.getText()); - - if (size <= 0) { - return null; - } - - return size; - } catch (NumberFormatException e) { - return null; - } - } - + @Override public void updateErosion(Image image) { modeTab.loadImage(EROSION_TAB_TITLE, image); } + @Override public void updateDilation(Image image) { modeTab.loadImage(DILATION_TAB_TITLE, image); } + @Override public void updateOpening(Image image) { modeTab.loadImage(OPENING_TAB_TITLE, image); } + @Override public void updateClosing(Image image) { modeTab.loadImage(CLOSING_TAB_TITLE, image); } @@ -198,31 +181,4 @@ public class BasicMorphologyView extends AbstractView implements IBasicMorpholog presenter.setView(null); presenter.dispose(); } - - private static class PositiveIntegerFilter extends DocumentFilter { - - @Override - public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr - ) throws BadLocationException { - if (isValid(fb.getDocument().getText(0, fb.getDocument().getLength()), offset, 0, string)) { - super.insertString(fb, offset, string, attr); - } - } - - @Override - public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { - if (isValid(fb.getDocument().getText(0, fb.getDocument().getLength()), offset, length, text)) { - super.replace(fb, offset, length, text, attrs); - } - } - - private boolean isValid(String current, int offset, int length, String replacement) { - String result = current.substring(0, offset) + replacement + current.substring(offset + length); - if (result.isEmpty()) { - return true; - } - - return result.matches("[1-9][0-9]*"); - } - } } \ No newline at end of file diff --git a/src/main/java/ui/implementation/views/morphology/ComplexMorphologyView.java b/src/main/java/ui/implementation/views/morphology/ComplexMorphologyView.java new file mode 100644 index 0000000..0138351 --- /dev/null +++ b/src/main/java/ui/implementation/views/morphology/ComplexMorphologyView.java @@ -0,0 +1,234 @@ +package ui.implementation.views.morphology; + +import domain.image.Image; +import infrastructure.image.io.ImageLoader; +import infrastructure.ui.ImageMapper; +import jakarta.inject.Inject; +import presenters.morphology.ComplexMorphologyPresenter; +import ui.abstractions.AbstractView; +import ui.abstractions.IComplexMorphologyView; +import ui.implementation.components.controls.SaveButton; +import ui.implementation.components.image.TabbedImagePanel; +import ui.implementation.components.intent.FileChooser; + +import javax.swing.*; +import java.awt.*; +import java.io.File; +import java.io.IOException; + +public class ComplexMorphologyView extends AbstractView implements IComplexMorphologyView { + + private static final String DILATION_TAB_TITLE = "Dilatation géodésique"; + private static final String RECONSTRUCTION_TAB_TITLE = "Reconstruction géodésique"; + private static final String MEDIAN_TAB_TITLE = "Filtre médian"; + + private final ComplexMorphologyPresenter presenter; + + private TabbedImagePanel modeTab; + + private JButton maskButton; + private JLabel maskLabel; + private JSpinner iterationsSpinner; + + @Inject + public ComplexMorphologyView(ComplexMorphologyPresenter presenter) { + this.presenter = presenter; + } + + @Override + public void initComponents() { + modeTab = new TabbedImagePanel(); + + modeTab.addPreviewTab(DILATION_TAB_TITLE); + modeTab.addPreviewTab(RECONSTRUCTION_TAB_TITLE); + modeTab.addPreviewTab(MEDIAN_TAB_TITLE); + + iterationsSpinner = new JSpinner( + new SpinnerNumberModel(1, 1, Integer.MAX_VALUE, 1) + ); + + maskButton = new JButton("Choisir..."); + maskLabel = new JLabel("Aucun masque"); + + maskButton.addActionListener(e -> chooseGeodesicMask()); + + JButton applyButton = new JButton("Appliquer"); + applyButton.addActionListener(e -> applyMorphology()); + + 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("Traitement morphologique complexe"); + setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + pack(); + } + + @Override + public void initValues() { + modeTab.addChangeListener(e -> updateControls()); + + presenter.setView(this); + + updateControls(); + } + + private JPanel createControlPanel(JButton applyButton) { + JPanel panel = new JPanel(); + + GroupLayout layout = new GroupLayout(panel); + panel.setLayout(layout); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + + JLabel maskLabelTitle = new JLabel("Masque géodésique :"); + JLabel iterationsLabel = new JLabel("Nombre d'itérations :"); + + layout.setHorizontalGroup( + layout.createParallelGroup(GroupLayout.Alignment.CENTER) + .addComponent(maskLabelTitle) + .addComponent(maskButton) + .addComponent(maskLabel) + .addComponent(iterationsLabel) + .addComponent( + iterationsSpinner, + GroupLayout.PREFERRED_SIZE, + 70, + GroupLayout.PREFERRED_SIZE + ) + .addComponent(applyButton) + ); + + layout.setVerticalGroup( + layout.createSequentialGroup() + .addComponent(maskLabelTitle) + .addComponent(maskButton) + .addComponent(maskLabel) + .addComponent(iterationsLabel) + .addComponent( + iterationsSpinner, + GroupLayout.PREFERRED_SIZE, + GroupLayout.DEFAULT_SIZE, + GroupLayout.PREFERRED_SIZE + ) + .addComponent(applyButton) + ); + + return panel; + } + + private void chooseGeodesicMask() { + FileChooser chooser = new FileChooser("./"); + + if (chooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) { + return; + } + + File file = chooser.getSelectedFile(); + loadImage(file); + maskLabel.setText(file.getName()); + applyMorphology(); + } + + private void updateControls() { + boolean geodesicOperation = modeTab.getSelectedIndex() == 0 || modeTab.getSelectedIndex() == 1; + + maskButton.setEnabled(geodesicOperation); + maskLabel.setEnabled(geodesicOperation); + } + + private void applyMorphology() { + int operation = modeTab.getSelectedIndex(); + int iterations = (Integer) iterationsSpinner.getValue(); + + switch (operation) { + case 0 -> presenter.computeGeodesicDilation(iterations); + case 1 -> presenter.computeGeodesicReconstruction(); + case 2 -> presenter.computeMedianFilter(iterations); + } + } + + @Override + public void updateMask(Image mask) { + + } + + @Override + public void updateDilation(Image image) { + modeTab.loadImage(DILATION_TAB_TITLE, image); + } + + @Override + public void updateReconstruction(Image image) { + modeTab.loadImage(RECONSTRUCTION_TAB_TITLE, image); + } + + @Override + public void updateMedian(Image image) { + modeTab.loadImage(MEDIAN_TAB_TITLE, image); + } + + @Override + public void disposeCore() { + presenter.setView(null); + presenter.dispose(); + } + + private void loadImage(File file) { + try { + Image mask = ImageLoader.loadImage(file); + presenter.setMask(mask); + } catch (IOException ioe) { + displayErrorMessage("Oups !", "Impossible de charger le masque"); + } + } + + private void displayErrorMessage(String title, String message) { + JOptionPane.showMessageDialog( + this, + message, + title, + JOptionPane.ERROR_MESSAGE + ); + } +} \ No newline at end of file diff --git a/src/main/resources/Thumbs.db b/src/main/resources/Thumbs.db deleted file mode 100644 index 79d32e6..0000000 Binary files a/src/main/resources/Thumbs.db and /dev/null differ