Add filtering operations

This commit is contained in:
2026-09-21 19:06:57 +02:00
parent bab8f81966
commit 1bca7a5ef2
15 changed files with 376 additions and 119 deletions

View File

@@ -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")

View File

@@ -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(() -> {

View File

@@ -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);

View File

@@ -10,5 +10,6 @@ public interface INavigator {
* Displays Fourier view
*/
void showFourier();
void showFiltering();
}

View File

@@ -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<DoubleMatrix> doubleMatrixProvider;
private final Provider<FilteringView> filteringProvider;
@Inject
public SwingNavigator(
Provider<DoubleMatrix> doubleMatrixProvider
Provider<DoubleMatrix> doubleMatrixProvider,
Provider<FilteringView> 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);
}
}

View File

@@ -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(){
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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<imageNG.getLargeur() ; i++)
// for(int j=0 ; j<imageNG.getHauteur() ; j++) f[i][j] = (double)(f_int[i][j]);
//
// System.out.println("Debut Fourier");
// MatriceComplexe fourier = Fourier.Fourier2D(f);
// System.out.println("Fin Fourier");
// fourier = Fourier.decroise(fourier);
// double partieImaginaire[][] = fourier.getPartieImaginaire();
//
// DoubleMatrix dialog = new DoubleMatrix(this,true,partieImaginaire,"Fourier : Affichage de la partie imaginaire");
// dialog.setVisible(true);
// }
// catch (CImageNGException ex)
// {
// System.out.println("Erreur CImageNG : " + ex.getMessage());
// }
}
private void jMenuItemFourierAfficherPartieReelleActionPerformed(ActionEvent e) {
// try
// {
// int f_int[][] = imageNG.getMatrice();
// double f[][] = new double[imageNG.getLargeur()][imageNG.getHauteur()];
// for(int i=0 ; i<imageNG.getLargeur() ; i++)
// for(int j=0 ; j<imageNG.getHauteur() ; j++) f[i][j] = (double)(f_int[i][j]);
//
// System.out.println("Debut Fourier");
// MatriceComplexe fourier = Fourier.Fourier2D(f);
// System.out.println("Fin Fourier");
// fourier = Fourier.decroise(fourier);
// double partieReelle[][] = fourier.getPartieReelle();
//
// DoubleMatrix dialog = new DoubleMatrix(this,true,partieReelle,"Fourier : Affichage de la partie reelle");
// dialog.setVisible(true);
// }
// catch (CImageNGException ex)
// {
// System.out.println("Erreur CImageNG : " + ex.getMessage());
// }
}
private void jMenuItemFourierAfficherPhaseActionPerformed(ActionEvent e) {
// try
// {
// int f_int[][] = imageNG.getMatrice();
// double f[][] = new double[imageNG.getLargeur()][imageNG.getHauteur()];
// for(int i=0 ; i<imageNG.getLargeur() ; i++)
// for(int j=0 ; j<imageNG.getHauteur() ; j++) f[i][j] = (double)(f_int[i][j]);
//
// System.out.println("Debut Fourier");
// MatriceComplexe fourier = Fourier.Fourier2D(f);
// System.out.println("Fin Fourier");
// fourier = Fourier.decroise(fourier);
// double phase[][] = fourier.getPhase();
//
// DoubleMatrix dialog = new DoubleMatrix(this,true,phase,"Fourier : Affichage de la phase");
// dialog.setVisible(true);
// }
// catch (CImageNGException ex)
// {
// System.out.println("Erreur CImageNG : " + ex.getMessage());
// }
}
private void displayFourier(ActionEvent e) {
try
{
presenter.goToFourier();
// int f_int[][] = imageNG.getMatrice();
// double f[][] = new double[imageNG.getLargeur()][imageNG.getHauteur()];
// for(int i=0 ; i<imageNG.getLargeur() ; i++)
// for(int j=0 ; j<imageNG.getHauteur() ; j++) f[i][j] = (double)(f_int[i][j]);
//
// System.out.println("Debut Fourier");
// MatriceComplexe fourier = Fourier.Fourier2D(f);
// System.out.println("Fin Fourier");
// fourier = Fourier.decroise(fourier);
// double module[][] = fourier.getModule();
//
// DoubleMatrix dialog = new DoubleMatrix(this,true,module,"Fourier : Affichage du module");
// dialog.setVisible(true);
}
catch (Exception ex)
{
System.out.println("Erreur CImageNG : " + ex.getMessage());
}
}
private void displayFilter(ActionEvent e) {
presenter.goToFiltering();
}
private void displayErrorMessage(String title, String message) {
JOptionPane.showMessageDialog(
this,
message,
title,
JOptionPane.ERROR_MESSAGE
);
}
}

