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 showFiltering();
void showButterworth();
}

View File

@@ -4,6 +4,7 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import ui.implementation.views.DoubleMatrix;
import ui.implementation.views.IdealFilteringView;
import ui.implementation.views.NonIdealFilteringView;
import ui.implementation.views.ViewInitializer;
/**
@@ -15,27 +16,37 @@ public class SwingNavigator implements INavigator {
private final Provider<DoubleMatrix> doubleMatrixProvider;
private final Provider<IdealFilteringView> filteringProvider;
private final Provider<NonIdealFilteringView> nonIdealFilteringProvider;
@Inject
public SwingNavigator(
Provider<DoubleMatrix> doubleMatrixProvider,
Provider<IdealFilteringView> filteringProvider
Provider<IdealFilteringView> filteringProvider,
Provider<NonIdealFilteringView> nonIdealFilteringProvider
) {
this.doubleMatrixProvider = doubleMatrixProvider;
this.filteringProvider = filteringProvider;
this.nonIdealFilteringProvider = nonIdealFilteringProvider;
}
@Override
public void showFourier() {
DoubleMatrix doubleMatrix = doubleMatrixProvider.get();
ViewInitializer.Initialize(doubleMatrix);
doubleMatrix.setVisible(true);
DoubleMatrix view = doubleMatrixProvider.get();
ViewInitializer.Initialize(view);
view.setVisible(true);
}
@Override
public void showFiltering() {
IdealFilteringView filteringView = filteringProvider.get();
ViewInitializer.Initialize(filteringView);
filteringView.setVisible(true);
IdealFilteringView view = filteringProvider.get();
ViewInitializer.Initialize(view);
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();
}
public void gotToButterworth() {
navigator.showButterworth();
}
public GrayScaleMatrix getImageGrayMatrix() {
return appState.getImage().toGrayScale();
}
@@ -57,4 +62,5 @@ public class NavPresenter {
public void setMode(Mode mode) {
modeService.setMode(mode);
}
}

View File

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

View File

@@ -18,15 +18,20 @@ public class FilteringService {
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) {
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 IntensitySlider() {
public IntensitySlider(int min, int max) {
super();
setMinimum(0);
setMaximum(1000);
setMinimum(min);
setMaximum(max);
}
public float getPercentage(){

View File

@@ -73,7 +73,7 @@ public class NavBar extends JMenuBar implements INavBar {
new MenuItem("NG", e -> {
displayErrorMessage("Oups !", "Not implemented.");
})
)
)
);
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",
new Menu("Global",
new Menu("Ideal",
new MenuItem("Passe-bas", this::displayFilter),
new MenuItem("Passe-haut", this::displayFilter)),
new Menu("Butterworth",
new MenuItem("Passe-bas", this::displayFilter),
new MenuItem("Passe-haut", this::displayFilter)
)
new MenuItem("Ideal", this::displayIdealFilter),
new MenuItem("Butterworth", this::displayButterworth)
),
new Menu("Local",
new MenuItem("Masque de convolution", this::displayIdealFilter),
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();
}
private void displayButterworth(ActionEvent e) {
presenter.gotToButterworth();
}
private void displayErrorMessage(String title, String message) {
JOptionPane.showMessageDialog(
this,

View File

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

View File

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