Implement Butterworth filters

This commit is contained in:
2026-09-22 15:10:24 +02:00
parent b177ea5a38
commit 048ff5c792
9 changed files with 110 additions and 62 deletions

View File

@@ -11,5 +11,5 @@ public interface INavigator {
*/ */
void showFourier(); void showFourier();
void showFiltering(); void showFiltering();
void showButterworth();
} }

View File

@@ -4,6 +4,7 @@ import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import ui.implementation.views.DoubleMatrix; import ui.implementation.views.DoubleMatrix;
import ui.implementation.views.IdealFilteringView; import ui.implementation.views.IdealFilteringView;
import ui.implementation.views.NonIdealFilteringView;
import ui.implementation.views.ViewInitializer; import ui.implementation.views.ViewInitializer;
/** /**
@@ -15,27 +16,37 @@ public class SwingNavigator implements INavigator {
private final Provider<DoubleMatrix> doubleMatrixProvider; private final Provider<DoubleMatrix> doubleMatrixProvider;
private final Provider<IdealFilteringView> filteringProvider; private final Provider<IdealFilteringView> filteringProvider;
private final Provider<NonIdealFilteringView> nonIdealFilteringProvider;
@Inject @Inject
public SwingNavigator( public SwingNavigator(
Provider<DoubleMatrix> doubleMatrixProvider, Provider<DoubleMatrix> doubleMatrixProvider,
Provider<IdealFilteringView> filteringProvider Provider<IdealFilteringView> filteringProvider,
Provider<NonIdealFilteringView> nonIdealFilteringProvider
) { ) {
this.doubleMatrixProvider = doubleMatrixProvider; this.doubleMatrixProvider = doubleMatrixProvider;
this.filteringProvider = filteringProvider; this.filteringProvider = filteringProvider;
this.nonIdealFilteringProvider = nonIdealFilteringProvider;
} }
@Override @Override
public void showFourier() { public void showFourier() {
DoubleMatrix doubleMatrix = doubleMatrixProvider.get(); DoubleMatrix view = doubleMatrixProvider.get();
ViewInitializer.Initialize(doubleMatrix); ViewInitializer.Initialize(view);
doubleMatrix.setVisible(true); view.setVisible(true);
} }
@Override @Override
public void showFiltering() { public void showFiltering() {
IdealFilteringView filteringView = filteringProvider.get(); IdealFilteringView view = filteringProvider.get();
ViewInitializer.Initialize(filteringView); ViewInitializer.Initialize(view);
filteringView.setVisible(true); view.setVisible(true);
}
@Override
public void showButterworth() {
NonIdealFilteringView view = nonIdealFilteringProvider.get();
ViewInitializer.Initialize(view);
view.setVisible(true);
} }
} }

View File

@@ -50,6 +50,11 @@ public class NavPresenter {
navigator.showFiltering(); navigator.showFiltering();
} }
public void gotToButterworth() {
navigator.showButterworth();
}
public GrayScaleMatrix getImageGrayMatrix() { public GrayScaleMatrix getImageGrayMatrix() {
return appState.getImage().toGrayScale(); return appState.getImage().toGrayScale();
} }
@@ -57,4 +62,5 @@ public class NavPresenter {
public void setMode(Mode mode) { public void setMode(Mode mode) {
modeService.setMode(mode); modeService.setMode(mode);
} }
} }

View File

@@ -41,15 +41,15 @@ public class FilteringPresenter implements FilteringListener {
state.setHighPass(image); state.setHighPass(image);
} }
public void computeButterworthLowPassFiltering(float percentage) { public void computeButterworthLowPassFiltering(float percentage, int order) {
GrayScaleMatrix matrix = imageService.getGrayScale(appState.getImage()); GrayScaleMatrix matrix = imageService.getGrayScale(appState.getImage());
Image image = filteringService.butterworthLowPassFiltering(matrix, percentage / 100); Image image = filteringService.butterworthLowPassFiltering(matrix, percentage / 100, order);
state.setButterworthLowPass(image); state.setButterworthLowPass(image);
} }
public void computeButterworthHighPassFiltering(float percentage) { public void computeButterworthHighPassFiltering(float percentage, int order) {
GrayScaleMatrix matrix = imageService.getGrayScale(appState.getImage()); GrayScaleMatrix matrix = imageService.getGrayScale(appState.getImage());
Image image = filteringService.butterworthHighPassFiltering(matrix, percentage / 100); Image image = filteringService.butterworthHighPassFiltering(matrix, percentage / 100, order);
state.setButterworthHighPass(image); state.setButterworthHighPass(image);
} }

View File

@@ -18,15 +18,20 @@ public class FilteringService {
return filteredMatrix.toImage(); return filteredMatrix.toImage();
} }
public Image butterworthLowPassFiltering(GrayScaleMatrix matrix, float percentage, int order) {
double cutoff = percentage * getMaxCutoff(matrix.getWidth(), matrix.getHeight());
GrayScaleMatrix filteredMatrix = GlobalLinearFiltering.butterworthLowPassFiltering(matrix, cutoff, order);
return filteredMatrix.toImage();
}
public Image butterworthHighPassFiltering(GrayScaleMatrix matrix, float percentage, int order) {
double cutoff = percentage * getMaxCutoff(matrix.getWidth(), matrix.getHeight());
GrayScaleMatrix filteredMatrix = GlobalLinearFiltering.butterworthHighPassFiltering(matrix, cutoff, order);
return filteredMatrix.toImage();
}
private double getMaxCutoff(int width, int height) { private double getMaxCutoff(int width, int height) {
return Math.sqrt(Math.pow(width / 2.0, 2) + Math.pow(height / 2.0, 2)); return Math.sqrt(Math.pow(width / 2.0, 2) + Math.pow(height / 2.0, 2));
} }
public Image butterworthLowPassFiltering(GrayScaleMatrix matrix, float v) {
return null;
}
public Image butterworthHighPassFiltering(GrayScaleMatrix matrix, float v) {
return null;
}
} }

View File

@@ -5,10 +5,10 @@ import java.awt.event.ActionListener;
public class IntensitySlider extends JSlider { public class IntensitySlider extends JSlider {
public IntensitySlider() { public IntensitySlider(int min, int max) {
super(); super();
setMinimum(0); setMinimum(min);
setMaximum(1000); setMaximum(max);
} }
public float getPercentage(){ public float getPercentage(){

View File

@@ -73,7 +73,7 @@ public class NavBar extends JMenuBar implements INavBar {
new MenuItem("NG", e -> { new MenuItem("NG", e -> {
displayErrorMessage("Oups !", "Not implemented."); displayErrorMessage("Oups !", "Not implemented.");
}) })
) )
); );
fourierMenu = new Menu("Fourier","/cp_51_p1.jpg", fourierMenu = new Menu("Fourier","/cp_51_p1.jpg",
@@ -82,13 +82,12 @@ public class NavBar extends JMenuBar implements INavBar {
linearMenu = new Menu("Linéaire","/cp_51_p1.jpg", linearMenu = new Menu("Linéaire","/cp_51_p1.jpg",
new Menu("Global", new Menu("Global",
new Menu("Ideal", new MenuItem("Ideal", this::displayIdealFilter),
new MenuItem("Passe-bas", this::displayFilter), new MenuItem("Butterworth", this::displayButterworth)
new MenuItem("Passe-haut", this::displayFilter)), ),
new Menu("Butterworth", new Menu("Local",
new MenuItem("Passe-bas", this::displayFilter), new MenuItem("Masque de convolution", this::displayIdealFilter),
new MenuItem("Passe-haut", this::displayFilter) new MenuItem("Filtre moyenneur", this::displayButterworth)
)
) )
); );
@@ -222,10 +221,14 @@ public class NavBar extends JMenuBar implements INavBar {
} }
} }
private void displayFilter(ActionEvent e) { private void displayIdealFilter(ActionEvent e) {
presenter.goToFiltering(); presenter.goToFiltering();
} }
private void displayButterworth(ActionEvent e) {
presenter.gotToButterworth();
}
private void displayErrorMessage(String title, String message) { private void displayErrorMessage(String title, String message) {
JOptionPane.showMessageDialog( JOptionPane.showMessageDialog(
this, this,

View File

@@ -14,8 +14,8 @@ import javax.swing.event.ChangeEvent;
public class IdealFilteringView extends AbstractView implements IFilteringView { public class IdealFilteringView extends AbstractView implements IFilteringView {
private final static String LOW_PASS_TAB_TITLE = "Passe-haut"; private final static String LOW_PASS_TAB_TITLE = "Passe-bas";
private final static String HIGH_PASS_TAB_TITLE = "Passe-bas"; private final static String HIGH_PASS_TAB_TITLE = "Passe-haut";
private TabbedImagePanel modeTab; private TabbedImagePanel modeTab;
private IntensitySlider intensitySlider; private IntensitySlider intensitySlider;
@@ -31,7 +31,7 @@ public class IdealFilteringView extends AbstractView implements IFilteringView {
@Override @Override
public void initComponents(){ public void initComponents(){
this.intensityLabel = new JLabel(); this.intensityLabel = new JLabel();
this.intensitySlider = new IntensitySlider(); this.intensitySlider = new IntensitySlider(0, 1000);
this.intensitySlider.setOrientation(JSlider.VERTICAL); this.intensitySlider.setOrientation(JSlider.VERTICAL);
this.modeTab = new TabbedImagePanel(); this.modeTab = new TabbedImagePanel();
@@ -43,7 +43,7 @@ public class IdealFilteringView extends AbstractView implements IFilteringView {
saveButton.addActionListener(e -> {}); saveButton.addActionListener(e -> {});
buildLayout(modeTab, intensityLabel, intensitySlider, saveButton); buildLayout(modeTab, intensityLabel, intensitySlider, saveButton);
setTitle("Filtering"); setTitle("Filtre idéal");
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
pack(); pack();
} }

View File

@@ -14,12 +14,14 @@ import javax.swing.event.ChangeEvent;
public class NonIdealFilteringView extends AbstractView implements IFilteringView { public class NonIdealFilteringView extends AbstractView implements IFilteringView {
private final static String LOW_PASS_TAB_TITLE = "Passe-haut"; private final static String LOW_PASS_TAB_TITLE = "Passe-bas";
private final static String HIGH_PASS_TAB_TITLE = "Passe-bas"; private final static String HIGH_PASS_TAB_TITLE = "Passe-haut";
private TabbedImagePanel modeTab; private TabbedImagePanel modeTab;
private IntensitySlider intensitySlider; private IntensitySlider intensitySlider;
private IntensitySlider orderSlider;
private JLabel intensityLabel; private JLabel intensityLabel;
private JLabel orderLabel;
private final FilteringPresenter presenter; private final FilteringPresenter presenter;
private boolean isUpdating; private boolean isUpdating;
@@ -28,35 +30,36 @@ public class NonIdealFilteringView extends AbstractView implements IFilteringVie
this.presenter = presenter; this.presenter = presenter;
} }
private void buildLayout(JTabbedPane modeTab, JButton saveButton) {
private void buildLayout(JTabbedPane modeTab,
JLabel valueTextField,
JSlider intensitySlider,
JButton saveButton) {
JPanel rightPanel = new JPanel(); JPanel rightPanel = new JPanel();
GroupLayout rightLayout = new GroupLayout(rightPanel); GroupLayout rightLayout = new GroupLayout(rightPanel);
rightPanel.setLayout(rightLayout); rightPanel.setLayout(rightLayout);
rightLayout.setHorizontalGroup( rightLayout.setHorizontalGroup(
rightLayout.createParallelGroup(GroupLayout.Alignment.LEADING) rightLayout.createSequentialGroup()
.addComponent(valueTextField, GroupLayout.PREFERRED_SIZE, 90, GroupLayout.PREFERRED_SIZE) .addGroup(rightLayout.createParallelGroup(GroupLayout.Alignment.CENTER)
.addGroup(rightLayout.createSequentialGroup() .addComponent(intensityLabel)
.addComponent(intensitySlider) .addComponent(intensitySlider)
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED) )
.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(rightLayout.createParallelGroup(GroupLayout.Alignment.CENTER)
.addComponent(orderLabel)
.addComponent(orderSlider)
) )
); );
rightLayout.setVerticalGroup( rightLayout.setVerticalGroup(
rightLayout.createSequentialGroup() rightLayout.createSequentialGroup()
.addComponent(valueTextField, GroupLayout.PREFERRED_SIZE, .addGroup(rightLayout.createParallelGroup(GroupLayout.Alignment.BASELINE)
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addComponent(intensityLabel)
.addComponent(orderLabel)
)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addGroup(rightLayout.createParallelGroup(GroupLayout.Alignment.CENTER) .addGroup(rightLayout.createParallelGroup(GroupLayout.Alignment.CENTER)
.addComponent(intensitySlider) .addComponent(intensitySlider)
.addComponent(orderSlider)
) )
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
); );
// ------------------------- // -------------------------
@@ -72,10 +75,8 @@ public class NonIdealFilteringView extends AbstractView implements IFilteringVie
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
//.addComponent(valeurMin)
.addGap(50) .addGap(50)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
//.addComponent(valeurMax)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED,
GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(saveButton) .addComponent(saveButton)
@@ -117,8 +118,22 @@ public class NonIdealFilteringView extends AbstractView implements IFilteringVie
try { try {
this.intensityLabel.setText(String.valueOf(intensitySlider.getPercentage())); this.intensityLabel.setText(String.valueOf(intensitySlider.getPercentage()));
switch (modeTab.getSelectedIndex()) { switch (modeTab.getSelectedIndex()) {
case 0 -> presenter.computeLowPassFiltering(intensitySlider.getPercentage()); case 0 -> presenter.computeButterworthLowPassFiltering(intensitySlider.getPercentage(), orderSlider.getValue());
case 1 -> presenter.computeHighPassFiltering(intensitySlider.getPercentage()); case 1 -> presenter.computeButterworthHighPassFiltering(intensitySlider.getPercentage(), orderSlider.getValue());
}
} finally {
isUpdating = false;
}
}
private void onOrderUpdate(ChangeEvent e) {
if (isUpdating) return;
isUpdating = true;
try {
this.orderLabel.setText(String.valueOf(orderSlider.getValue()));
switch (modeTab.getSelectedIndex()) {
case 0 -> presenter.computeButterworthLowPassFiltering(intensitySlider.getPercentage(), orderSlider.getValue());
case 1 -> presenter.computeButterworthHighPassFiltering(intensitySlider.getPercentage(), orderSlider.getValue());
} }
} finally { } finally {
isUpdating = false; isUpdating = false;
@@ -136,21 +151,26 @@ public class NonIdealFilteringView extends AbstractView implements IFilteringVie
} }
@Override @Override
public void updateButterworthHighPassFiltering(Image image) { public void updateButterworthLowPassFiltering(Image image) {
modeTab.loadImage(LOW_PASS_TAB_TITLE, image); modeTab.loadImage(LOW_PASS_TAB_TITLE, image);
} }
@Override @Override
public void updateButterworthLowPassFiltering(Image image) { public void updateButterworthHighPassFiltering(Image image) {
modeTab.loadImage(HIGH_PASS_TAB_TITLE, image); modeTab.loadImage(HIGH_PASS_TAB_TITLE, image);
} }
@Override @Override
public void initComponents(){ public void initComponents(){
this.intensityLabel = new JLabel(); this.intensityLabel = new JLabel();
this.intensitySlider = new IntensitySlider(); this.orderLabel = new JLabel();
this.intensitySlider = new IntensitySlider(0, 1000);
this.intensitySlider.setOrientation(JSlider.VERTICAL); this.intensitySlider.setOrientation(JSlider.VERTICAL);
this.orderSlider = new IntensitySlider(0, 10);
this.orderSlider.setOrientation(JSlider.VERTICAL);
this.modeTab = new TabbedImagePanel(); this.modeTab = new TabbedImagePanel();
this.modeTab.addPreviewTab(LOW_PASS_TAB_TITLE); this.modeTab.addPreviewTab(LOW_PASS_TAB_TITLE);
this.modeTab.addPreviewTab(HIGH_PASS_TAB_TITLE); this.modeTab.addPreviewTab(HIGH_PASS_TAB_TITLE);
@@ -159,8 +179,8 @@ public class NonIdealFilteringView extends AbstractView implements IFilteringVie
saveButton.addActionListener(e -> {}); saveButton.addActionListener(e -> {});
buildLayout(modeTab, intensityLabel, intensitySlider, saveButton); buildLayout(modeTab, saveButton);
setTitle("Filtering"); setTitle("Filtre non-idéal");
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
pack(); pack();
} }
@@ -172,14 +192,17 @@ public class NonIdealFilteringView extends AbstractView implements IFilteringVie
this.intensitySlider.addChangeListener(this::onCutoffUpdate); this.intensitySlider.addChangeListener(this::onCutoffUpdate);
this.intensityLabel.setText(String.valueOf(intensitySlider.getValue())); this.intensityLabel.setText(String.valueOf(intensitySlider.getValue()));
this.orderSlider.addChangeListener(this::onOrderUpdate);
this.orderLabel.setText(String.valueOf(orderSlider.getValue()));
this.modeTab.addChangeListener(e -> { this.modeTab.addChangeListener(e -> {
switch (modeTab.getSelectedIndex()) { switch (modeTab.getSelectedIndex()) {
case 0 -> presenter.computeLowPassFiltering(intensitySlider.getPercentage()); case 0 -> presenter.computeButterworthLowPassFiltering(intensitySlider.getPercentage(), orderSlider.getValue());
case 1 -> presenter.computeHighPassFiltering(intensitySlider.getPercentage()); case 1 -> presenter.computeButterworthHighPassFiltering(intensitySlider.getPercentage(), orderSlider.getValue());
} }
}); });
this.presenter.computeLowPassFiltering(intensitySlider.getPercentage()); this.presenter.computeButterworthLowPassFiltering(intensitySlider.getPercentage(), orderSlider.getValue());
} }
@Override @Override