View File

@@ -266,25 +266,24 @@ public class DoubleMatrix extends JFrame implements IDoubleMatrix
}
}
@Override
public void updateModule(GrayScaleMatrix source, GrayScaleMatrix clipped) {
updatePreview(modulePreviewContainer, source, clipped);
public void updateModule(GrayScaleMatrix source, GrayScaleMatrix matrix) {
updatePreview(modulePreviewContainer, source, matrix);
}
@Override
public void updatePhase(GrayScaleMatrix source, GrayScaleMatrix clipped) {
updatePreview(phasePreviewContainer, source, clipped);
public void updatePhase(GrayScaleMatrix source, GrayScaleMatrix matrix) {
updatePreview(phasePreviewContainer, source, matrix);
}
@Override
public void updateReal(GrayScaleMatrix source, GrayScaleMatrix clipped) {
updatePreview(realPreviewContainer, source, clipped);
public void updateReal(GrayScaleMatrix source, GrayScaleMatrix matrix) {
updatePreview(realPreviewContainer, source, matrix);
}
@Override
public void updateImaginary(GrayScaleMatrix source, GrayScaleMatrix clipped) {
updatePreview(imaginaryPreviewContainer, source, clipped);
public void updateImaginary(GrayScaleMatrix source, GrayScaleMatrix matrix) {
updatePreview(imaginaryPreviewContainer, source, matrix);
}
private void updatePreview(ImagePanel imagePanel, GrayScaleMatrix source, GrayScaleMatrix matrix) {

View File

@@ -0,0 +1,58 @@
package ui.implementation.views;
import com.google.inject.Inject;
import infrastructure.ui.ImageMapper;
import presenters.FilteringPresenter;
import ui.implementation.components.image.ImagePanel;
import ui.interfaces.IFilteringView;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class FilteringView extends JFrame implements IFilteringView {
private ImagePanel imagePanel;
private final FilteringPresenter presenter;
@Inject
public FilteringView(FilteringPresenter presenter) {
this.presenter = presenter;
this.presenter.setView(this);
initComponents();
}
private void initComponents(){
this.imagePanel = new ImagePanel();
this.imagePanel.loadImage(ImageMapper.toBufferedImage(presenter.computeHighPassFiltering()));
buildLayout();
setTitle("Filtering");
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
pack();
}
private void buildLayout(){
setLayout(new BorderLayout());
add(imagePanel, BorderLayout.CENTER);
}
@Override
public void updateLowPassFiltering(BufferedImage image) {
this.imagePanel.loadImage(image);
}
@Override
public void updateHighPassFiltering(BufferedImage image) {
}
@Override
public void updateButterworthHighPassFiltering(BufferedImage image) {
}
@Override
public void updateButterworthLowPassFiltering(BufferedImage image) {
}
}

View File

@@ -89,7 +89,7 @@ public class MainView extends JFrame implements IMainView
.addContainerGap())
);
setSize(new Dimension(500, 400));
setSize(new Dimension(750, 500));
setLocationRelativeTo(null);
}

View File

@@ -0,0 +1,10 @@
package ui.interfaces;
import java.awt.image.BufferedImage;
public interface IFilteringView {
void updateLowPassFiltering(BufferedImage image);
void updateHighPassFiltering(BufferedImage image);
void updateButterworthHighPassFiltering(BufferedImage image);
void updateButterworthLowPassFiltering(BufferedImage image);
}