From 435c1b9527730bc9c66bbf5744f952dab26345fb Mon Sep 17 00:00:00 2001 From: Salim Kanoun Date: Mon, 18 Sep 2017 13:56:35 +0200 Subject: [PATCH] Add files via upload Initial Release Candidate --- About.java | 259 ++++++++++++ Condense_Dynamique.java | 230 +++++++++++ Controleur_Shunpo.java | 398 ++++++++++++++++++ Controleur_VG_Dynamique.java | 742 ++++++++++++++++++++++++++++++++++ Controleur_VG_Roi.java | 653 ++++++++++++++++++++++++++++++ Modele_Shunpo.java | 629 +++++++++++++++++++++++++++++ Modele_VG_Dynamique.java | 266 ++++++++++++ Modele_VG_Roi.java | 495 +++++++++++++++++++++++ PrefsWindow.java | 121 ++++++ Vue_Shunpo.java | 758 +++++++++++++++++++++++++++++++++++ Vue_VG_Dynamique.java | 416 +++++++++++++++++++ Vue_VG_Roi.java | 588 +++++++++++++++++++++++++++ 12 files changed, 5555 insertions(+) create mode 100644 About.java create mode 100644 Condense_Dynamique.java create mode 100644 Controleur_Shunpo.java create mode 100644 Controleur_VG_Dynamique.java create mode 100644 Controleur_VG_Roi.java create mode 100644 Modele_Shunpo.java create mode 100644 Modele_VG_Dynamique.java create mode 100644 Modele_VG_Roi.java create mode 100644 PrefsWindow.java create mode 100644 Vue_Shunpo.java create mode 100644 Vue_VG_Dynamique.java create mode 100644 Vue_VG_Roi.java diff --git a/About.java b/About.java new file mode 100644 index 00000000..ceabdbb1 --- /dev/null +++ b/About.java @@ -0,0 +1,259 @@ +/** +Copyright (C) 2017 MOHAND Mathis and KANOUN Salim +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public v.3 License as published by +the Free Software Foundation; +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +import javax.imageio.ImageIO; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import java.awt.GridLayout; +import java.awt.Image; +import java.awt.RenderingHints; + +import javax.swing.JTable; +import javax.swing.table.DefaultTableModel; + +import ij.plugin.PlugIn; + +import javax.swing.JTextField; +import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.awt.event.ActionEvent; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics2D; + +import javax.swing.JScrollPane; +import javax.swing.SwingConstants; +import javax.swing.JTextPane; + +public class About extends JDialog implements PlugIn { + /** + * + */ + private static final long serialVersionUID = 1L; + + private JPanel panel; + private JPanel PanelBas; + private JButton btnOk; + private JTextField txtAbout; + private JScrollPane scrollPane_1; + private JTable table; + private JPanel About; + private JPanel Logo; + private JPanel CHUToulouse; + private JPanel IUTInformatique; + private JPanel ImageJ; + private JPanel Oncopole; + private JTextPane txtpnGeneralPublicLicence; + private JTextPane txtpnGeneralPublicLicence_1; + private JPanel panel_1; + private JPanel panel_2; + private JTextPane txtpnProjectLeaders; + private JTextPane txtpnGerardVictorPierre; + + /** + * Launch the application. + */ + public static void main(String[] args) { + try { + About dialog = new About(); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.setVisible(true); + dialog.setSize(800,500); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Create the dialog. + */ + public About() { + setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + setAlwaysOnTop(true); + setBounds(100, 100, 545, 402); + getContentPane().setLayout(new GridLayout(3, 1, 0, 0)); + { + panel = new JPanel(); + getContentPane().add(panel); + panel.setLayout(new GridLayout(2, 1, 0, 0)); + { + About = new JPanel(); + panel.add(About); + About.setLayout(new GridLayout(0, 1, 0, 0)); + { + txtAbout = new JTextField(); + txtAbout.setFont(new Font("Tahoma", Font.BOLD, 16)); + txtAbout.setEditable(false); + About.add(txtAbout); + txtAbout.setBackground(Color.LIGHT_GRAY); + txtAbout.setHorizontalAlignment(SwingConstants.CENTER); + txtAbout.setText("About us..."); + txtAbout.setColumns(5); + } + } + { + Logo = new JPanel(); + panel.add(Logo); + Logo.setLayout(new GridLayout(1, 0, 0, 0)); + { + CHUToulouse = new JPanel(); + BufferedImage myPicture; + try { + myPicture = ImageIO.read(getClass().getResource("/logos/logo_chu_toulouse.jpeg")); + BufferedImage myPicture2=(BufferedImage) this.scale(myPicture, 167, 62); + JLabel picLabel = new JLabel(new ImageIcon(myPicture2)); + CHUToulouse.add(picLabel); + } catch (IOException e) { + e.printStackTrace(); + } + + Logo.add(CHUToulouse); + } + { + IUTInformatique = new JPanel(); + BufferedImage myPicture; + try { + myPicture = ImageIO.read(getClass().getResource("/logos/IUT_Toulouse.jpg")); + BufferedImage myPicture2=(BufferedImage) this.scale(myPicture, 112, 70); + JLabel picLabel = new JLabel(new ImageIcon(myPicture2)); + IUTInformatique.add(picLabel); + } catch (IOException e) { + e.printStackTrace(); + } + Logo.add(IUTInformatique); + } + { + Oncopole = new JPanel(); + BufferedImage myPicture; + try { + myPicture = ImageIO.read(getClass().getResource("/logos/Institut-Claudius-Regaud.jpg")); + BufferedImage myPicture2=(BufferedImage) this.scale(myPicture, 187, 64); + JLabel picLabel = new JLabel(new ImageIcon(myPicture2)); + Oncopole.add(picLabel); + } catch (IOException e) {e.printStackTrace();} + Logo.add(Oncopole); + } + { + ImageJ = new JPanel(); + BufferedImage myPicture; + try { + myPicture = ImageIO.read(getClass().getResource("/logos/ImageJ.png")); + BufferedImage myPicture2=(BufferedImage) this.scale(myPicture, 64, 64); + JLabel picLabel = new JLabel(new ImageIcon(myPicture2)); + ImageJ.add(picLabel); + } catch (IOException e) {e.printStackTrace();} + Logo.add(ImageJ); + } + + } + } + { + } + { + scrollPane_1 = new JScrollPane(); + getContentPane().add(scrollPane_1); + { + table = new JTable(); + table.setModel(new DefaultTableModel( + new Object[][] { + {"Pulmonary Shunt", "Gerard Victor", "Mathis Mohand", "CHU Toulouse"}, + {"Gastric Emptying", "Gerard Victor", "Ping Xie", "CHU Toulouse"}, + }, + new String[] { + "Software", "Creator", "Developper", "Institution" + } + )); + scrollPane_1.setViewportView(table); + } + } + { + PanelBas = new JPanel(); + getContentPane().add(PanelBas); + PanelBas.setLayout(new GridLayout(3, 1, 0, 0)); + { + } + { + panel_2 = new JPanel(); + PanelBas.add(panel_2); + panel_2.setLayout(new GridLayout(0, 2, 0, 0)); + { + txtpnProjectLeaders = new JTextPane(); + txtpnProjectLeaders.setBackground(Color.LIGHT_GRAY); + panel_2.add(txtpnProjectLeaders); + txtpnProjectLeaders.setText("Project Leaders :"); + } + { + txtpnGeneralPublicLicence_1 = new JTextPane(); + panel_2.add(txtpnGeneralPublicLicence_1); + txtpnGeneralPublicLicence_1.setBackground(Color.LIGHT_GRAY); + txtpnGeneralPublicLicence_1.setText("General Public Licence v.3"); + } + } + btnOk = new JButton("OK"); + btnOk.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent arg0) { + dispose(); + } + }); + { + panel_1 = new JPanel(); + PanelBas.add(panel_1); + panel_1.setLayout(new GridLayout(0, 2, 0, 0)); + { + txtpnGerardVictorPierre = new JTextPane(); + txtpnGerardVictorPierre.setBackground(Color.LIGHT_GRAY); + panel_1.add(txtpnGerardVictorPierre); + txtpnGerardVictorPierre.setText("Gerard Victor, Pierre Pascal, Olivier Morel\r\nSalim Kanoun, Ilan Tal"); + } + { + txtpnGeneralPublicLicence = new JTextPane(); + panel_1.add(txtpnGeneralPublicLicence); + txtpnGeneralPublicLicence.setFont(new Font("Tahoma", Font.BOLD, 15)); + txtpnGeneralPublicLicence.setBackground(Color.LIGHT_GRAY); + txtpnGeneralPublicLicence.setText(" http://petctviewer.org\r\n"); + } + } + PanelBas.add(btnOk); + } + } + + private Image scale(Image source, int width, int height) { + /* On crée une nouvelle image aux bonnes dimensions. */ + BufferedImage buf = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + + /* On dessine sur le Graphics de l'image bufferisée. */ + Graphics2D g = buf.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.drawImage(source, 0, 0, width, height, null); + g.dispose(); + + /* On retourne l'image bufferisée, qui est une image. */ + return buf; + } + + @Override + public void run(String arg0) { + About dialog = new About(); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.setVisible(true); + dialog.setResizable(false); + dialog.setSize(800,500); + + } + +} diff --git a/Condense_Dynamique.java b/Condense_Dynamique.java new file mode 100644 index 00000000..dbec4bf1 --- /dev/null +++ b/Condense_Dynamique.java @@ -0,0 +1,230 @@ +/** +Copyright (C) 2017 PING Xie and KANOUN Salim +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public v.3 License as published by +the Free Software Foundation; +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +import java.awt.*; +import java.util.ArrayList; +import ij.*; +import ij.gui.*; +import ij.plugin.ContrastEnhancer; +import ij.plugin.MontageMaker; +import ij.plugin.PlugIn; +import ij.plugin.ZProjector; + +public class Condense_Dynamique implements PlugIn { + + private ArrayList condenses = new ArrayList(); + private String tag=new String(); + private Dimension dimCondense; + private Point p=new Point(); + + @Override + public void run(String arg) { + ouvertureImage(); + } + + private void ouvertureImage() { + + IJ.setTool("Rectangle"); + + WaitForUserDialog wait=new WaitForUserDialog("Please Open Images"); + wait.show(); + //On liste les images ouvertes + String[] imagesOuvertes=WindowManager.getImageTitles(); + + if (imagesOuvertes!=null) { + + } + + //On ferme les images posterieures et on assigne a chaque image un nom unique car sinon confusion du programme (les images originales on le meme nom) + for (int i=0 ; i Probleme de draw de l'overlay avant capture // EDT + laVue.overlay.add(laVue.leRoi.getRoi(10)); + //FIN A DEBEUGER SALIM + laVue.win.getImagePlus().setOverlay(laVue.overlay);; + capture[2]=Modele_Shunpo.captureImage(laVue.win.getImagePlus(),512,512); + etat = etat.next() ; + laVue.overlay.clear(); + laVue.win.getImagePlus().setRoi((Roi) laVue.leRoi.getRoi(10)); + laVue.win.showSlice(1); + laVue.overlayDG(); + laVue.win.getImagePlus().setOverlay(laVue.overlay); + laVue.setInstructions(listeInstructions[index]); + break; + case Fin: + addRoi("Brain Ant"); + leModele.calculerCoups("CA",laVue.win.getImagePlus()); + laVue.overlay.add(laVue.leRoi.getRoi(12)); + capture[3]=Modele_Shunpo.captureImage(laVue.win.getImagePlus(),512,512); + laVue.win.getImagePlus().deleteRoi(); + laVue.overlay.clear(); + String[] resultats = leModele.resultats(); + laVue.labelsResultats(resultats) ; + //On passe les capture en stack + ImageStack stackCapture=Modele_Shunpo.captureToStack(capture); + //on fait le montage du stack et on g茅n猫re l'interface resultat + laVue.UIResultats(leModele.montage(stackCapture, nomProgramme)); + laVue.win.close(); + laVue.leRoi.close(); + } + } + if (b == laVue.lesBoutons.get("Capture")){ + laVue.lesBoutons.get("Capture").setVisible(false); + laVue.Csv.setText("Provided By Petctviewer.org"); + ImagePlus captureFinale =Modele_Shunpo.captureFenetre(WindowManager.getCurrentImage(),0,0); + WindowManager.getCurrentWindow().close(); + //On genere la 2eme partie des tag dicom et on l'ajoute è„¿ la 1ere partie dans le property de l'image finale + captureFinale.setProperty("Info", tagCapture+=(Modele_Shunpo.genererDicomTagsPartie2(captureFinale))); + //On affiche et on agrandie la fenetre de la capture finale + captureFinale.show(); + captureFinale.getCanvas().setScaleToFit(true); + //On met un zoom a 80% + captureFinale.getCanvas().setMagnification(0.8); + //On sauve les resultats en CSV et ZIP + try { + String[] resultatscsv= leModele.buildCSVResultats(); + Modele_Shunpo.exportAll(resultatscsv,2,laVue.leRoi, nomProgramme,captureFinale); + } catch (FileNotFoundException e) {} + //On fait la capture finale + captureFinale.getWindow().toFront(); + //On propose de sauver la capture en DICOM + IJ.run("myDicom..."); + //fin du programme ici + + } + + if (b == laVue.lesBoutons.get("Precedent")) { + switch(etat) { + + case PoumonD_Post: + break; + + case PoumonG_Post: + retour(); + break; + + case ReinD_Post: + retour(); + break; + + case ReinG_Post: + retour(); + break; + + case BDF: + retour(); + laVue.overlay.clear(); + laVue.overlayDG(); + break; + + case PoumonD_Ant: + retour(); + laVue.win.showSlice(2); + break; + + case PoumonG_Ant: + retour(); + break; + + case ReinD_Ant: + retour(); + break; + + case ReinG_Ant: + retour(); + break; + + case Poumon_valide: + retour(); + laVue.overlay.clear(); + laVue.overlayDG(); + break; + + case Cerveau_Post: + break; + + case Cerveau_Ant: + break; + + case Fin: + retour(); + laVue.win.showSlice(1); + break; + + } + } + if (b == laVue.lesBoutons.get("Draw ROI")) + IJ.setTool(Toolbar.POLYGON); + + if (b == laVue.lesBoutons.get("Contrast")) + IJ.run("Window Level Tool"); + + if (b == laVue.lesBoutons.get("Quitter")) { + laVue.end("") ; + return; + } + + if (b == laVue.lesBoutons.get("Show Log")) + + //Regarder methode de Ping pour changer le libelle des bouttons + if (!showLog){ + showLog=true; + laVue.lesBoutons.get("Show Log").setLabel("Hide Log"); + } + + else{ + showLog=false; + laVue.lesBoutons.get("Show Log").setLabel("Show Log"); + } + + } + + private void genererBDF() { + Roi[] rois = laVue.leRoi.getRoisAsArray() ; + Rectangle r = new Rectangle(15, 30); + int x = (int) ((rois[2].getBounds().getLocation().x + rois[3].getBounds().getLocation().x + rois[3].getBounds().getWidth() ) / 2) ; + int y = (rois[2].getBounds().getLocation().y + rois[3].getBounds().getLocation().y ) / 2 ; + r.setLocation(x, y); + laVue.win.getImagePlus().setRoi(r); + } + + private void addRoi(String nom){ + // On verifie que la ROI n'existe pas dans le ROI manager avant de l'ajouter pour eviter les doublons + if (laVue.leRoi.getRoi(index)==null){ + laVue.leRoi.add(laVue.win.getImagePlus(), laVue.win.getImagePlus().getRoi(), index); + laVue.leRoi.rename(index, nom); + } + // Si elle existe on fait un update. Si elle a 閠� perdue dans l'imagePlus on revient a la ROI sauvegardee et on notifie l'utilisateur + else { + if (laVue.win.getImagePlus().getRoi()==null){ + IJ.showMessage("Roi lost, restoring previous saved ROI"); + laVue.leRoi.select(index); + } + laVue.leRoi.runCommand("Update"); + } + index++; + } + + private void retour() { + etat = etat.previous() ; + index -- ; + laVue.setInstructions(listeInstructions[index]); + laVue.leRoi.select(index); + } +} diff --git a/Controleur_VG_Dynamique.java b/Controleur_VG_Dynamique.java new file mode 100644 index 00000000..b4ff8999 --- /dev/null +++ b/Controleur_VG_Dynamique.java @@ -0,0 +1,742 @@ +/** +Copyright (C) 2017 PING Xie and KANOUN Salim +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public v.3 License as published by +the Free Software Foundation; +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +import java.awt.Button; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JOptionPane; +import ij.IJ; +import ij.ImagePlus; +import ij.WindowManager; +import ij.gui.Roi; +import ij.gui.Toolbar; +import ij.util.DicomTools; + +public class Controleur_VG_Dynamique implements ActionListener { + + private Vue_VG_Dynamique laVue; + + private Modele_VG_Dynamique.Etat etat; + + private Modele_VG_Dynamique leModele; + + private String[] listeInstructions = { "Delimit the stomac", "Delimit the intestine", "Adjuste the stomac", + "Adjuste the intestine and to next image", "Next to correct the result by background noise", + "Next to set default ROIs ", "Duplicate the ROI adjuste it to the area of egg", + "Next to correct the result by ingested eggs ","Fin" }; + + private int index_Roi;// index de la Roi + + private int index_Instru;// index de l'instruction + + protected ImagePlus ze; + + private int index_Ingestion;//index de l'ingestion (index de la serie) + + protected static int oeufsIngere;//le nombre de oeufs ingeres + + private int index_Oeuf;//index de l'oeuf + + private boolean estAntreCorrect;//signifie si'l y a intersection entre la ROI de l'estomac et de l'intestin pour fabriquer la ROi de l'antre + + private int reponseFinBDFAntre;//la reponse de l'utilisateur quand il est demande si sur la serie courant la region de l'antre est bruit de fond + + private int reponseFinBDFIntestin;//la reponse de l'utilisateur quand il est demande si sur la serie courant la region de l'intestin est bruit de fond + + public Controleur_VG_Dynamique(Vue_VG_Dynamique vue, Modele_VG_Dynamique leModele) { + this.index_Roi = 0; + this.index_Instru = 0; + this.index_Oeuf = 1; + this.laVue = vue; + this.leModele = leModele; + this.etat = Modele_VG_Dynamique.Etat.ESTOMAC_ANT; + this.estAntreCorrect = true; + this.reponseFinBDFAntre = 1; + this.reponseFinBDFIntestin = 1; + } + + @Override + public void actionPerformed(ActionEvent arg0) { + Button b = (Button) arg0.getSource(); + if (b == laVue.lesBoutons.get("Suivant")) { + + switch (etat) { + + case ESTOMAC_ANT: + // si le ROI n'est pas present on demande a l'utilisateur de le faire + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please delimite the stomach!"); + //si le Roi exist deja on affiche le ROI existante pour que l'utilisateur puisse la modifier + if(laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi); + } + + break; + } else { + leModele.initModele(laVue.imp); + oeufsIngere = laVue.imp.getStackSize() / 2; + index_Ingestion = laVue.imp.getStackSize() / 2; + addRoi("Stomach_Ant" + index_Ingestion); + } + leModele.calculerCoupsBrut(index_Ingestion, 0, 3, laVue.imp); + laVue.imp.killRoi(); + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + etat = etat.next(); + laVue.lesBoutons.get("Precedent").setEnabled(true); + leModele.setAcquisitionTime(index_Ingestion, DicomTools.getTag(laVue.imp, "0008,0032")); + leModele.setNomSerie(index_Ingestion, DicomTools.getTag(laVue.imp, "0008,103E")); + // on regarde si la ROI suivante est deja dans le ROI manager, si oui on l'affiche + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi); + } + break; + + case INTESTIN_ANT: + // si le ROI n'est pas present on demande a l'utilisateur de le faire + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please delimite the intestine!"); + //si le Roi exist deja on affiche le ROI existante pour que l'utilisateur puisse la modifier + if(laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi); + } + break; + } else { + addRoi("Intestine_Ant" + index_Ingestion); + } + //On fait les calculs + leModele.calculerCoupsBrut(index_Ingestion, 0, 2, laVue.imp); + laVue.imp.killRoi(); + //On genere la roiAntre + getAntreFundus("Ant"); + // Verification d'une intrestection entre les 2 ROIs + if (!estAntreCorrect) { + estAntreCorrect = true; + break; + } + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + etat = etat.next(); + demandeFinBDFAntre(); + demandeFinBDFIntestin(); + //On passe a l'image post + laVue.windowstack.showSlice(2); + laVue.imp.setSlice(2); + //On regarde si la ROI suivante est presente et sinon on met la ROI n-2 + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + break; + + case ESTOMAC_POS: + // On verifie que la ROI a ete dessinee + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please click on roi " + laVue.leRoi.getRoi(index_Roi - 2).getName() + " and adjust it!"); + laVue.leRoi.deselect(); + //si la Roi exite pas, on met la Roi precedente, sinon on met la Roi existante pour qu'on puisse le modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + else{ + laVue.leRoi.select(index_Roi); + } + + + break; + } else { + modifierRoi("Stomach_Pos", index_Ingestion, 1, 3); + } + etat = etat.next(); + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + //On regarde si la ROI suivante est presente et sinon on met la ROI n-2 + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + break; + case INTESTIN_POS: + // On verifie que la ROI est presente + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please click on roi " + laVue.leRoi.getRoi(index_Roi - 2).getName() + " and adjust it!"); + laVue.leRoi.deselect(); + //si la Roi exite pas, on met la Roi precedente, sinon on met la Roi existante pour qu'on puisse le modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + else{ + laVue.leRoi.select(index_Roi); + } + break; + } else { + modifierRoi("Intestine_Pos", index_Ingestion, 1, 2); + } + //On genere l'antre + getAntreFundus("Pos"); + //On verifie qu'il existe une intersection + if (!estAntreCorrect) { + estAntreCorrect = true; + break; + } + etat = etat.next(); + index_Instru--; + laVue.setInstructions(listeInstructions[index_Instru]); + index_Ingestion--; + //On change de coupe + laVue.windowstack.showSlice((laVue.imp.getStackSize() / 2 - index_Ingestion) * 2 + 1); + laVue.imp.setSlice((laVue.imp.getStackSize() / 2 - index_Ingestion) * 2 + 1); + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + break; + + case CIR_ESTOMAC_ANT: + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please click on roi " + laVue.leRoi.getRoi(index_Roi - 2).getName() + " and adjust it!"); + laVue.leRoi.deselect(); + //si la Roi exite pas, on met la Roi precedente, sinon on met la Roi existante pour qu'on puisse le modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + else{ + laVue.leRoi.select(index_Roi); + } + break; + } else { + + modifierRoi("Stomach_Ant", index_Ingestion, 0, 3); + } + etat = etat.next(); + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + leModele.setAcquisitionTime(index_Ingestion, DicomTools.getTag(laVue.imp, "0008,0032")); + leModele.setNomSerie(index_Ingestion, DicomTools.getTag(laVue.imp, "0008,103E")); + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + break; + case CIR_INTESTIN_ANT: + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please click on roi " + laVue.leRoi.getRoi(index_Roi - 2).getName() + " and adjust it!"); + laVue.leRoi.deselect(); + //si la Roi exite pas, on met la Roi precedente, sinon on met la Roi existante pour qu'on puisse le modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + else{ + laVue.leRoi.select(index_Roi); + } + break; + } else { + modifierRoi("Intestine_Ant", index_Ingestion, 0, 2); + } + getAntreFundus("Ant"); + if (!estAntreCorrect) { + estAntreCorrect = true; + break; + } + etat = etat.next(); + index_Instru--; + laVue.setInstructions(listeInstructions[index_Instru]); + demandeFinBDFAntre(); + demandeFinBDFIntestin(); + laVue.windowstack.showSlice((laVue.imp.getStackSize() / 2 - index_Ingestion) * 2 + 2); + laVue.imp.setSlice((laVue.imp.getStackSize() / 2 - index_Ingestion) * 2 + 2); + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + break; + case CIR_ESTOMAC_POS: + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please click on roi " + laVue.leRoi.getRoi(index_Roi - 2).getName() + " and adjust it!"); + laVue.leRoi.deselect(); + //si la Roi exite pas, on met la Roi precedente, sinon on met la Roi existante pour qu'on puisse le modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + else{ + laVue.leRoi.select(index_Roi); + } + break; + } else { + modifierRoi("Stomach_Pos", index_Ingestion, 1, 3); + } + etat = etat.next(); + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + break; + case CIR_INTESTIN_POS: + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please click on roi " + laVue.leRoi.getRoi(index_Roi - 2).getName() + " and adjust it! "); + laVue.leRoi.deselect(); + //si la Roi exite pas, on met la Roi precedente, sinon on met la Roi existante pour qu'on puisse le modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + else{ + laVue.leRoi.select(index_Roi); + } + break; + } else { + modifierRoi("Intestine_Pos", index_Ingestion, 1, 2); + } + getAntreFundus("Pos"); + + if (!estAntreCorrect) { + estAntreCorrect = true; + break; + } + // Si c'est la premiere image + if (index_Ingestion == 1) { + etat = etat.next(); + index_Instru++; + leModele.calculerTempsImages(); + } else { + // si'l est pas la derniere image, on va etre dans la boucle "CIR_ESTOMAC_ANT---CIR_INTESTIN_ANT---CIR_ESTOMAC_POS---CIR_INTESTIN_POS" + for (int i = 0; i < 3; i++) { + etat = etat.previous(); + } + index_Instru--; + index_Ingestion--; + laVue.windowstack.showSlice((laVue.imp.getStackSize() / 2 - index_Ingestion) * 2 + 1); + laVue.imp.setSlice((laVue.imp.getStackSize() / 2 - index_Ingestion) * 2 + 1); + + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + } + laVue.setInstructions(listeInstructions[index_Instru]); + + break; + + case OEUFOUVERT: + laVue.lesBoutons.get("Suivant").setEnabled(false); + //on calcule les resultats corriges par BF + leModele.corrigerParBDF(); + //on demande l'utilisateur de preciser si'l veut ouvrir l'image des oeufs + int reponse = JOptionPane.showConfirmDialog(laVue.windowstack, + "Would you like to open the image of eggs ?", "", JOptionPane.YES_NO_OPTION); + //tant que on repond pas + while(reponse!=0&&reponse!=1) + { + //par exemple si on ferme la dialogue + reponse = JOptionPane.showConfirmDialog(laVue.windowstack, + "Would you like to open the image of eggs ?", "", JOptionPane.YES_NO_OPTION); + } + if (reponse == 0) { + //si on reponds "YES", on met l'image des oeufs au fenetre principal pour compter les oeufs + laVue.ouvrirImage("Oeufs"); + laVue.overlay.clear(); + laVue.addOverlayGD(); + laVue.lesBoutons.get("Precedent").setEnabled(false); + etat = etat.next(); + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + } else if(reponse==1){ + //si on reponds "No", on passe a l'etat CORRIGER, et on tenir compte que tous les oeufs ont la m¨ºme taille pour corriger les resultats + etat = etat.next(); + index_Instru++; + etat = etat.next(); + index_Instru++; + etat = etat.next(); + index_Instru++; + laVue.lesBoutons.get("Precedent").setEnabled(false); + laVue.lesBoutons.get("Suivant").setEnabled(true); + laVue.setInstructions(listeInstructions[index_Instru]); + //On envoi le message "next" + ActionEvent e = new ActionEvent(laVue.lesBoutons.get("Suivant"),ActionEvent.ACTION_PERFORMED,"Suivant"); + this.actionPerformed(e); + } + break; + + case ROIDEFAULT: + // on met un ROI par default en gauche-haut de la fenetre principal + Rectangle r = new Rectangle(15, 15); + r.setLocation(10, 10); + laVue.leRoi.deselect(); + laVue.imp.setRoi(r); + addRoi("oeuf"); + etat = etat.next(); + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + break; + + case OEUFS: + //si aucunne ROI est selecte, on remet la ROI precedente pour qu'on puisse le modifie + if (laVue.imp.getRoi()==null){ + Roi roiTemp=(Roi) laVue.leRoi.getRoi(index_Roi-1).clone(); + roiTemp.setLocation(10, 10); + laVue.leRoi.deselect(); + laVue.imp.setRoi(roiTemp); + break; + } + modifierRoiOeuf(); + + //si le nombre de coups de tous les oeufs sont calcule + if (index_Oeuf > oeufsIngere) { + //on passe a l'etat suivant + etat = etat.next(); + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + //on calcule le % de chaque oeuf par rapport au repas total + leModele.calculerOeufsPourcent(); + //on demande l'utilisateur de verifier le % de chaque oeuf + laVue.confirmerOeufPourc(); + + } + break; + + case CORRIGER: + //corriger les resultats par les ouefs non ingeres + leModele.corrigerParOeuf(); + String resultat = leModele.getResultat(); + etat = etat.next(); + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + laVue.lesBoutons.get("Suivant").setEnabled(false); + Modele_Shunpo.exportRoiManager(laVue.leRoi, laVue.nomProgramme, laVue.windowstack.getImagePlus()); + laVue.windowstack.close(); + laVue.leRoi.close(); + IJ.runMacro("run(\"Gastric Emptying software\","+"\"" + resultat+ "\""+");"); + break; + default: + break; + } + } + //ESTOMAC_ANT, INTESTIN_ANT, ESTOMAC_POS, INTESTIN_POS, CIR_ESTOMAC_ANT, CIR_INTESTIN_ANT, CIR_ESTOMAC_POS, CIR_INTESTIN_POS, OEUFOUVERT, ROIDEFAULT, OEUFES, CORRIGER,FIN; + + if (b == laVue.lesBoutons.get("Precedent")){ + switch (etat) { + + case INTESTIN_ANT: + retour(); + laVue.lesBoutons.get("Precedent").setEnabled(false); + break; + + + case ESTOMAC_POS: + retour(); + laVue.windowstack.showSlice(1); + laVue.imp.setSlice(1); + //si la reponse etait "YES" a ce etat, on remet la reponse a "no", pour qu'on puisse redemander l'utilisateur + if (leModele.getFinBDFAntre() == index_Ingestion) { + reponseFinBDFAntre = 1; + } + if (leModele.getFinBDFIntestin() == index_Ingestion) { + reponseFinBDFIntestin = 1; + } + break; + + case INTESTIN_POS: + retour(); + break; + + case CIR_ESTOMAC_ANT: + retour(); + index_Instru++; + index_Instru++; + index_Ingestion++; + // si'l est pas la premiere image, on va etre dans la boucle "CIR_INTESTIN_POS---CIR_ESTOMAC_POS---CIR_INTESTIN_ANT---CIR_ESTOMAC_ANT" + if (index_Ingestion != laVue.imp.getStackSize() / 2) { + etat = etat.next(); + etat = etat.next(); + etat = etat.next(); + etat = etat.next(); + } + laVue.windowstack.showSlice((laVue.imp.getStackSize() / 2 - index_Ingestion) * 2 + 2); + laVue.imp.setSlice((laVue.imp.getStackSize() / 2 - index_Ingestion) * 2 + 2); + break; + + case CIR_INTESTIN_ANT: + retour(); + break; + + case CIR_ESTOMAC_POS: + retour(); + index_Instru++; + index_Instru++; + laVue.windowstack.showSlice((laVue.imp.getStackSize() / 2 - index_Ingestion) * 2 + 1); + laVue.imp.setSlice((laVue.imp.getStackSize() / 2 - index_Ingestion) * 2 + 1); + if (leModele.getFinBDFAntre() == index_Ingestion) { + reponseFinBDFAntre = 1; + } + if (leModele.getFinBDFIntestin() == index_Ingestion) { + reponseFinBDFIntestin = 1; + } + break; + + case CIR_INTESTIN_POS: + retour(); + break; + + case OEUFOUVERT: + retour(); + laVue.leRoi.deselect(); + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi).clone()); + laVue.lesBoutons.get("Suivant").setEnabled(true); + break; + + case ROIDEFAULT: + retour(); + break; + + case OEUFS: + retourOeuf(); + break; + + case CORRIGER: + etat = etat.previous(); + index_Instru--; + laVue.setInstructions(listeInstructions[index_Instru]); + retourOeuf(); + break; + + case FIN: + etat = etat.previous(); + index_Instru--; + laVue.setInstructions(listeInstructions[index_Instru]); + laVue.lesBoutons.get("Suivant").setEnabled(true); + break; + default: + break; + } + } + + if (b == laVue.lesBoutons.get("Draw ROI")) + IJ.setTool(Toolbar.POLYGON); + + if (b == laVue.lesBoutons.get("Contrast")) + IJ.run("Window Level Tool"); + + if (b == laVue.lesBoutons.get("Quitter")) { + laVue.end(); + return; + } + + if (b == laVue.lesBoutons.get("Show")) { + Modele_VG_Dynamique.logOn = !Modele_VG_Dynamique.logOn; + if (!Modele_VG_Dynamique.logOn) { + IJ.log("\\Close"); + laVue.lesBoutons.get("Show").setLabel("Show MG%"); + } else { + laVue.lesBoutons.get("Show").setLabel("Close MG%"); + } + } + + } + + private void addRoi(String nom) { + //si la ROI exist deja, on le update, sinon on ajoute une nouvelle ROI a ROI manager + if(laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.runCommand("Update"); + laVue.leRoi.runCommand("Remove Slice Info"); + } + else { + laVue.leRoi.add(laVue.imp, laVue.imp.getRoi(), index_Roi); + laVue.leRoi.runCommand("Remove Slice Info"); + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi); + laVue.leRoi.rename(index_Roi, nom); + } + + //en cas de ingestion, on update l'Overlay toujours, mais en cas de oeufs, on update l'Overlay juste la premire oeuf + if(index_Oeuf==1){ + //on update l'Overlay + laVue.overlay.clear(); + laVue.addOverlayGD(); + } + + if (nom.contains("Stomach")){ + laVue.overlay.add(laVue.leRoi.getRoi(index_Roi)); + } + if (nom.contains("Intestine")){ + laVue.overlay.add(laVue.leRoi.getRoi(index_Roi-1)); + } + + index_Roi++; + } + + // permet de revenir en arriere, le parametre i signifie le nombre de ROIs a revenir en arriere + private void retour() { + laVue.imp.killRoi(); + index_Roi--; + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi); + //On clear l'Overlay + laVue.overlay.clear(); + laVue.addOverlayGD(); + //On affiche l'overlay de la ROI n-1 si intestin ou n+1 si estomac + if (laVue.leRoi.getRoi(index_Roi).getName().contains("Intestine")){ + laVue.overlay.add(laVue.leRoi.getRoi(index_Roi-1)); + } + if (laVue.leRoi.getRoi(index_Roi).getName().contains("Stomach")){ + laVue.overlay.add(laVue.leRoi.getRoi(index_Roi+1)); + } + + index_Instru--; + laVue.setInstructions(listeInstructions[index_Instru]); + etat = etat.previous(); + } + + // permet de faire les instructions "AND" sur les ROIs + // "Estomache" et "Intestine" + // pour obtenir le ROI "Antre" et calculer ses coups + private void getAntreFundus(String cote) { + + laVue.leRoi.setSelectedIndexes(new int[] { index_Roi - 1, index_Roi - 2 }); + laVue.leRoi.runCommand("AND"); + laVue.leRoi.runCommand("Deselect"); + // si il n'y a pas de intersection, redemande a l'utilisateur de adjuster le ROI + if (WindowManager.getCurrentImage().getRoi()== null) { + //si la ROI de l'estomac de image suivante existe pas, on delete la ROI de l'intestin de l'image courant, et on remet une ROI precedente pour modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi-1); + laVue.leRoi.runCommand("Delete"); + laVue.overlay.add(laVue.leRoi.getRoi(index_Roi-2)); + //Si on n'est pas dans la 1ere image on remet la ROI intestin precedente + if (index_Roi > 2) { + laVue.leRoi.deselect(); + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 3).clone()); + } + }else{ + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi-1); + } + index_Roi--; + IJ.showMessage( + "please adjust the intestine So that there is an intersection between the estomac and the intestine !"); + estAntreCorrect = false; + } else { + + //pour la deuxieme dimension de tableau coupsBrut, j est 0: image ANT, j est 1: image POS + int j = 1; + if (cote == "Ant") { + j = 0; + } + + leModele.calculerCoupsBrut(index_Ingestion, j, 1, laVue.imp); + laVue.imp.killRoi(); + leModele.setCoupsBrut(index_Ingestion, j, 0, + leModele.getCoupsBrut(index_Ingestion, j, 3) - leModele.getCoupsBrut(index_Ingestion, j, 1)); + leModele.setCoupsBrut(index_Ingestion, j, 2, + leModele.getCoupsBrut(index_Ingestion, j, 2) - leModele.getCoupsBrut(index_Ingestion, j, 1)); + leModele.setCoupsBrut(index_Ingestion, j, 4, + leModele.getCoupsBrut(index_Ingestion, j, 2) + leModele.getCoupsBrut(index_Ingestion, j, 3)); + + } + + } + + // permet de modifier le Roi + private void modifierRoi(String roiNouvNom, int i, int j, int k) { + addRoi(roiNouvNom + index_Ingestion); + leModele.calculerCoupsBrut(i, j, k, laVue.imp); + laVue.imp.killRoi(); + } + + // permet de modifier le Roi d'oeuf + private void modifierRoiOeuf() { + addRoi(""+index_Oeuf); + leModele.calculerCoupsOeufs(index_Oeuf, laVue.imp); + if (index_Oeuf == 1) { + laVue.lesBoutons.get("Precedent").setEnabled(true); + } + index_Oeuf++; + //Voir probleme d'overlay eviter de la vider dans add ROI pour les oeufs... + laVue.overlay.add(laVue.leRoi.getRoi(index_Roi - 1)); + laVue.leRoi.deselect(); + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 1).clone()); + laVue.imp.getRoi().setLocation(10, 10); + } + + // permet de revenir en arriere pour modifier le Roi d'oeuf + private void retourOeuf() { + index_Oeuf--; + index_Roi--; + //on supprime l'overlay et la ROI qu'on a ajoute a ce etat + laVue.overlay.remove(laVue.leRoi.getRoi(index_Roi)); + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi); + laVue.leRoi.runCommand("Delete"); + if (index_Oeuf == 1) { + laVue.lesBoutons.get("Precedent").setEnabled(false); + etat = etat.previous(); + index_Instru--; + laVue.setInstructions(listeInstructions[index_Instru]); + }else{ + //Voir probleme d'overlay eviter de la vider dans add ROI pour les oeufs... + laVue.leRoi.deselect(); + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 1).clone()); + laVue.imp.getRoi().setLocation(10, 10); + } + } + + //permet de demander a l'utilisateur de preciser sur la serie courant si la region de l'antre est bruit de fond + private void demandeFinBDFAntre() { + if (reponseFinBDFAntre == 1) { + reponseFinBDFAntre = JOptionPane.showConfirmDialog(laVue.windowstack, "Antrum region is background noise ?", + "", JOptionPane.YES_NO_OPTION); + if (reponseFinBDFAntre == 0) { + leModele.setFinBDFAntre(index_Ingestion); + } + + } + } + //permet de demander a l'utilisateur de preciser sur la serie courant si la region de l'intestin est bruit de fond + private void demandeFinBDFIntestin() { + if (reponseFinBDFIntestin == 1) { + reponseFinBDFIntestin = JOptionPane.showConfirmDialog(laVue.windowstack, + "Intestine region is background noise ?", "", JOptionPane.YES_NO_OPTION); + if (reponseFinBDFIntestin == 0) { + leModele.setFinBDFIntestin(index_Ingestion); + } + + } + } +} diff --git a/Controleur_VG_Roi.java b/Controleur_VG_Roi.java new file mode 100644 index 00000000..bbff7ba6 --- /dev/null +++ b/Controleur_VG_Roi.java @@ -0,0 +1,653 @@ +/** +Copyright (C) 2017 PING Xie and KANOUN Salim +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public v.3 License as published by +the Free Software Foundation; +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.FileNotFoundException; +import java.text.ParseException; +import ij.IJ; +import ij.ImagePlus; +import ij.ImageStack; +import ij.WindowManager; +import ij.gui.ImageWindow; +import ij.gui.Roi; +import ij.gui.Toolbar; +import ij.plugin.CanvasResizer; +import ij.process.ImageProcessor; +import ij.util.DicomTools; + +public class Controleur_VG_Roi implements ActionListener { + + private Vue_VG_Roi laVue; + + private Modele_VG_Roi.Etat etat; + + private Modele_VG_Roi leModele; + + private String[] listeInstructions = { "Delimit the stomac", "Delimit the intestine", "Adjuste the stomac", + "Adjuste the intestine and to next image", "Next to display results", "Fin" }; + + private int index_Roi;// index des Rois + + private int index_Instru;// index des instructions + + private int index_Image;// index des images + + private String acquisitionTime; + + private String[] resultats; + + private boolean estAntreCorrect;//signifie si'l y a intersection entre la ROI de l'estomac et de l'intestin pour fabriquer la ROi de l'antre + + private String tagCaptureFinale; + + private ImagePlus[] capture = new ImagePlus[4]; + + public Controleur_VG_Roi(Vue_VG_Roi vue, Modele_VG_Roi leModele) { + index_Roi = 0; + index_Instru = 0; + laVue = vue; + this.leModele = leModele; + etat = Modele_VG_Roi.Etat.ESTOMAC_ANT; + index_Image = 1; + acquisitionTime = ""; + estAntreCorrect = true; + } + + @Override + public void actionPerformed(ActionEvent arg0) { + Button b = (Button) arg0.getSource(); + if (b == laVue.lesBoutons.get("Suivant")) { + switch (etat) { + + case ESTOMAC_ANT: + // si le ROI n'est pas present on demande a l'utilisateur de le faire + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please delimite the stomach!"); + //si le Roi exist deja on affiche le ROI existante pour que l'utilisateur puisse la modifier + if(laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi); + } + break; + } else { + addRoi("Stomach_Ant" + index_Image); + } + + leModele.calculerCoups("Estomac_Ant", index_Image, laVue.imp); + laVue.imp.deleteRoi(); + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + etat = etat.next(); + laVue.lesBoutons.get("Precedent").setEnabled(true); + leModele.initResultat(laVue.imp); + + String tagNom = DicomTools.getTag(laVue.imp, "0010,0010").substring(1); + leModele.setPatient(tagNom, laVue.imp); + //On genere la 1ere partie du Header qui servira a la capture finale + tagCaptureFinale=Modele_Shunpo.genererDicomTagsPartie1(laVue.imp, laVue.nomProgramme); + // on regarde si la ROI suivante est deja dans le ROI manager, si oui on l'affiche + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi); + } + break; + + case INTESTIN_ANT: + // si le ROI n'est pas delimite, demande a l'utilisateur de + // delimiter le ROI + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please delimite the intestine!"); + //si le Roi exist deja on affiche le ROI existante pour que l'utilisateur puisse la modifier + if(laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi); + } + break; + } else { + addRoi("Intestine_Ant" + index_Image); + } + //on fait le calcule + leModele.calculerCoups("Intes_Ant", index_Image,laVue.imp); + laVue.imp.deleteRoi(); + //On genere la roiAntre + getAntreFundus("Ant"); + //si'l y a pas de intersection entre la ROI de l'estomac et de l'intestin pour fabriquer la ROi de l'antre + //on refait la ROI de l'intestin + if (!estAntreCorrect) { + estAntreCorrect = true; + break; + } + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + etat = etat.next(); + laVue.windowstack.showSlice(2); + //On regarde si la ROI suivante est presente et sinon on met la ROI n-2 + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + break; + + case ESTOMAC_POS: + // si le ROI n'est pas Clique, demande a l'utilisateur de + // cliquer et adjust le ROI + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please click on roi " + laVue.leRoi.getRoi(index_Roi - 2).getName() + " and adjust it!"); + laVue.leRoi.deselect(); + //si la Roi exite pas, on met la Roi precedente, sinon on met la Roi existante pour qu'on puisse le modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + else{ + laVue.leRoi.select(index_Roi); + } + break; + } else { + modifierRoi("Stomach_Pos", "Estomac_Pos"); + } + + etat = etat.next(); + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + //On regarde si la ROI suivante est presente et sinon on met la ROI n-2 + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + }break; + + + case INTESTIN_POS: + // si le ROI n'est pas Clique, demande a l'utilisateur de + // cliquer et adjust le ROI + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please click on roi " + laVue.leRoi.getRoi(index_Roi - 2).getName() + " and adjust it!"); + laVue.leRoi.deselect(); + //si la Roi exite pas, on met la Roi precedente, sinon on met la Roi existante pour qu'on puisse le modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + else{ + + laVue.leRoi.select(index_Roi); + } + break; + } else { + modifierRoi("Intestine_Pos", "Intes_Pos"); + } + getAntreFundus("Pos"); + if (!estAntreCorrect) { + estAntreCorrect = true; + break; + } + //On appelle la methode de SunPo pour capture l'image (sans l'interface) + ImagePlus captureTemp=Modele_Shunpo.captureImage(laVue.imp, 512, 512); + //On met dans un canvas de meme taille que les courbes et on stock dans le tableau d'imagePlus capture + ImageProcessor ip=captureTemp.getProcessor(); + CanvasResizer canvas = new CanvasResizer(); + ImageProcessor iptemp=canvas.expandImage(ip, 640, 512, (640-512)/2, 0); + captureTemp.setProcessor(iptemp); + capture[0]=captureTemp; + acquisitionTime = DicomTools.getTag(laVue.windowstack.getImagePlus(), "0008,0032"); + try { + leModele.tempsImage(index_Image, acquisitionTime); + } catch (ParseException e) { + e.printStackTrace(); + } + leModele.pourcVGImage(index_Image); + etat = etat.next(); + index_Instru--; + laVue.setInstructions(listeInstructions[index_Instru]); + laVue.windowstack.showSlice(index_Image * 2 + 1); + ///On regarde si la ROI suivante est presente et sinon on met la ROI n-2 + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + index_Image++; + break; + + + case CIR_ESTOMAC_ANT: + // si le ROI n'est pas Clique, demande a l'utilisateur de + // cliquer et adjust le ROI + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please click on roi " + laVue.leRoi.getRoi(index_Roi - 2).getName() + " and adjust it!"); + laVue.leRoi.deselect(); + //si la Roi exite pas, on met la Roi precedente, sinon on met la Roi existante pour qu'on puisse le modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + else{ + laVue.leRoi.select(index_Roi); + } + break; + } else { + modifierRoi("Stomach_Ant", "Estomac_Ant"); + } + etat = etat.next(); + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + //On regarde si la ROI suivante est presente et sinon on met la ROI n-2 + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + break; + + + case CIR_INTESTIN_ANT: + // si le ROI n'est pas Clique, demande a l'utilisateur de + // cliquer et adjust le ROI + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please click on roi " + laVue.leRoi.getRoi(index_Roi - 2).getName() + " and adjust it!"); + laVue.leRoi.deselect(); + //si la Roi exite pas, on met la Roi precedente, sinon on met la Roi existante pour qu'on puisse le modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + else{ + laVue.leRoi.select(index_Roi); + } + break; + } else { + modifierRoi("Intestine_Ant", "Intes_Ant"); + } + getAntreFundus("Ant"); + if (!estAntreCorrect) { + estAntreCorrect = true; + break; + } + etat = etat.next(); + index_Instru--; + laVue.setInstructions(listeInstructions[index_Instru]); + laVue.windowstack.showSlice((index_Image * 2 )); + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + break; + + + case CIR_ESTOMAC_POS: + // si le ROI n'est pas Clique, demande a l'utilisateur de + // cliquer et adjust le ROI + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please click on roi " + laVue.leRoi.getRoi(index_Roi - 2).getName() + " and adjust it!"); + laVue.leRoi.deselect(); + //si la Roi exite pas, on met la Roi precedente, sinon on met la Roi existante pour qu'on puisse le modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + else{ + laVue.leRoi.select(index_Roi); + } + break; + } else { + modifierRoi("Stomach_Pos", "Estomac_Pos"); + } + etat = etat.next(); + index_Instru++; + laVue.setInstructions(listeInstructions[index_Instru]); + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + break; + + + case CIR_INTESTIN_POS: + // si le ROI n'est pas Clique, demande a l'utilisateur de + // cliquer et adjust le ROI + if (laVue.imp.getRoi() == null) { + IJ.showMessage("please click on roi " + laVue.leRoi.getRoi(index_Roi - 2).getName() + " and adjust it! "); + laVue.leRoi.deselect(); + //si la Roi exite pas, on met la Roi precedente, sinon on met la Roi existante pour qu'on puisse le modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + else{ + laVue.leRoi.select(index_Roi); + } + break; + } else { + modifierRoi("Intestine_Pos", "Intes_Pos"); + } + getAntreFundus("Pos"); + if (!estAntreCorrect) { + estAntreCorrect = true; + break; + } + acquisitionTime = DicomTools.getTag(laVue.imp, "0008,0032"); + try { + leModele.tempsImage(index_Image, acquisitionTime); + } catch (ParseException e) { + e.printStackTrace(); + } + leModele.pourcVGImage(index_Image); + if (index_Image == laVue.imp.getStackSize()/2) { + etat = etat.next(); + index_Instru++; + + } else { + // si'l est pas la derniere image, on va etre dans la boucle "CIR_ESTOMAC_ANT---CIR_INTESTIN_ANT---CIR_ESTOMAC_POS---CIR_INTESTIN_POS" + for (int i = 0; i < 3; i++) { + etat = etat.previous(); + + } + index_Instru--; + laVue.windowstack.showSlice((index_Image) * 2 + 1); + laVue.leRoi.deselect(); + if (laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.select(index_Roi); + } + else { + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 2).clone()); + } + index_Image++; + } + laVue.setInstructions(listeInstructions[index_Instru]); + break; + + + case FIN: + // on obtient les resultats et cree ue tableau et un panel et + // des graphiques pour afficher les resultats + resultats = leModele.resultats(laVue.imp); + if (Modele_VG_Roi.logOn) { + for(int i=0; i 1) { + for (int i = 1; i < index_Image; i++) { + IJ.log("image " + (i) + ": " + " Stomach " + Modele_VG_Roi.estomacPourcent[i] + " Intestine " + + Modele_VG_Roi.intestinPourcent[i] + " Fundus " + Modele_VG_Roi.fundusPourcent[i] + + " Antre " + Modele_VG_Roi.antrePourcent[i]); + } + } + laVue.lesBoutons.get("Show").setLabel("Close MG%"); + } + } + + } + + private void addRoi(String nom) { + //si la ROI exist deja, on le update, sinon on ajoute une nouvelle ROI a ROI manager + if(laVue.leRoi.getRoi(index_Roi)!=null){ + laVue.leRoi.runCommand("Update"); + laVue.leRoi.runCommand("Remove Slice Info"); + } + else { + laVue.leRoi.add(laVue.imp, laVue.imp.getRoi(), index_Roi); + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi); + laVue.leRoi.rename(index_Roi, nom); + laVue.leRoi.deselect(); + } + laVue.overlay.clear(); + laVue.addOverlayGD(); + + if (nom.contains("Stomach")){ + laVue.overlay.add(laVue.leRoi.getRoi(index_Roi)); + } + if (nom.contains("Intestine")){ + laVue.overlay.add(laVue.leRoi.getRoi(index_Roi-1)); + } + + index_Roi++; + } + + // permet de revenir en arriere + private void retour() { + index_Roi--; + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi); + //On clear l'Overlay + laVue.overlay.clear(); + laVue.addOverlayGD(); + //On affiche l'overlay de la ROI n-1 si intestin ou n+1 si estomac + if (laVue.leRoi.getRoi(index_Roi).getName().contains("Intestine")){ + laVue.overlay.add(laVue.leRoi.getRoi(index_Roi-1)); + } + if (laVue.leRoi.getRoi(index_Roi).getName().contains("Stomach")){ + laVue.overlay.add(laVue.leRoi.getRoi(index_Roi+1)); + } + + index_Instru--; + laVue.setInstructions(listeInstructions[index_Instru]); + etat = etat.previous(); + } + + // permet de faire les instructions "AND" sur les ROIs + // "Estomache" et "Intestine" + // pour obtenir le ROI "Antre" et calculer ses coups + private void getAntreFundus(String cote) { + + laVue.leRoi.setSelectedIndexes(new int[] { index_Roi - 1, index_Roi - 2 }); + laVue.leRoi.runCommand("AND"); + laVue.leRoi.runCommand("Deselect"); + // si il n'y a pas de intersection, redemande a l'utilisateur de adjuster le ROI + if (WindowManager.getCurrentImage().getRoi()== null) { + //si la ROI de l'estomac de image suivante existe pas, on delete la ROI de l'intestin de l'image courant, et on remet une ROI precedente pour modifier + if(laVue.leRoi.getRoi(index_Roi)==null){ + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi-1); + laVue.leRoi.runCommand("Delete"); + laVue.overlay.add(laVue.leRoi.getRoi(index_Roi-2)); + //Si on n'est pas dans la 1ere image on remet la ROI intestin precedente + if (index_Roi > 2) { + laVue.leRoi.deselect(); + laVue.imp.setRoi((Roi) laVue.leRoi.getRoi(index_Roi - 3).clone()); + } + }else{ + laVue.leRoi.deselect(); + laVue.leRoi.select(index_Roi-1); + } + index_Roi--; + IJ.showMessage( + "please adjust the intestine So that there is an intersection between the estomac and the intestine !"); + estAntreCorrect = false; + } else { + leModele.calculerCoups("Antre_" + cote, index_Image,laVue.imp); + laVue.windowstack.getImagePlus().deleteRoi(); + leModele.setCoups("Fundus_" + cote, index_Image, + leModele.getCoups("Estomac_" + cote, index_Image) + - leModele.getCoups("Antre_" + cote, index_Image)); + leModele.setCoups("Intestin_" + cote, index_Image, + leModele.getCoups("Intes_" + cote, index_Image) + - leModele.getCoups("Antre_" + cote, index_Image)); + } + + } + + // permet de modifier la ROI + private void modifierRoi(String roiNouvNom, String coupNouveNom) { + addRoi(roiNouvNom + index_Image); + leModele.calculerCoups(coupNouveNom, index_Image, laVue.imp); + laVue.imp.deleteRoi(); + } + +} diff --git a/Modele_Shunpo.java b/Modele_Shunpo.java new file mode 100644 index 00000000..8430b242 --- /dev/null +++ b/Modele_Shunpo.java @@ -0,0 +1,629 @@ +/** +Copyright (C) 2017 MOHAND Mathis and KANOUN Salim +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public v.3 License as published by +the Free Software Foundation; +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +import java.awt.AWTException; + +import ij.Prefs; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.Map.Entry; + +import ij.IJ; +import ij.ImagePlus; +import ij.ImageStack; +import ij.gui.ImageCanvas; +import ij.gui.ImageWindow; +import ij.gui.Roi; +//import ij.measure.Measurements; +//import ij.measure.ResultsTable; +import ij.plugin.MontageMaker; +//import ij.plugin.filter.Analyzer; +//import ij.plugin.filter.ParticleAnalyzer; +import ij.plugin.frame.RoiManager; +import ij.process.ImageProcessor; +import ij.process.ImageStatistics; +import ij.util.DicomTools; + +public class Modele_Shunpo { + + private HashMap coups; + private HashMap mgs; + private String[] abvsMG = { "PD", "PG", "RD", "RG", "C" }; + private String patient; + private String date; + private String dateForm; + private int pixrdp; + private int pixrgp; + private int pixrda; + private int pixrga; + protected String[] retour; + protected static double shunt; + + public Modele_Shunpo() { + coups = new HashMap<>(); + mgs = new HashMap<>(); + Prefs.useNamesAsLabels = true; + } + + protected static enum Etat { + PoumonD_Post, PoumonG_Post, ReinD_Post, ReinG_Post, BDF, PoumonD_Ant, PoumonG_Ant, ReinD_Ant, ReinG_Ant, Poumon_valide, Cerveau_Post, Cerveau_Ant, Fin; + private static Etat[] vals = values(); + + public Etat next() { + return vals[(this.ordinal() + 1) % vals.length]; + } + + public Etat previous() { + // On ajoute un vals.length car le modulo peut ¨ºtre < 0 en java + return vals[((this.ordinal() - 1) + vals.length) % vals.length]; + } + } + + // On recupere le nom du patient, la date et son id pour les resultats + protected void setPatient(String pat, ImagePlus imp) { + patient = pat; + date = DicomTools.getTag(imp, "0008,0020"); + char[] a = date.toCharArray(); + date = date.trim(); + dateForm = "" + a[7] + a[8] + "/" + a[5] + a[6] + "/" + a[1] + a[2] + a[3] + a[4]; + } + + /** + * Permet de creer un stack a partir d'un tableau d'ImagePlus + * @param tableauImagePlus : Tableau contenant les ImagePlus a mettre dans le stack (toutes les images doivent avoir la même taille) + * @return Renvoie le stack d'image produit + */ + public static ImageStack captureToStack(ImagePlus[] tableauImagePlus) { + // On verifie que toutes les images ont la meme taille + int[][] dimensionCapture = new int[4][2]; + for (int i = 0; i < tableauImagePlus.length; i++) { + + dimensionCapture[i] = tableauImagePlus[i].getDimensions(); + if (!Arrays.equals(dimensionCapture[i], tableauImagePlus[0].getDimensions())) { + IJ.showMessage("Error Capture have different dimension"); + } + } + // On cree de stack de taille adhoc + ImageStack stackCapture = new ImageStack(dimensionCapture[0][0], dimensionCapture[0][1]); + // On rajoute les images dans le stack + for (int i = 0; i < tableauImagePlus.length; i++) { + + stackCapture.addSlice(tableauImagePlus[i].getProcessor()); + } + // On retourne le stack de Capture + return stackCapture; + } + + // Cree le montage a partir de l'ImageStack + protected ImagePlus montage(ImageStack stackCapture, String nomProgramme) { + MontageMaker mm = new MontageMaker(); + ImagePlus imp = new ImagePlus("Resultats ShunPo -" + patient, stackCapture); + imp = mm.makeMontage2(imp, 2, 2, 0.50, 1, 4, 1, 10, false); + imp.setTitle("Resultats " + nomProgramme + " -" + patient); + return imp; + } + + /** + * Capture secondaire de l'image sans l'interface et la redimmensionner à la taille voulue + * @param imp : ImagePlus a capturer + * @param largeur : largeur de la capture finale (si hauteur et largeur = 0 : pas de redimensionnement) + * @param hauteur : hauteur de la capture finale (si hauteur =0 on ne redimensionne que la largeur en gardant le ratio) + * @return Renvoie l'ImagePlus contenant la capture secondaire + */ + public static ImagePlus captureImage(ImagePlus imp, int largeur, int hauteur) { + // Cette methode capture la partie image seule d'une fenetre + ImageWindow win = imp.getWindow(); + win.toFront(); + IJ.wait(500); + Point loc = win.getLocation(); + ImageCanvas ic = win.getCanvas(); + Rectangle bounds = ic.getBounds(); + loc.y += bounds.y; + loc.x += bounds.x; + BufferedImage buff = null; + // efface le zoom indicateur : carre bleu en haut a gauche quand zoom inf a 1 + boolean wasHidden = ic.hideZoomIndicator(true); + ic.repaint(); + try { + Rectangle r = new Rectangle(loc.x, loc.y, bounds.width, bounds.height); + buff = new Robot().createScreenCapture(r); + + } catch (AWTException e) { + } + ic.hideZoomIndicator(wasHidden); + // On resize la capture aux dimensions choisies pour rentrer dans le + // stack + ImagePlus imp2 = new ImagePlus("Capture", buff); + ImageProcessor ip = imp2.getProcessor(); + ip.setInterpolate(true); + ip.setInterpolationMethod(ImageProcessor.BICUBIC); + ImageProcessor ip2; + if (hauteur == 0 && largeur !=0) { + ip2 = ip.resize(largeur); + } + else if (hauteur == 0 && largeur==0) { + ip2 = ip; + } + else { + ip2 = ip.resize(largeur, hauteur, true); + } + imp2.setProcessor(ip2); + // On renvoie l'ImagePlus contenant la capture + return imp2; + } + + + /** + * Permet de capturer la fenetre entiere et de choisir la taille de l'image finale + * @param imp : l'ImagePlus de la fenetre à capturer + * @param largeur : largeur de l'image finale si largeur et hauteur =0 pas de resize on a la meme resolution que l'ecran + * @param hauteur : hauteur de l'image finale si hauteur =0 on ne resize que la largeur en gardant le même ratio + * @return Resultat de la capture dans une ImagePlus + */ + public static ImagePlus captureFenetre(ImagePlus imp, int largeur, int hauteur) { + ImageWindow win = imp.getWindow(); + Point loc = win.getLocation(); + Rectangle bounds = win.getBounds(); + bounds.height -= 66; + bounds.width -= 20; + loc.y += 58; + loc.x += 9; + win.toFront(); + ImageCanvas ic = win.getCanvas(); + boolean wasHidden = ic.hideZoomIndicator(true); + IJ.wait(500); + BufferedImage buff = null; + try { + Rectangle rec = new Rectangle(loc.x, loc.y, bounds.width, bounds.height); + buff = new Robot().createScreenCapture(rec); + } catch (AWTException e) { + } + ic.hideZoomIndicator(wasHidden); + // On ferme le ROI manage et fenetre resultat + ImagePlus imp2 = new ImagePlus("Results Capture", buff); + ImageProcessor ip = imp2.getProcessor(); + ip.setInterpolate(true); + ip.setInterpolationMethod(ImageProcessor.BICUBIC); + // Si hauteur=0 on resize que la largeur (et le ratio est maintenu) + // sinon on resize largeur et hauteur (et peut changer le ratio) + ImageProcessor ip2 = null; + if (hauteur == 0 && largeur!=0) { + ip2 = ip.resize(largeur); + } + else if (hauteur==0 && largeur ==0){ + ip2=ip; + } + else { + ip2 = ip.resize(largeur, hauteur, true); + } + imp2.setProcessor(ip2); + return imp2; + } + + protected void calculerCoups(String roi, ImagePlus imp) { + ImageStatistics is = imp.getStatistics(); + // int measurements=Measurements.INTEGRATED_DENSITY; + // int options=ParticleAnalyzer.SHOW_NONE; + // ResultsTable rt=new ResultsTable(); + // ParticleAnalyzer pa=new + // ParticleAnalyzer(measurements,options,rt,0,1000000); + // pa.analyze(imp); + // double coups2=rt.getValue("INTEGRATED_DENSITY", 0); + // IJ.log("ResultTable"+String.valueOf(coups2)); + if (roi.contains("BDF")) + coups.put(roi, is.mean); + else { + if (roi.contains("R")) { + if (roi.equals("RDP")) + pixrdp = is.pixelCount; + if (roi.equals("RGP")) + pixrgp = is.pixelCount; + if (roi.equals("RDA")) + pixrda = is.pixelCount; + if (roi.equals("RGA")) + pixrga = is.pixelCount; + } + coups.put(roi, is.pixelCount * is.mean); + } + if (Controleur_Shunpo.showLog) { + IJ.log(roi + "coups= " + String.valueOf(is.pixelCount * is.mean)); + } + } + + protected double getCoups(String roi) { + return coups.get(roi); + } + + private String convertAbrev(String abv) { + char[] decomp = abv.toCharArray(); + String result = ""; + for (int i = 0; i < decomp.length; i++) { + switch (decomp[i]) { + case 'P': + result += "Poumon "; + break; + case 'R': + result += "Rein "; + break; + case 'C': + result += "Cerveau "; + break; + case 'M': + result += "MG "; + i++; + break; + case 'D': + result += "Droite "; + break; + case 'G': + result += "Gauche "; + break; + } + if (i == decomp.length - 1) + result += ": "; + } + return result; + } + + private void mgs() { + for (String abv : abvsMG) + moyenneGeo(abv); + } + + // Calcule la moyenne g茅om茅trique pour un organe sp茅cifique + // Si abv = PD alors on calculera la MG pour le poumon droit + private void moyenneGeo(String abv) { + double[] coupsa = new double[2]; + String[] asuppr = new String[2]; + int index = 0; + for (Entry entry : coups.entrySet()) { + if (entry.getKey().contains(abv)) { + coupsa[index] = entry.getValue(); + asuppr[index] = entry.getKey(); + index++; + } + } + for (String so : asuppr) + coups.remove(so); + mgs.put("MG" + abv, moyenneGeometrique(coupsa)); + } + + // Retrait du BDF aux reins + private void coupsReins() { + double rdp = coups.get("RDP"); + double rgp = coups.get("RGP"); + double bdfp = coups.get("BDFP"); + coups.put("RDP", rdp - (bdfp * pixrdp)); + coups.put("RGP", rgp - (bdfp * pixrgp)); + double rda = coups.get("RDA"); + double rga = coups.get("RGA"); + double bdfa = coups.get("BDFA"); + coups.put("RDA", rda - (bdfa * pixrda)); + coups.put("RGA", rga - (bdfa * pixrga)); + } + + // Calcule la moyenne g茅om茅trique des nombres en param猫tre + private int moyenneGeometrique(double[] vals) { + double result = 1.0; + for (int i = 0; i < vals.length; i++) { + result *= vals[i]; + } + result = Math.sqrt(result); + return (int) result; + } + + protected String[] resultats() { + retour = new String[9]; + coupsReins(); + int index = 0; + // Les 5 MGs + mgs(); + for (Entry entry : mgs.entrySet()) { + retour[index] = convertAbrev(entry.getKey()) + entry.getValue(); + index++; + } + // Permet de definir le nombre de chiffre apr猫s la virgule et mettre la + // virgue en system US avec un . + DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(); + sym.setDecimalSeparator('.'); + DecimalFormat us = new DecimalFormat("##.##"); + us.setDecimalFormatSymbols(sym); + // Calculs + double percPD = (mgs.get("MGPD") / (1.0 * mgs.get("MGPD") + mgs.get("MGPG"))) * 100; + retour[3] += " (" + us.format(percPD) + "%)"; + double percPG = (mgs.get("MGPG") / (1.0 * mgs.get("MGPD") + mgs.get("MGPG"))) * 100; + retour[0] += " (" + us.format(percPG) + "%)"; + int totmg = mgs.get("MGPD") + mgs.get("MGPG"); + retour[index] = "Total MG : " + totmg; + index++; + int totshunt = mgs.get("MGRD") + mgs.get("MGRG") + mgs.get("MGC"); + retour[index] = "Total Shunt : " + totshunt; + index++; + double percSyst = (100.0 * totshunt) / totmg; + retour[index] = "% Systemic : " + us.format(percSyst) + "%"; + index++; + Modele_Shunpo.shunt = ((totshunt * 100.0) / (totmg * 0.38)); + retour[index] = "Pulmonary Shunt : " + us.format(Modele_Shunpo.shunt) + "% (total blood Flow)"; + + String[] clone = new String[10]; + clone[0] = retour[3]; + clone[1] = retour[5]; + clone[2] = retour[0]; + clone[3] = retour[6]; + clone[4] = retour[2]; + clone[5] = retour[7]; + clone[6] = retour[1]; + clone[7] = retour[8]; + clone[8] = retour[4]; + clone[9] = patient + " " + dateForm; + return clone; + } + + /** + * Permet de generer la 1ere partie du Header qui servira a la capture finale + * @param imp : imageplus originale (pour recuperer des elements du Header tels que le nom du patient...) + * @param nomProgramme : nom du programme qui l'utilise si par exemple "pulmonary shunt" la capture sera appelee "Capture Pulmonary Shunt" + * @return retourne la première partie du header en string auquelle on ajoutera la 2eme partie via la deuxieme methode + */ + public static String genererDicomTagsPartie1(ImagePlus imp, String nomProgramme) { + String sopID = BrownFat.generateSOPInstanceUID(new Date()); + String uid = myWriteDicom.generateUID6digits(); + String tag = "0002,0002 Media Storage SOP Class UID: " + "1.2.840.10008.5.1.4.1.1.7" + "\n" + + "0002,0003 Media Storage SOP Inst UID: " + sopID + "\n" + + "0002,0010 Transfer Syntax UID: " + "1.2.840.10008.1.2.1" + "\n" + + "0002,0013 Implementation Version Name: jpeg" + "\n" + + "0002,0016 Source Application Entity Title: " + "\n" + + "0008,0008 Image Type: DERIVED\\SECONDARY " + "\n" + + "0008,0016 SOP Class UID: " + "1.2.840.10008.5.1.4.1.1.7" + "\n" + + "0008,0018 SOP Instance UID: " + sopID + "\n" + + "0008,0020 Study Date:" + DicomTools.getTag(imp, "0008,0020") + "\n" + + "0008,0021 Series Date:" + DicomTools.getTag(imp, "0008,0021") + "\n" + + "0008,0030 Study Time:" + DicomTools.getTag(imp, "0008,0030") + "\n" + + "0008,0031 Series Time:" + DicomTools.getTag(imp, "0008,0031") + "\n" + + "0008,0050 Accession Number:" + DicomTools.getTag(imp, "0008,0050") + "\n" + + "0008,0060 Modality:" + DicomTools.getTag(imp, "0008,0060") + "\n" + + "0008,0064 Conversion Type: WSD" + "\n" + + "0008,0070 Manufacturer:" + DicomTools.getTag(imp, "0008,0070") + "\n" + + "0008,0080 Institution Name:" + DicomTools.getTag(imp, "0008,0080") + "\n" + + "0008,0090 Referring Physician's Name:" + DicomTools.getTag(imp, "0008,0090") + "\n" + + "0008,1030 Study Description:" + DicomTools.getTag(imp, "0008,1030") + "\n" + + "0008,103E Series Description: Capture " + nomProgramme + "\n" + + "0010,0010 Patient's Name:" + DicomTools.getTag(imp, "0010,0010") + "\n" + + "0010,0020 Patient ID:" + DicomTools.getTag(imp, "0010,0020") + "\n" + + "0010,0030 Patient's Birth Date:" + DicomTools.getTag(imp, "0010,0030") + "\n" + + "0010,0040 Patient's Sex:" + DicomTools.getTag(imp, "0010,0040") + "\n" + + "0020,000D Study Instance UID:" + DicomTools.getTag(imp, "0020,000D") + "\n" + + "0020,000E Series Instance UID:" + DicomTools.getTag(imp, "0020,000E").substring(0, DicomTools.getTag(imp, "0020,000E").length() - 6) + uid + "\n" + + "0020,0010 Study ID :" + DicomTools.getTag(imp, "0020,0010") + "\n" + + "0020,0011 Series Number: 1337" + "\n" + "0020,0013 Instance Number: 1" + "\n" + + "0020,0032 Image Position (Patient):" + DicomTools.getTag(imp, "0020,0032") + "\n" + + "0020,0037 Image Orientation (Patient):" + DicomTools.getTag(imp, "0020,0037") + "\n" + + "0028,0002 Samples per Pixel: 3" + "\n" + "0028,0004 Photometric Interpretation: RGB" + "\n" + + "0028,0006 Planar Configuration: 0" + "\n" + "0028,0008 Number of Frames: 1 \n"; + return tag; + } + /** + * Permet d'obtenir la 2ème partie du header qu'il faudra ajouter à la 1ere partie + * @param CaptureFinale : L'ImagePlus de la capture secondaire (permet de récuperer le nombre de ligne et de colonne qui doit apparait dans le header DICOM) + * @return retourne la 2eme partie du tag qu'il faut ajouter à la 1ere partie (tag1+=tag2) + */ + public static String genererDicomTagsPartie2(ImagePlus CaptureFinale) { + String tag = "0028,0010 Rows: " + CaptureFinale.getHeight() + "\n" + + "0028,0011 Columns: " + CaptureFinale.getWidth() + "\n" + + "0028,0100 Bits Allocated: 8" + "\n" + "0028,0101 Bits Stored: 8" + "\n" + + "0028,0102 High Bit: 7" + "\n" + + "0028,0103 Pixel Representation: 0 \n"; + return tag; + } + + protected String[] buildCSVResultats() { + + String[] res2 = retour.clone(); + String[] res3 = new String[(res2.length )*2]; + for (int i = 0, j=0; i < res2.length ; i++,j++) { + res3[j] = res2[i].split(":")[0]; + res3[j] = res3[j].trim(); + j++; + res3[j] = res2[i].split(":")[1]; + res3[j] = res3[j].trim(); + } + return res3; + } + + // Permet la sauvegarde finale a partir du string builder contenant le + // tableau de resultat, ROI manager, nom programme et imageplus finale pour + // recuperer ID et date examen + /** + * Permet de realiser l'export du fichier CSV et des ROI contenues dans l'export Manager vers le repertoire d'export defini dans les options + * @param resultats : Tableau contenant les resultats a exporter (doit contenir les titres de colonnes) + * @param nombreColonne : Nombre de colonne avant de passer à la seconde ligne (si 4 colonne mettre 4) + * @param Roi : le ROI manager utilise dans le programme + * @param nomProgramme : le nom du programme (sera utilise comme sous repertoire) + * @param imp : l'ImagePlus d'une image originale ou de la capture secondaire auquel on a ajoute le header, permet de recuperer le nom, l'ID et la date d'examen + * @throws FileNotFoundException : en cas d'erreur d'ecriture + */ + public static void exportAll(String[] resultats, int nombreColonne, RoiManager Roi, String nomProgramme, ImagePlus imp) + throws FileNotFoundException { + + + // On recupere le Patient Name de l'ImagePlus + String patientName = new String(); + patientName = DicomTools.getTag(imp, "0010,0010"); + patientName = patientName.substring(1); + // On eleve l'espace de la fin si il existe + if (patientName.endsWith(" ")) + patientName = patientName.substring(0, patientName.length() - 1); + + + // On recupere le Patient ID de l'ImagePlus + String patientID = new String(); + patientID = DicomTools.getTag(imp, "0010,0020"); + patientID = patientID.substring(1); + // On eleve l'espace de la fin si il existe + if (patientID.endsWith(" ")) + patientID = patientID.substring(0, patientID.length() - 1); + + // On recupere la date d'examen + String date = new String(); + date = DicomTools.getTag(imp, "0008,0020"); + date = date.substring(1); + // On enleve l'espace de la fin si il existe + if (date.endsWith(" ")) + date = date.substring(0, date.length() - 1); + + //Realisation du string builder qui sera ecrit en CSV + StringBuilder content = new StringBuilder(); + //Ajout titre colonne + content.append("Patient's Name"); + content.append(','); + content.append("Patient's ID"); + content.append(','); + content.append("Study Date"); + content.append('\n'); + // Ajouts des valeurs + content.append(patientName); + content.append(','); + content.append(patientID); + content.append(','); + content.append(date); + for (int i = 0; i < resultats.length; i++) { + // Si multiple de n (nombre de valeur par ligne) on fait retour à la + // ligne sinon on met une virgule + if (i % nombreColonne == 0) { + content.append('\n'); + } else { + content.append(','); + } + content.append(resultats[i]); + } + content.append('\n'); + + + //On recupere le path de sauvegarde + String path = Prefs.get("dir.preferred", null); + Boolean testEcriture=false; + + // On verifie que le path est writable si il existe + if(path!=null) + { + File testPath = new File(path); + testEcriture = testPath.canWrite(); + } + + if(path!=null&&testEcriture==false) + { + // Si pas de repertoire defini on notifie l'utilisateur + IJ.showMessage("CSV Path not writable, CSV/ZIP export has failed"); + }if(path!=null&&testEcriture==true) + { + // On construit le sous repertoire avecle nom du programme et l'ID du + // Patient + String pathFinal = path + File.separator + nomProgramme + File.separator + patientID; + File subDirectory = new File(pathFinal); + subDirectory.mkdirs(); + + File f = new File(subDirectory + File.separator + patientID + "_" + date + ".csv"); + + // On ecrit les CSV + PrintWriter pw = null; + try { + pw = new PrintWriter(f); + pw.write(content.toString()); + } catch (IOException e) { + e.printStackTrace(); + } finally { + pw.close(); + } + + // On ecrit le ZIP contenant la sauvegarde des ROIs + Roi[] rois2 = Roi.getRoisAsArray(); + int[] tab = new int[rois2.length]; + for (int i = 0; i < rois2.length; i++) + tab[i] = i; + Roi.setSelectedIndexes(tab); + Roi.runCommand("Save", pathFinal.toString() + File.separator + patientID + "_" + date + ".zip"); + + } +} + /** + * Permet d'exporter le ROI manager uniquement dans un zip contenant les ROI (dans le cadre d'un logiciel ne generant pas de resultat utile a sauver qui seront traités par un autre logiciel par exemple) + * @param Roi : Le ROI manager utilise dans le programme + * @param nomProgramme : Le nom du programme (creation d'un sous repertoire) + * @param imp : Une ImagePlus originale ou de capture secondaire avec le header pour recuperer nom, ID, date d'examen. + */ + public static void exportRoiManager(RoiManager Roi, String nomProgramme, ImagePlus imp) { + + // On recupere le Patient ID de l'ImagePlus + String patientID = new String(); + patientID = DicomTools.getTag(imp, "0010,0020"); + patientID = patientID.substring(1); + // On eleve l'espace de la fin si il existe + if (patientID.endsWith(" ")) + patientID = patientID.substring(0, patientID.length() - 1); + + // On recupere la date d'examen + String date = new String(); + date = DicomTools.getTag(imp, "0008,0020"); + date = date.substring(1); + // On enleve l'espace de la fin si il existe + if (date.endsWith(" ")) + date = date.substring(0, date.length() - 1); + + //On recupere le path de sauvegarde + String path = Prefs.get("dir.preferred", null); + Boolean testEcriture=false; + + // On verifie que le path est writable si il existe + if(path!=null) + { + File testPath = new File(path); + testEcriture = testPath.canWrite(); + } + + if(path!=null&&testEcriture==false) + { + // Si pas de repertoire defini on notifie l'utilisateur + IJ.showMessage("Path not writable, CSV/ZIP export has failed"); + } + + if(path!=null&&testEcriture==true) + { + // On construit le sous repertoire avecle nom du programme et l'ID du + // Patient + String pathFinal = path + File.separator + nomProgramme + File.separator + patientID; + File subDirectory = new File(pathFinal); + subDirectory.mkdirs(); + + // On ecrit le ZIP contenant la sauvegarde des ROIs + Roi[] rois2 = Roi.getRoisAsArray(); + int[] tab = new int[rois2.length]; + for (int i = 0; i < rois2.length; i++) + tab[i] = i; + Roi.setSelectedIndexes(tab); + Roi.runCommand("Save", pathFinal.toString() + File.separator + patientID + "_" + date + ".zip"); + + } + } + +} diff --git a/Modele_VG_Dynamique.java b/Modele_VG_Dynamique.java new file mode 100644 index 00000000..6cc50283 --- /dev/null +++ b/Modele_VG_Dynamique.java @@ -0,0 +1,266 @@ +/** +Copyright (C) 2017 PING Xie and KANOUN Salim +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public v.3 License as published by +the Free Software Foundation; +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +import ij.Prefs; +import ij.WindowManager; +import java.awt.Font; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Date; +import ij.IJ; +import ij.ImagePlus; +import ij.process.ImageStatistics; + +public class Modele_VG_Dynamique { + + public static Font italic = new Font("Arial", Font.ITALIC, 8); + + private int[][][] coupsBrut;//pour enregistrer le nombre de coups de chaque organe pour chaque image + private int[][][] coupsCorrigeBDF;//pour enregistrer le nombre de coups apre la premiere correction de chaque organe pour chaque image + private int[] coupsOeufs;//pour enregistrer le nombre de coups de chaque oeuf + protected static double[] oeufPourc;//pour enregistrer le % de chaque oeuf + private double[] oeufNonIngerePourc;//pour enregistrer le % des oeufs non ingeres + private double[][] pourcCorrigeOeuf;//pour enregistrer le % apre la deuxieme correction de chaque organe pour chaque image + private String[] tempsAcquisition;//pour enregistrer le temps d'acquisition de chaque serie + private int[] temps;//pour enregistrer l'heure d'acquisition de chaque serie par rapport a l'heure ou le patient commence a manger + private String[] nomSerie; + private int finBDFAntre;//pour enregistrer l'index de serie depuis laquelle la region de l'antre est bruit de fond + private int finBDFIntestin;//pour enregistrer l'index de serie depuis laquelle la region de l'intestin est bruit de fond + protected static boolean logOn;//signifie si le log est ouvert + + public Modele_VG_Dynamique() { + + Prefs.useNamesAsLabels = true; + this.finBDFAntre = 0; + this.finBDFIntestin = 0; + logOn=false; + } + + public enum Etat { + ESTOMAC_ANT, INTESTIN_ANT, ESTOMAC_POS, INTESTIN_POS, CIR_ESTOMAC_ANT, CIR_INTESTIN_ANT, CIR_ESTOMAC_POS, CIR_INTESTIN_POS, OEUFOUVERT, ROIDEFAULT, OEUFS, CORRIGER,FIN; + private static Etat[] vals = values(); + + public Etat next() { + return vals[(this.ordinal() + 1) % vals.length]; + } + + public Etat previous() { + // On ajoute un vals.length car le modulo peut ¨ºtre < 0 en java + return vals[((this.ordinal() - 1) + vals.length) % vals.length]; + } + } + + // calcule le coups de chaque organe pour chaque image + // i: l'index de serie, j: 0 (image ANT de la serie), 1 (image POS de la serie), k: 0 estomac, 1 intestin, 2 antre, 3 fundus, 4 total + public void calculerCoupsBrut(int i, int j, int k, ImagePlus imp) { + // Ancienne methode mathis nombre de pixel * moyenne + ImageStatistics is = imp.getStatistics(); + this.coupsBrut[i - 1][j][k] = (int) (is.pixelCount * is.mean); + if (Modele_VG_Dynamique.logOn) IJ.log(""+this.coupsBrut[i - 1][j][k]); + } + + // calcule le coups de chaque oeuf + public void calculerCoupsOeufs(int i, ImagePlus imp) { + ImageStatistics is = imp.getStatistics(); + this.coupsOeufs[i - 1] = (int) (is.pixelCount * is.mean); + } + + // calcule le % de chaque oeuf par rapport au repas total + public void calculerOeufsPourcent() { + int coupsOeufsTotal = 0; + for (int i = 0; i < oeufPourc.length; i++) { + coupsOeufsTotal = coupsOeufsTotal + this.coupsOeufs[i]; + } + + DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(); + sym.setDecimalSeparator('.'); + DecimalFormat us = new DecimalFormat("##.##"); + us.setDecimalFormatSymbols(sym); + if (Modele_VG_Dynamique.logOn) IJ.log("pourcentage des oeufs calcule"); + for (int i = 0; i < oeufPourc.length; i++) { + double pourOeuf = ((double) this.coupsOeufs[i] / (double) coupsOeufsTotal) * 100; + oeufPourc[i] = Double.parseDouble(us.format(pourOeuf)); + if (Modele_VG_Dynamique.logOn) IJ.log("egg"+(i+1)+" : "+oeufPourc[i]); + } + } + + public int getCoupsBrut(int i, int j, int k) { + return this.coupsBrut[i - 1][j][k]; + } + + public void setCoupsBrut(int i, int j, int k, int valeur) { + this.coupsBrut[i - 1][j][k] = valeur; + } + + public void calculerTempsImages() { + for (int i = 0; i < this.tempsAcquisition.length; i++) { + try { + this.tempsImage(i); + } catch (ParseException e) { + e.printStackTrace(); + } + } + } + + //calculer l'heure d'acquisition de chaque serie par rapport a l'heure ou le patient commence a manger + private void tempsImage(int index) throws ParseException { + DateFormat df = new SimpleDateFormat("HHmmss"); + Date d1 = df.parse(this.tempsAcquisition[index].substring(1, 7)); + Date d2 = df.parse(this.tempsAcquisition[0].substring(1, 7)); + long diff = d1.getTime() - d2.getTime(); + long day = diff / (24 * 60 * 60 * 1000); + long hour = (diff / (60 * 60 * 1000) - day * 24); + long min = ((diff / (60 * 1000)) - day * 24 * 60 - hour * 60); + this.temps[index] = (int) (day * 24 * 60 + hour * 60 + min); + } + + // initialisation des tables de resultats + public void initModele(ImagePlus imp) { + this.coupsBrut = new int[imp.getStackSize() / 2][2][5]; + this.coupsCorrigeBDF = new int[imp.getStackSize() / 2][3][5]; + this.coupsOeufs = new int[imp.getStackSize() / 2]; + this.temps = new int[imp.getStackSize() / 2]; + this.tempsAcquisition = new String[imp.getStackSize() / 2]; + this.nomSerie = new String[imp.getStackSize() / 2]; + oeufPourc = new double[imp.getStackSize() / 2]; + this.oeufNonIngerePourc = new double[imp.getStackSize() / 2+1]; + this.pourcCorrigeOeuf=new double[imp.getStackSize() / 2][4]; + for(int i=0; i= this.finBDFAntre) { + this.coupsCorrigeBDF[i][0][1] = Math + .max(this.coupsBrut[i][0][1] - this.coupsBrut[this.finBDFAntre - 1][0][1], 0); + this.coupsCorrigeBDF[i][1][1] = Math + .max(this.coupsBrut[i][1][1] - this.coupsBrut[this.finBDFAntre - 1][1][1], 0); + } else { + this.coupsCorrigeBDF[i][0][1] = 0; + this.coupsCorrigeBDF[i][1][1] = 0; + } + + // modification des valeurs de l'intestin + if (this.finBDFIntestin == 0) { + this.coupsCorrigeBDF[i][0][2] = this.coupsBrut[i][0][2]; + this.coupsCorrigeBDF[i][1][2] = this.coupsBrut[i][1][2]; + } else if (i >= this.finBDFIntestin) { + this.coupsCorrigeBDF[i][0][2] = Math + .max(this.coupsBrut[i][0][2] - this.coupsBrut[this.finBDFIntestin - 1][0][2], 0); + this.coupsCorrigeBDF[i][1][2] = Math + .max(this.coupsBrut[i][1][2] - this.coupsBrut[this.finBDFIntestin - 1][1][2], 0); + } else { + this.coupsCorrigeBDF[i][0][2] = 0; + this.coupsCorrigeBDF[i][1][2] = 0; + } + + // mis a jour des valeur de l'estomac et le total + for (int j = 0; j < 2; j++) { + this.coupsCorrigeBDF[i][j][3] = this.coupsCorrigeBDF[i][j][0] + this.coupsCorrigeBDF[i][j][1]; + this.coupsCorrigeBDF[i][j][4] = this.coupsCorrigeBDF[i][j][2] + this.coupsCorrigeBDF[i][j][3]; + } + + // calcul des Moyennes geometriques + for (int k = 0; k < 3; k++) { + this.coupsCorrigeBDF[i][2][k] = (int) Math + .sqrt((double) this.coupsCorrigeBDF[i][0][k] * (double) this.coupsCorrigeBDF[i][1][k]); + } + this.coupsCorrigeBDF[i][2][3] = this.coupsCorrigeBDF[i][2][0] + this.coupsCorrigeBDF[i][2][1]; + this.coupsCorrigeBDF[i][2][4] = this.coupsCorrigeBDF[i][2][2] + this.coupsCorrigeBDF[i][2][3]; + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 5; k++) { + if (Modele_VG_Dynamique.logOn) IJ.log(i + " " + j + " " + k + " " + this.coupsBrut[i][j][k] + "------" + this.coupsCorrigeBDF[i][j][k]); + } + } + for (int k = 0; k < 5; k++) { + if (Modele_VG_Dynamique.logOn) IJ.log(i + " " + 2 + " " + k + " " + this.coupsCorrigeBDF[i][2][k]); + } + } + } + + // Pour corriger les resultats par les oeufs non ingeres + public void corrigerParOeuf(){ + + DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(); + sym.setDecimalSeparator('.'); + DecimalFormat us = new DecimalFormat("##.##"); + us.setDecimalFormatSymbols(sym); + this.oeufNonIngerePourc[0]=Double.parseDouble(us.format(100.00)); + if (Modele_VG_Dynamique.logOn) IJ.log("resultats apres corrige par oeufs"); + for (int i = 0; i < oeufPourc.length; i++) { + this.oeufNonIngerePourc[i+1]=Double.parseDouble(us.format(this.oeufNonIngerePourc[i]-oeufPourc[i])); + if (Modele_VG_Dynamique.logOn) IJ.log("egg"+(i+1)+" : "+us.format(oeufPourc[i])+"non ingere :"+this.oeufNonIngerePourc[i]); + this.pourcCorrigeOeuf[i][0]=Double.parseDouble(us.format(this.oeufNonIngerePourc[i]+((double)this.coupsCorrigeBDF[i][2][0]/((double)this.coupsCorrigeBDF[i][2][4]+0.00000000001))*(100.00-this.oeufNonIngerePourc[i]))); + this.pourcCorrigeOeuf[i][1]=Double.parseDouble(us.format(((double)this.coupsCorrigeBDF[i][2][1]/((double)this.coupsCorrigeBDF[i][2][4]+0.00000000001))*(100.00-this.oeufNonIngerePourc[i]))); + this.pourcCorrigeOeuf[i][3]=Double.parseDouble(us.format(this.pourcCorrigeOeuf[i][0]+this.pourcCorrigeOeuf[i][1])); + this.pourcCorrigeOeuf[i][2]=Double.parseDouble(us.format(100.00-this.pourcCorrigeOeuf[i][3])); + if (Modele_VG_Dynamique.logOn) IJ.log("coups avant corrige: "+" pourcentage apres corrige"); + if (Modele_VG_Dynamique.logOn) IJ.log("fundus "+this.coupsCorrigeBDF[i][2][0]+" "+this.pourcCorrigeOeuf[i][0]); + if (Modele_VG_Dynamique.logOn) IJ.log("antre "+this.coupsCorrigeBDF[i][2][1]+" "+this.pourcCorrigeOeuf[i][1]); + if (Modele_VG_Dynamique.logOn) IJ.log("intestin "+this.coupsCorrigeBDF[i][2][2]+" "+this.pourcCorrigeOeuf[i][2]); + if (Modele_VG_Dynamique.logOn) IJ.log("estomac "+this.coupsCorrigeBDF[i][2][3]+" "+this.pourcCorrigeOeuf[i][3]); + } + } + + public void setAcquisitionTime(int index, String time) { + this.tempsAcquisition[index - 1] = time; + } + + public void setNomSerie(int index, String nomSerie) { + this.nomSerie[index - 1] = nomSerie; + } + + public void setFinBDFAntre(int finBDFAntre) { + this.finBDFAntre = finBDFAntre; + } + + public void setFinBDFIntestin(int finBDFIntestin) { + this.finBDFIntestin = finBDFIntestin; + } + + public int getFinBDFAntre() { + return finBDFAntre; + } + + public int getFinBDFIntestin() { + return finBDFIntestin; + } + + //permet de mettre les resultats en forme String pour transmettre au application VG_Ingestin_Statique + public String getResultat() { + String resultat=this.tempsAcquisition[0].substring(1,7)+";"; + for(int i=0; i coups;//pour enregistrer les coups dans chaque organe sur chaque image + private HashMap mgs;//pour enregistrer le MG dans chaque organe pour chaque serie + protected static double[] temps;//pour enregistrer l'horaire où on recupere chaque serie + protected static double[] estomacPourcent;//pour enregistrer le pourcentage de l'estomac(par rapport a total) pour chaque serie + protected static double[] fundusPourcent;//pour enregistrer le pourcentage du fundus(par rapport a total) pour chaque serie + protected static double[] antrePourcent;//pour enregistrer le pourcentage de l'antre(par rapport a total) pour chaque serie + protected static double[] funDevEsto;//pour enregistrer le rapport fundus/estomac pour chaque serie + protected static double[] estoInter;//pour enregistrer le rapport fundus/estomac pour chaque serie + protected static double[] tempsInter;//pour enregistrerla derivee de la courbe de variation de l’estomac + protected static boolean logOn;//signifie si log est ouvert + protected static double[] intestinPourcent;//pour enregistrer le pourcentage de l'intestin(par rapport a total) pour chaque serie + + private String[] organes = { "Estomac", "Intestin", "Fundus", "Antre" }; + + private String patient; + private String date; + private boolean trouve;//signifie si la valeur qu'on veut est trouvee sur la courbe + + public Modele_VG_Roi() { + this.coups = new HashMap<>(); + this.mgs = new HashMap<>(); + Prefs.useNamesAsLabels = true; + Modele_VG_Roi.logOn = false; + } + + public enum Etat { + ESTOMAC_ANT, INTESTIN_ANT, ESTOMAC_POS, INTESTIN_POS, CIR_ESTOMAC_ANT, CIR_INTESTIN_ANT, CIR_ESTOMAC_POS, CIR_INTESTIN_POS, FIN, RESULTAT; + private static Etat[] vals = values(); + + public Etat next() { + return vals[(this.ordinal() + 1) % vals.length]; + } + + public Etat previous() { + // On ajoute un vals.length car le modulo peut être < 0 en java + return vals[((this.ordinal() - 1) + vals.length) % vals.length]; + } + } + + // On recupère le nom du patient, la date et son id pour les resultats + public void setPatient(String pat, ImagePlus imp) { + this.patient = pat; + this.date = DicomTools.getTag(imp, "0008,0020"); + this.date = this.date.trim(); + } + + // Cree un montage a partir de l'ImageStack + // En ajoutant les infos DICOM de l'image + public ImagePlus montage(ImageStack stack, String nomProgramme) { + MontageMaker mm = new MontageMaker(); + ImagePlus imp = new ImagePlus("Resultats Vidange Gastrique -" + this.patient, stack); + imp = mm.makeMontage2(imp, 2, 2, 0.5, 1, 4, 1, 10, false); + imp.setTitle("Resultats " + nomProgramme + " -" + this.patient); + return imp; + } + + // calcule le coups du ROI + public void calculerCoups(String roi, int indexImage, ImagePlus imp) { + // Ancienne methode mathis nombre de pixel * moyenne + ImageStatistics is = imp.getStatistics(); + this.coups.put(roi + indexImage, (int) (is.pixelCount * is.mean)); + } + + public int getCoups(String roi, int indexImage) { + return this.coups.get(roi + indexImage); + } + + public void setCoups(String roi, int indexImage, int d) { + this.coups.put(roi + indexImage, d); + } + + private void mgs(int indexImage) { + //on calcule cet de l'antre et de l'intestin + for (String roi : this.organes) + this.moyenneGeo(roi, indexImage); + //on calcule les MGs de l'estomac + this.mgs.put("Estomac_MG" + indexImage, + 1 * this.mgs.get("Fundus_MG" + indexImage) + 1 * this.mgs.get("Antre_MG" + indexImage)); + //on calcule la somme des MGs + this.mgs.put("Total" + indexImage, + 1 * this.mgs.get("Estomac_MG" + indexImage) + 1 * this.mgs.get("Intestin_MG" + indexImage)); + } + + // Calcule la moyenne geometrique pour un organe specifique + private void moyenneGeo(String organe, int indexImage) { + int[] coupsa = new int[2]; + String[] asuppr = new String[2]; + int index = 0; + for (Entry entry : this.coups.entrySet()) { + if (entry.getKey().contains(organe) && entry.getKey().contains(Integer.toString(indexImage))) { + coupsa[index] = entry.getValue(); + asuppr[index] = entry.getKey(); + index++; + } + } + this.mgs.put(organe + "_MG" + indexImage, this.moyenneGeometrique(coupsa)); + } + + // Calcule la moyenne geometrique des nombres en paramètre + private int moyenneGeometrique(int[] vals) { + double result = 1.0; + for (int i = 0; i < vals.length; i++) { + result *= vals[i]; + } + result = Math.sqrt(result); + return (int) result; + } + + // calcule le temps de acquisition de l'image + public void tempsImage(int indexImage, String acquisitionTime) throws ParseException { + DateFormat df = new SimpleDateFormat("HHmmss"); + Date d1 = df.parse(acquisitionTime.substring(1, 7)); + Date d2 = df.parse(Vue_VG_Roi.timeStart); + long diff = d1.getTime() - d2.getTime(); + long day = diff / (24 * 60 * 60 * 1000); + long hour = (diff / (60 * 60 * 1000) - day * 24); + long min = ((diff / (60 * 1000)) - day * 24 * 60 - hour * 60); + Modele_VG_Roi.temps[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 1] = (double) (day * 24 * 60 + hour * 60 + min); + } + + //pour chaque serie, on calcule le pourcentage de l'estomac, le fundus, l'antre et l'intestin par rapport au total du repas + //calcule le rapport fundus/estomac et la derivee de la courbe de variation de l’estomac + public void pourcVGImage(int indexImage) { + this.mgs(indexImage); + + DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(); + sym.setDecimalSeparator('.'); + DecimalFormat us = new DecimalFormat("##.#"); + us.setDecimalFormatSymbols(sym); + + double fundusPour = ((double) this.mgs.get("Fundus_MG" + indexImage) + / (double) this.mgs.get("Total" + indexImage)) * 100; + Modele_VG_Roi.fundusPourcent[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 1] = Double + .parseDouble(us.format(fundusPour)); + double antrePour = ((double) this.mgs.get("Antre_MG" + indexImage) + / (double) this.mgs.get("Total" + indexImage)) * 100; + Modele_VG_Roi.antrePourcent[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 1] = Double + .parseDouble(us.format(antrePour)); + double estomacPour = ((double) this.mgs.get("Estomac_MG" + indexImage) + / (double) this.mgs.get("Total" + indexImage)) * 100; + Modele_VG_Roi.estomacPourcent[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 1] = Double + .parseDouble(us.format(estomacPour)); + double intestinPour = ((double) this.mgs.get("Intestin_MG" + indexImage) + / (double) this.mgs.get("Total" + indexImage)) * 100; + Modele_VG_Roi.intestinPourcent[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 1] = Double + .parseDouble(us.format(intestinPour)); + double funDevEsto = (Modele_VG_Roi.fundusPourcent[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 1] + / Modele_VG_Roi.estomacPourcent[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 1]) * 100; + Modele_VG_Roi.funDevEsto[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 1] = Double + .parseDouble(us.format(funDevEsto)); + + Modele_VG_Roi.tempsInter[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 2] = Modele_VG_Roi.temps[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 1]; + double estoInter = ((Modele_VG_Roi.estomacPourcent[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 2] - Modele_VG_Roi.estomacPourcent[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 1]) + / (Modele_VG_Roi.temps[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 1] - Modele_VG_Roi.temps[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 2])) * 30; + Modele_VG_Roi.estoInter[indexImage + Vue_VG_Roi.resultatsDynamique.length / 4 - 2] = Double + .parseDouble(us.format(estoInter)); + if (Modele_VG_Roi.logOn) { + // affiche le resultat au log pour que l'utilisateur peut le + // verifier + IJ.log("image " + (indexImage) + ": " + " Stomach " + Modele_VG_Roi.estomacPourcent[indexImage] + + " Intestine " + Modele_VG_Roi.intestinPourcent[indexImage] + " Fundus " + + Modele_VG_Roi.fundusPourcent[indexImage] + " Antre " + Modele_VG_Roi.antrePourcent[indexImage]); + } + + } + + // initialisation des tables de resultats + public void initResultat(ImagePlus imp) { + Modele_VG_Roi.temps = new double[imp.getStackSize() / 2 + Vue_VG_Roi.resultatsDynamique.length / 4]; + Modele_VG_Roi.estomacPourcent = new double[imp.getStackSize() / 2 + Vue_VG_Roi.resultatsDynamique.length / 4]; + Modele_VG_Roi.fundusPourcent = new double[imp.getStackSize() / 2 + Vue_VG_Roi.resultatsDynamique.length / 4]; + Modele_VG_Roi.antrePourcent = new double[imp.getStackSize() / 2 + Vue_VG_Roi.resultatsDynamique.length / 4]; + Modele_VG_Roi.intestinPourcent = new double[imp.getStackSize() / 2 + Vue_VG_Roi.resultatsDynamique.length / 4]; + Modele_VG_Roi.funDevEsto = new double[imp.getStackSize() / 2 + Vue_VG_Roi.resultatsDynamique.length / 4]; + Modele_VG_Roi.estoInter = new double[imp.getStackSize() / 2 + Vue_VG_Roi.resultatsDynamique.length / 4 - 1]; + Modele_VG_Roi.tempsInter = new double[imp.getStackSize() / 2 + Vue_VG_Roi.resultatsDynamique.length / 4 - 1]; + + for (int i = 0; i < Vue_VG_Roi.resultatsDynamique.length / 4; i++) { + Modele_VG_Roi.temps[i] = Integer.parseInt(Vue_VG_Roi.resultatsDynamique[i * 4]); + Modele_VG_Roi.estomacPourcent[i] = Double.parseDouble(Vue_VG_Roi.resultatsDynamique[i * 4 + 1]); + Modele_VG_Roi.fundusPourcent[i] = Double.parseDouble(Vue_VG_Roi.resultatsDynamique[i * 4 + 2]); + Modele_VG_Roi.antrePourcent[i] = Double.parseDouble(Vue_VG_Roi.resultatsDynamique[i * 4 + 3]); + Modele_VG_Roi.intestinPourcent[i] = 100.0 - Modele_VG_Roi.estomacPourcent[i]; + Modele_VG_Roi.funDevEsto[i] = Modele_VG_Roi.fundusPourcent[i] / Modele_VG_Roi.estomacPourcent[i] * 100.0; + if(i>0){ + Modele_VG_Roi.tempsInter[i-1]=Modele_VG_Roi.temps[i]; + Modele_VG_Roi.estoInter[i-1] = ((Modele_VG_Roi.estomacPourcent[i-1] - Modele_VG_Roi.estomacPourcent[i]) + / (Modele_VG_Roi.temps[i] - Modele_VG_Roi.temps[i-1])) * 30.0; + + } + } + } + + // permet de transferer toutes les resultats en une tableau de chaine + public String[] resultats(ImagePlus imp) { + String[] retour = new String[4 * (imp.getStackSize() / 2 +Vue_VG_Roi.resultatsDynamique.length/4+ 1) + 12]; + //on enregistre la premiere partie des resultats + retour[0] = "Time(min)"; + retour[1] = "Stomach(%)"; + retour[2] = "Fundus(%)"; + retour[3] = "Antrum(%)"; + for (int i = 0; i < imp.getStackSize() / 2+Vue_VG_Roi.resultatsDynamique.length/4; i++) { + retour[i * 4 + 4] = Integer.toString((int) Modele_VG_Roi.temps[i]); + retour[i * 4 + 5] = Double.toString(Modele_VG_Roi.estomacPourcent[i]); + retour[i * 4 + 6] = Double.toString(Modele_VG_Roi.fundusPourcent[i]); + retour[i * 4 + 7] = Double.toString(Modele_VG_Roi.antrePourcent[i]); + } + // on enregistre la deuxime partie des resultats + int j = (imp.getStackSize() / 2+Vue_VG_Roi.resultatsDynamique.length/4) * 4 + 4; + String institutionName = ""; + if (DicomTools.getTag(imp, "0008,0080") != null) { + institutionName = DicomTools.getTag(imp, "0008,0080").substring(1); + } + retour[j++] = institutionName; + String studyDescription = ""; + if (DicomTools.getTag(imp, "0008,1030") != null) { + studyDescription = DicomTools.getTag(imp, "0008,1030").substring(1); + } + retour[j++] = studyDescription; + retour[j++] = "Patient : " + this.patient; + retour[j++] = "Date : " + this.date.substring(6, 8) + "/" + this.date.substring(4, 6) + "/" + + this.date.substring(0, 4) + " " + Vue_VG_Roi.timeStart.substring(0, 2) + "h" + + Vue_VG_Roi.timeStart.substring(2, 4); + retour[j++] = "Start Antrum : " + this.getDebut("Antre") + " min"; + retour[j++] = "Start Intestine : " + this.getDebut("Intestin") + " min"; + //SI valeur interpolee on ajoute * a la string + retour[j++] = "Lag Phase : " + this.getX(95.0) + (trouve? " min":" min*") ; + retour[j++] = "T1/2 : " + this.getX(50.0) + (trouve? " min":" min*") ; + retour[j++] = "Retention at 1h : " + this.getY(60.0) + (trouve? "%":"%*") ; + retour[j++] = "Retention at 2h : " + this.getY(120.0) + (trouve? "%":"%*") ; + retour[j++] = "Retention at 3h : " + this.getY(180.0) + (trouve? "%":"%*") ; + retour[j] = "Retention at 4h : " + this.getY(240.0) + (trouve? "%":"%*") ; + return retour; + } + + // permet de obtenir un courbe + protected ImagePlus createCourbeUn(String yAxisLabel, Color color, String titre, double[] resX, double[] resY, + double upperBound) { + JFreeChart xylineChart = ChartFactory.createXYLineChart("", "min", yAxisLabel, + createDatasetUn(resX, resY, titre), PlotOrientation.VERTICAL, true, true, true); + + XYPlot plot = (XYPlot) xylineChart.getPlot(); + // Background + plot.setBackgroundPaint(Color.WHITE); + + // XYLineAndShapeRenderer + // reference: + // https://stackoverflow.com/questions/28428991/setting-series-line-style-and-legend-size-in-jfreechart + XYLineAndShapeRenderer lineAndShapeRenderer = new XYLineAndShapeRenderer(); + lineAndShapeRenderer.setSeriesPaint(0, color); + lineAndShapeRenderer.setSeriesStroke(0, new BasicStroke(2.0F)); + plot.setRenderer(lineAndShapeRenderer); + lineAndShapeRenderer.setBaseLegendTextFont(new Font("", Font.BOLD, 16)); + // XAxis + NumberAxis domainAxis = (NumberAxis) plot.getDomainAxis(); + domainAxis.setRange(0.00, 360.00); + domainAxis.setTickUnit(new NumberTickUnit(30.00)); + domainAxis.setTickMarkStroke(new BasicStroke(2.5F)); + domainAxis.setLabelFont(new Font("", Font.BOLD, 16)); + domainAxis.setTickLabelFont(new Font("", Font.BOLD, 12)); + // YAxis + NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); + rangeAxis.setRange(0.00, upperBound); + rangeAxis.setTickUnit(new NumberTickUnit(10.00)); + rangeAxis.setTickMarkStroke(new BasicStroke(2.5F)); + rangeAxis.setLabelFont(new Font("", Font.BOLD, 16)); + rangeAxis.setTickLabelFont(new Font("", Font.BOLD, 12)); + // Grid + plot.setDomainGridlinesVisible(false); + BufferedImage buff = xylineChart.createBufferedImage(640, 512); + ImagePlus courbe = new ImagePlus("", buff); + return courbe; + } + + // permet de creer un dataset d'un group de donees pour un courbe + private XYSeriesCollection createDatasetUn(double[] resX, double[] resY, String titre) { + XYSeries courbe = new XYSeries(titre); + XYSeriesCollection dataset = new XYSeriesCollection(); + for (int i = 0; i < resX.length; i++) { + courbe.add(resX[i], resY[i]); + } + dataset.addSeries(courbe); + return dataset; + } + + // permet de creer un graphique avec trois courbes + protected ImagePlus createCourbeTrois(String yAxisLabel, double[] resX, double[] resY1, Color color1, String titre1, + double[] resY2, Color color2, String titre2, double[] resY3, Color color3, String titre3) { + //On cree un dataset qui contient les 3 series + XYSeriesCollection dataset=createDatasetTrois(resX, resY1, titre1, resY2, titre2, resY3, titre3); + //On cree le graphique + JFreeChart xylineChart = ChartFactory.createXYLineChart("", "min", yAxisLabel, + dataset, PlotOrientation.VERTICAL, true, + true, false); + // Parametres de l'affichage + XYPlot plot = (XYPlot) xylineChart.getPlot(); + // choix du Background + plot.setBackgroundPaint(Color.WHITE); + + // XYLineAndShapeRenderer + // reference: + // https://stackoverflow.com/questions/28428991/setting-series-line-style-and-legend-size-in-jfreechart + XYLineAndShapeRenderer lineAndShapeRenderer = new XYLineAndShapeRenderer(); + //on set le renderer dans le plot + plot.setRenderer(lineAndShapeRenderer); + //On definit les parametre du renderer + lineAndShapeRenderer.setBaseLegendTextFont(new Font("", Font.BOLD, 16)); + lineAndShapeRenderer.setSeriesPaint(0, color1); + lineAndShapeRenderer.setSeriesPaint(1, color2); + lineAndShapeRenderer.setSeriesPaint(2, color3); + //Defini la taille de la courbes (epaisseur du trait) + lineAndShapeRenderer.setSeriesStroke(0, new BasicStroke(2.0F)); + lineAndShapeRenderer.setSeriesStroke(1, new BasicStroke(2.0F)); + lineAndShapeRenderer.setSeriesStroke(2, new BasicStroke(2.0F)); + + // XAxis + NumberAxis domainAxis = (NumberAxis) plot.getDomainAxis(); + //Limites de l'axe X + domainAxis.setRange(0.00, 360.00); + //Pas de l'axe X + domainAxis.setTickUnit(new NumberTickUnit(30.00)); + domainAxis.setTickMarkStroke(new BasicStroke(2.5F)); + domainAxis.setLabelFont(new Font("", Font.BOLD, 16)); + domainAxis.setTickLabelFont(new Font("", Font.BOLD, 12)); + + // YAxis + NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); + rangeAxis.setRange(0.00, 100.00); + rangeAxis.setTickUnit(new NumberTickUnit(10.00)); + rangeAxis.setTickMarkStroke(new BasicStroke(2.5F)); + rangeAxis.setLabelFont(new Font("", Font.BOLD, 16)); + rangeAxis.setTickLabelFont(new Font("", Font.BOLD, 12)); + + // Grid + //On ne met pas de grille sur la courbe + plot.setDomainGridlinesVisible(false); + //On cree la buffered image de la courbe et on envoie dans une ImagePlus + BufferedImage buff = xylineChart.createBufferedImage(640, 512); + ImagePlus courbe = new ImagePlus("", buff); + + return courbe; + } + + // permet de creer un dataset de trois groups de donees pour trois courbes + private XYSeriesCollection createDatasetTrois(double[] resX, double[] resY1, String titre1, double[] resY2, + String titre2, double[] resY3, String titre3) { + // On initialise les 3 series avec un titre + XYSeries courbe1 = new XYSeries(titre1); + XYSeries courbe2 = new XYSeries(titre2); + XYSeries courbe3 = new XYSeries(titre3); + //On initialise un dataset + XYSeriesCollection dataset = new XYSeriesCollection(); + // Pour chaque serie on ajouter les valeurs une a une + for (int i = 0; i < resX.length; i++) { + courbe1.add(resX[i], resY1[i]); + courbe2.add(resX[i], resY2[i]); + courbe3.add(resX[i], resY3[i]); + } + //On ajoute les 3 series dans le dataset + dataset.addSeries(courbe1); + dataset.addSeries(courbe2); + dataset.addSeries(courbe3); + return dataset; + } + + //Pour faire un fit lineaire et extrapoler les valeurs non existante + private double[] getParametreCourbeFlit(){ + XYSeriesCollection dataset=createDatasetTrois(Modele_VG_Roi.temps, + Modele_VG_Roi.estomacPourcent, "Stomach", Modele_VG_Roi.fundusPourcent, + "Fundus", Modele_VG_Roi.antrePourcent, "Antrum"); + //Retourne les valurs a et b de la fonction fit y=ax+b + double[] parametreCourbe=Regression.getOLSRegression(dataset, 0 ); + return parametreCourbe; + } + + // permet de obtenir le temps de debut antre et debut intestin + private int getDebut(String organe) { + boolean trouve = false; + double res = 0.0; + if(organe=="Antre"){ + for (int i = 0; i < Modele_VG_Roi.antrePourcent.length && !trouve; i++) { + //si le pourcentage de l'antre > 0 + if (Modele_VG_Roi.antrePourcent[i] > 0) { + trouve = true; + res = Modele_VG_Roi.temps[i]; + } + } + } + if(organe=="Intestin"){ + for (int i = 0; i < Modele_VG_Roi.estomacPourcent.length && !trouve; i++) { + //si le pourcentage de l'estomac est < 100, c'est a dire le pourcentage de l'intestin > 0 + if (Modele_VG_Roi.estomacPourcent[i] < 100.0) { + trouve = true; + res = Modele_VG_Roi.temps[i]; + } + } + } + return (int) Math.round(res); + } + + // permet de obtenir la valeur X de la courbe selon la valeur Y donee + private int getX(double valueY) { + trouve = false; + double valueX = 0.0; + for (int i = 0; i < Modele_VG_Roi.estomacPourcent.length && !trouve; i++) { + if (Modele_VG_Roi.estomacPourcent[i] <= valueY) { + trouve = true; + double x1 = Modele_VG_Roi.temps[i - 1]; + double x2 = Modele_VG_Roi.temps[i]; + double y1 = Modele_VG_Roi.estomacPourcent[i - 1]; + double y2 = Modele_VG_Roi.estomacPourcent[i]; + valueX = x2 - (y2 - valueY) * ((x2 - x1) / (y2 - y1)); + } + } + if(trouve==false){ + //Si les valeurs n'existent pas on realise le fit + double[] parametres=this.getParametreCourbeFlit(); + double a=parametres[1]; + double b=parametres[0]; + valueX=(valueY-b)/a; + } + return (int) Math.round(valueX); + } + + // permet de obtenir la valeur Y de la courbe selon la valeur X donee + private double getY(double valueX) { + trouve = false; + double valueY = 0.0; + for (int i = 0; i < Modele_VG_Roi.temps.length && !trouve; i++) { + if (Modele_VG_Roi.temps[i] >= valueX) { + trouve = true; + double x1 = Modele_VG_Roi.temps[i - 1]; + double x2 = Modele_VG_Roi.temps[i]; + double y1 = Modele_VG_Roi.estomacPourcent[i - 1]; + double y2 = Modele_VG_Roi.estomacPourcent[i]; + valueY = y2 - (x2 - valueX) * ((y2 - y1) / (x2 - x1)); + } + } + if(trouve==false){ + //Si les valeurs n'existent pas on realise le fit + double[] parametres=this.getParametreCourbeFlit(); + double a=parametres[1]; + double b=parametres[0]; + valueY=a*valueX+b; + } + return (double) (Math.round(valueY * 10) / 10.0); + } + +} \ No newline at end of file diff --git a/PrefsWindow.java b/PrefsWindow.java new file mode 100644 index 00000000..e3753087 --- /dev/null +++ b/PrefsWindow.java @@ -0,0 +1,121 @@ +/** +Copyright (C) 2017 MOHAND Mathis and KANOUN Salim +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public v.3 License as published by +the Free Software Foundation; +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import ij.IJ; +import ij.Prefs; +import ij.plugin.PlugIn; + +public class PrefsWindow extends JPanel implements PlugIn, ActionListener { + + private static final long serialVersionUID = 1148288454969248518L; + private JFrame frame ; + private JLabel lut, dir ; + private JButton btn_choixLut, btn_savelut, btn_dir, btn_savedir, btn_displut ; + private JFileChooser fc ; + + @Override + public void run(String arg0) { + this.frame = new JFrame("Preferences Window"); + String plut = Prefs.get("lut.preferred", null)==null?"Preferred LUT":Prefs.get("lut.preferred", null) ; + this.lut = new JLabel(plut) ; + this.lut.setEnabled(false); + this.btn_choixLut = new JButton("Open...") ; + this.btn_choixLut.addActionListener(this); + this.btn_savelut = new JButton("Save") ; + this.btn_savelut.addActionListener(this); + + this.btn_displut = new JButton("Show LUTs"); + this.btn_displut.addActionListener(this); + + this.btn_savedir = new JButton("Save") ; + this.btn_savedir.addActionListener(this); + + String pdir = Prefs.get("dir.preferred", null)==null?"Save Directory":Prefs.get("dir.preferred", null) ; + this.dir = new JLabel(pdir) ; + this.dir.setEnabled(false); + this.btn_dir = new JButton("Browse") ; + this.btn_dir.addActionListener(this); + this.fc = new JFileChooser() ; + JPanel pan = new JPanel(); + pan.setLayout(new GridLayout(2,1)); + + JPanel pan_lut = new JPanel(); + pan_lut.add(this.lut); + pan_lut.add(this.btn_choixLut); + pan_lut.add(this.btn_displut); + pan_lut.add(this.btn_savelut); + pan.add(pan_lut); + + JPanel pan_dir = new JPanel() ; + pan_dir.add(this.dir); + pan_dir.add(this.btn_dir); + pan_dir.add(this.btn_savedir); + pan.add(pan_dir); + this.add(pan); + //Create and set up the window. + this.frame.setSize(this.frame.getPreferredSize()); + //Add content to the window. + this.frame.add(this); + + //Display the window. + this.frame.pack(); + this.frame.setVisible(true); + this.frame.setSize(frame.getPreferredSize()); + } + + @Override + public void actionPerformed(ActionEvent arg0) { + if (arg0.getSource() == this.btn_choixLut) { + this.fc.setCurrentDirectory(new File("./luts")); + this.fc.setDialogTitle("Choose Preferred LUT"); + int returnVal = fc.showOpenDialog(PrefsWindow.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = fc.getSelectedFile(); + this.lut.setText(file.getPath()); + } + } + if (arg0.getSource() == this.btn_savelut) { + Prefs.set("lut.preferred", this.lut.getText()+""); + Prefs.savePreferences(); + } + if (arg0.getSource() == this.btn_dir) { + this.fc.setDialogTitle("Export directory"); + this.fc.setCurrentDirectory(this.fc.getFileSystemView().getDefaultDirectory()); + this.fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + this.fc.setAcceptAllFileFilterUsed(false); + int rval = fc.showOpenDialog(PrefsWindow.this); + if (rval == JFileChooser.APPROVE_OPTION) { + this.dir.setText(fc.getSelectedFile().getAbsoluteFile().toString()) ; + } + } + if (arg0.getSource() == this.btn_savedir) { + Prefs.set("dir.preferred", this.dir.getText()+""); + Prefs.savePreferences(); + } + if (arg0.getSource() == this.btn_displut) + IJ.run("Display LUTs"); + this.fc = new JFileChooser() ; + } + +} diff --git a/Vue_Shunpo.java b/Vue_Shunpo.java new file mode 100644 index 00000000..2b9069a2 --- /dev/null +++ b/Vue_Shunpo.java @@ -0,0 +1,758 @@ +/** +Copyright (C) 2017 MOHAND Mathis and KANOUN Salim +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public v.3 License as published by +the Free Software Foundation; +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +import java.awt.Button; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Frame; +import java.awt.GridLayout; +import java.awt.Label; +import java.awt.Panel; +import java.awt.Point; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map.Entry; + +import javax.swing.JOptionPane; + +import ij.IJ; +import ij.ImagePlus; +import ij.Prefs; +import ij.WindowManager; +import ij.gui.ImageCanvas; +import ij.gui.ImageWindow; +import ij.gui.Overlay; +import ij.gui.StackWindow; +import ij.gui.TextRoi; +import ij.gui.Toolbar; +import ij.plugin.Concatenator; +import ij.plugin.PlugIn; +import ij.plugin.frame.RoiManager; +import ij.process.LUT; +import ij.util.DicomTools; + +public class Vue_Shunpo implements PlugIn { + + protected HashMap lesBoutons; + + private boolean imageOuverte = false; + + private Label img_inst; + + private Controleur_Shunpo leControleur = new Controleur_Shunpo(this, new Modele_Shunpo()); + + protected Overlay overlay ; + + private Label[] labRes ; + + protected Label Csv = new Label(); + + protected RoiManager leRoi; + + protected Label instructions; + + protected CustomWindow res; + + protected CustomStackWindow win; + + protected ImagePlus imp; + + protected static boolean image2Ouverte; + + private Dimension dimensionPanelPrincipal; + + private Dimension dimensionPanelResultat; + + + @Override + public void run(String arg) { + // Initialisation des differents attributs + RoiManager rm=new RoiManager(false); + leRoi=rm; + instructions = new Label(""); + img_inst = new Label(); + labRes = new Label[10] ; + for (int i = 0 ; i < 10 ; i++) + labRes[i] = new Label("") ; + initBoutons(); + addEcouteurs(); + lesBoutons.get("Valider").addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent arg0) { + Button b = (Button) arg0.getSource(); + if (WindowManager.getCurrentImage() != null && WindowManager.getCurrentImage() != imp && imageOuverte == true) { + ImagePlus imp=WindowManager.getCurrentImage(); + ouvertureImageBrain(imp) ; + } + if (WindowManager.getCurrentImage() != null && imageOuverte == false) { + imageOuverte = true; + ImagePlus imp=WindowManager.getCurrentImage(); + ouvertureImage(imp) ; + } + ((Frame) b.getParent().getParent()).dispose(); + + } + + }); + //Methode de d茅marrage automatique mais pas tr猫s int茅r茅ssante + //if (WindowManager.getCurrentImage() != null) { + // ImagePlus imp=WindowManager.getCurrentImage(); + // imageOuverte = true ; + // ouvertureImage(imp); + //} + if (!imageOuverte) + ouvrirImage("Lungs - Kidneys"); + } + + + + private void initBoutons() { + + lesBoutons = new HashMap<>(); + lesBoutons.put("Show Log", new Button(" Show Log ")); + lesBoutons.put("Draw ROI", new Button(" Draw ROI ")); + lesBoutons.put("Quitter", new Button(" Quit ")); + lesBoutons.put("Contrast", new Button(" Contrast ")); + lesBoutons.put("Precedent", new Button(" Previous ")); + lesBoutons.put("Suivant", new Button(" Next ")); + lesBoutons.put("Contraste", new Button(" Contrast ")); + lesBoutons.put("Valider", new Button(" Confirm ")); + lesBoutons.put("Capture", new Button(" Capture ")); + } + + private void addEcouteurs() { + for (Entry entry : lesBoutons.entrySet()) + entry.getValue().addActionListener(leControleur); + } + + protected void setInstructions(String inst) { + instructions.setText(inst); + } + + // Extraite du code de la classe Panel_Window de Wayne Rasband + // Source : https://imagej.nih.gov/ij/plugins/panel-window.html + class CustomCanvas extends ImageCanvas { + + private static final long serialVersionUID = -5710708795558320699L; + + CustomCanvas(ImagePlus imp) { + super(imp); + } + } // Fin CustomCanvas + + + // Extraite puis modifi茅e du code de la classe Panel_Window de Wayne Rasband + // Source : https://imagej.nih.gov/ij/plugins/panel-window.html + // Cette classe permet d'avoir une image et des 茅l茅ments graphiques sur une + // fen锚tre + // On l'utilise pour la fen锚tre de r茅sultats + + class CustomWindow extends ImageWindow{ + + private static final long serialVersionUID = -9097595151860174657L; + + + CustomWindow(ImagePlus imp) { + super(imp, new CustomCanvas(imp)); + addPanel(); + } + // Fin constructeur CustomWindow + + // Permet d'ajouter les boutons è„¿ la fen锚tre + + private void addPanel() { + // Agencement des composants + Panel panel = new Panel(); + panel.setLayout(new FlowLayout()); + + Panel resultats = new Panel(); + resultats.setLayout(new GridLayout(6, 2)); + for (int i = 0 ; i < 10 ; i++) { + resultats.add(labRes[i]) ; + resultats.setSize(resultats.getMaximumSize()); + } + Panel csv = new Panel(); + csv.setLayout(new GridLayout(1, 1)); + + //On test la pr茅sence d'un repertoire CSV defini + String path = Prefs.get("dir.preferred", null); + if (path==null){ + Csv.setText("No CSV output"); + Csv.setForeground(Color.RED); + } + else{ + Csv.setText("CSV Save OK"); + } + csv.add(Csv); + Panel Capture = new Panel(); + Capture.setLayout(new GridLayout(1, 1)); + Button capture=lesBoutons.get("Capture"); + Capture.add(capture); + resultats.add(csv); + resultats.add(Capture); + panel.add(resultats) ; + add(panel); + pack(); + dimensionPanelResultat=panel.getSize(); + + // Permet d'avoir la fen锚tre ouverte au m锚me endroit que l'image + // s茅lectionn茅e par l'utilisateur + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + Point loc = getLocation(); + Dimension size = getSize(); + if (loc.y + size.height > screen.height) + getCanvas().zoomOut(0, 0); + + } // Fin addPanel + + } // Fin CustomWindow + + // Extraite puis modifi茅e du code de la classe Panel_Window de Wayne Rasband + // Source : https://imagej.nih.gov/ij/plugins/panel-window.html + // Cette classe permet d'avoir une pile d'images et des 茅l茅ments graphiques + // sur une fen锚tre + + class CustomStackWindow extends StackWindow { + + private static final long serialVersionUID = -6280620624574294247L; + + CustomStackWindow(ImagePlus imp) { + super(imp, new CustomCanvas(imp)); + addPanel(); + } // Fin constructeur CustomStackWindow + + // Construction de l'interface + void addPanel() { + // Agencement des composants + Panel panel = new Panel(); + panel.setLayout(new FlowLayout()); + //addEcouteurs(); + + // Partie gauche + Panel gauche = new Panel(); + gauche.setLayout(new FlowLayout()); + + // Premiers boutons + Panel btns_glob = new Panel(); + btns_glob.setLayout(new GridLayout(1, 3)); + btns_glob.add(lesBoutons.get("Quitter")); + btns_glob.add(lesBoutons.get("Draw ROI")); + btns_glob.add(lesBoutons.get("Contrast")); + gauche.add(btns_glob); + + // Instructions + Panel instru = new Panel(); + instru.setLayout(new GridLayout(2, 1)); + instru.add(instructions); + Panel btns_instru = new Panel(); + btns_instru.setLayout(new GridLayout(1, 3)); + btns_instru.add(lesBoutons.get("Show Log")); + btns_instru.add(lesBoutons.get("Precedent")); + lesBoutons.get("Precedent").setEnabled(false); + btns_instru.add(lesBoutons.get("Suivant")); + instru.add(btns_instru); + instru.setBackground(Color.LIGHT_GRAY); + gauche.add(instru); + panel.add(gauche); + add(panel); + pack(); + //On recupere la dimension du panel qu'on vient de creer pour prendre en compte sa taille dans la dimension de la fenetre finale + dimensionPanelPrincipal=panel.getSize(); + + // Permet d'avoir la fen锚tre ouverte au meme endroit que l'image + // selectionnee par l'utilisateur + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + Point loc = getLocation(); + Dimension size = getSize(); + if (loc.y + size.height > screen.height) + getCanvas().zoomOut(0, 0); + } // Fin addPanel + + @Override + public void windowClosing(WindowEvent we) { + //On ferme le ROI manager en plus de la fenetre + leRoi.close(); + win.close(); + System.gc(); + } + + } // Fin CustomStackWindow + + //Ouvre le dialog pour charger une image + protected void ouvrirImage(String image) { + Frame f = new Frame(); + Panel pan = new Panel(); + pan.setLayout(new GridLayout(2, 1)); + img_inst.setText("Please open the " + image + " image then confirm."); + pan.add(img_inst); + pan.add(lesBoutons.get("Valider")); + f.add(pan); + f.setLocationRelativeTo(null); + Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); + f.setSize(200, 300); + f.pack(); + f.setLocation(dim.width / 2 - f.getSize().width / 2, dim.height / 2 - f.getSize().height / 2); + f.setVisible(true); + f.setResizable(false); + //tj au dessus Pour eviter un click qui ferait perdre la fenetre + f.setAlwaysOnTop(true); + f.toFront(); + + f.addWindowListener(new WindowAdapter(){ + public void windowClosing(WindowEvent we) { + end("dialog"); + f.dispose(); + } + }); + + if (imageOuverte==true){ + lesBoutons.get("Suivant").setEnabled(false); + } + + } + + private void ouvertureImage(ImagePlus imp) { + int nombreImage=imp.getStackSize(); + if (nombreImage!=2) IJ.showMessage("Wrong Input, number of image should be 2, please restart"); + //Tri des image dans une nouvelle imageplus + ImagePlus imp2=trierImage(imp); + imp2.show(); + imp.close(); + //Suite du programme sur la nouvelle imageplus + this.imp=imp2; + //Charge la LUT + setCustomLut(this.imp); + // Initialisation du Canvas qui permet de mettre la pile d'images + // dans une fenetre c'est une pile d'images (plus d'une image) on cree une fenetre pour la pile d'images; + CustomStackWindow win = new CustomStackWindow(this.imp); + this.win=win; + win.setTitle(setTitre(win.getImagePlus())); + this.imp.setTitle(setTitre(this.imp)); + // Salim On fixe la taille on prend taille originale on met un zoom de 2 et ajoute la hauteur du panel (46) a la fenetre et on ajoute 12 width et 68 (+15 de marge) en height pixel pour les element graphique de imageplus + win.setSize(512+30 , (512 + dimensionPanelPrincipal.height + 90)); + win.repaint(); + win.getCanvas().fitToWindow(); + win.toFront(); + //On initialise l'overlay + initOverlay(); + //On ajouter l'overlay Droite/Gauche + overlayDG(); + //On met sur l'image + win.getImagePlus().setOverlay(overlay); + if (instructions.getText().equals("")) + instructions.setText("Delimit the right lung."); + IJ.setTool(Toolbar.POLYGON); + win.showSlice(2); + } + + // Definit le titre de la forme : ShunPo - XXXXX Xxxxx - organe et renvoit la string titre + private String setTitre(ImagePlus imp) { + String tagSerie = DicomTools.getTag(this.imp, "0008,103E"); + String tagNom = DicomTools.getTag(this.imp, "0010,0010"); + String titre = "ShunPo - " ; + titre = titre + tagNom + " - " + tagSerie ; + return titre ; + } + + // Interface graphique pour resultats + protected void UIResultats(ImagePlus screen) { + //On cree la fenetre resultat avec le panel resultat + res = new CustomWindow(screen); + res.pack(); + //On resize la window pour laisser la place a l'image et au pannel + //Ici on ajoute que 70 pixel en hauteur car il n'y a pas l'ascenseur horizontal du stack + res.setSize(screen.getWidth()+30,screen.getHeight()+dimensionPanelResultat.height+70); + ImageCanvas ic=res.getCanvas(); + //On cache le zoom indicator pour la capture + ic.hideZoomIndicator(true); + ic.fitToWindow(); + ic.setScaleToFit(true); + //On prend le focus + res.toFront(); + //On implemente le titre de la fenetre + res.setTitle("Pulmonary Shunt - Results"); + //On quitte l'outil ROI pour Hand (evite d'avoir le curseur qui fait des ROI) + IJ.setTool("hand"); + } + + // routine de fermeture du programme + protected void end(String dialog) { + int optionChoisie = JOptionPane.showConfirmDialog(null, "The program will now shut down", "", JOptionPane.OK_CANCEL_OPTION) ; + if (optionChoisie == JOptionPane.OK_OPTION) { + if (dialog==null){ + leRoi.close(); + win.close(); + System.gc(); + } + if (dialog=="dialog"){ + leRoi.close(); + System.gc(); + } + + } + } + + + /** + * Applique la LUT definie dans les preference à l'ImagePlus demandee + * @param imp : L'ImagePlus sur laquelle on va appliquer la LUT des preferences + */ + static void setCustomLut(ImagePlus imp){ + String lalut = Prefs.get("lut.preferred", null) ; + if (lalut != null) { + LUT lut = ij.plugin.LutLoader.openLut(lalut); + imp.setLut(lut); + } + } + + //Injecte une nouvelle imageplus dans la fenetre existante + private void ouvertureImageBrain(ImagePlus imp) { + //On reactive le boutton suivant + lesBoutons.get("Suivant").setEnabled(true); + //On ordonne les images dans une nouvelle imageplus + ImagePlus cerveau2=trierImage(imp); + //On injecte l'image cerveau dans la fenetre + win.setImage(cerveau2); + imp.getWindow().close(); + //On applique la LUT des preference si presente + setCustomLut(cerveau2); + //On set le Titre + cerveau2.setTitle(setTitre(cerveau2)); + win.setTitle(setTitre(cerveau2)); + //on set le canvas et on repaint + win.getCanvas().fitToWindow(); + win.repaint(); + win.getImagePlus().killRoi(); + //On ajouter l'overlay Droite/Gauche + initOverlay(); + overlayDG(); + win.getImagePlus().setOverlay(overlay); + //Variable pour notifier que l'image 2 est ouverte + image2Ouverte=true ; + //On envoi un event au controleur pour passer è„¿ l'茅tat suivant + ActionEvent e = new ActionEvent(lesBoutons.get("Suivant"), ActionEvent.ACTION_PERFORMED, "Suivant"); + leControleur.actionPerformed(e); + } + + // Remplis les labels des resultats + protected void labelsResultats(String[] resultats) { + for (int i = 0 ; i < resultats.length ; i++) { + labRes[i].setText(resultats[i]);} + if (Modele_Shunpo.shunt<2) { + //Si shunt inférieur à 2% (examen normal) Affiche en vert + labRes[7].setForeground(new Color(0,89,0)); + } + if (Modele_Shunpo.shunt<5 && Modele_Shunpo.shunt>2) { + //Affiche en Orange + labRes[7].setForeground(new Color(229,148,0)); + } + if (Modele_Shunpo.shunt>5) { + //Affiche en Rouge + labRes[7].setForeground(new Color(230,0,0)); + } + //Affiche le nom en gras + labRes[9].setFont(new Font ("Arial", Font.BOLD, 12)); + } + + private void initOverlay() { + //On initialise l'overlay il ne peut y avoir qu'un Overlay + // pour tout le programme sur lequel on va ajouter/enlever les ROI au fur et è„¿ mesure + overlay = new Overlay(); + overlay.drawLabels(true); + overlay.drawNames(true); + } + + protected void overlayDG() { + //Creer overlay Droit et gauche + Font font = new Font("Arial",Font.PLAIN, 19) ; + overlay.setLabelFont(font); + // Ajout de l'indication de la droite du patient + double xr = 10; + double y = (win.getImagePlus().getHeight())/2; + TextRoi right = new TextRoi(xr, y, ""); + overlay.add(right, "Right"); + // Ajout de la gauche du patient + double xl = win.getImagePlus().getWidth() - 20; // -20 sinon on sort de l'image + TextRoi left = new TextRoi(xl, y, ""); + overlay.add(left, "Left"); + } + + //Permet de tester si image unique ou multiframe + /** + * Permet de savoir si l'ImagePlus vient d'une Image MultiFrame (teste l'Image 1) + * @param imp : L'ImagePlus a tester + * @return : vrai si multiframe + */ + public static boolean isMultiFrame(ImagePlus imp) { + //On regarde la coupe 1 + imp.setSlice(1); + + //Regarde si frame unique ou multiple + String numFrames = DicomTools.getTag(imp, "0028,0008").substring(1,DicomTools.getTag(imp, "0028,0008").length()); + + //bug du dicom tool, si il y a un espace � la fin de la string on l'enleve + if (numFrames.endsWith(" ")) numFrames=numFrames.substring(0, numFrames.length()-1); + + //On passe le texte en Int + int slices=Integer.parseInt(numFrames); + + if (slices==1) return false; + else return true; + + } + + //Methode globale pour les deux type d'image teste l'image d'input et lance la bonne methode et renvoie l'image triee + /** + * Permet de trier les image Anterieure et posterieure et retourne les images posterieures pour garder la meme lateralisation (la droite est à gauche de l'image comme une image de face) + * @param imp : ImagePlus a trier + * @return Retourne l'ImagePlus avec les images posterieures inversees + */ + public static ImagePlus trierImage(ImagePlus imp) { + ImagePlus imp2=null; + if (isMultiFrame(imp)) { + imp2=trierImageMultiFrame(imp); + } + if (!isMultiFrame(imp)) { + imp2=trierImageUniqueFrame(imp); + } + return imp2; + } + /** + * Permet de tirer et inverser les images posterieure pour les images multiframe + * Eviter d'utiliser la methode trierImage(ImagePlus imp) est générique pour tout type d'image + * @param imp0 : ImagePlus a trier + * @return Retourne l'ImagePlus triee + */ + @Deprecated + public static ImagePlus trierImageMultiFrame(ImagePlus imp0) { + + //On duplique pour faire les modifs dans l'image dupliqu锟絜 + ImagePlus imp=imp0.duplicate(); + + //On prend le Header + String metadata=imp.getInfoProperty(); + + //On recupere la chaine de vue + String tag = DicomTools.getTag(imp, "0011,1012"); + + // On recupere la chaine de detecteur + String tagDetecteur = DicomTools.getTag(imp, "0054,0020"); + tagDetecteur=tagDetecteur.substring(1, tagDetecteur.length()-1); + String delims = "[ ]+"; + String[] sequenceDeteceur = tagDetecteur.split(delims); + + ///On recupere le 1er separateur de chaque vue dans le champ des orientation + int separateur=tag.indexOf("\\"); + + //Si on ne trouve pas le separateur, on met la position du separateur � la fin de la string pour tout traiter + if (separateur==-1) separateur=(tag.length()); + + // Si la 1ere image est labelisee anterieure + if (tag.substring(0, separateur).contains("ANT") || tag.substring(0, separateur).contains("_E")) { + //On recupere le num锟絩o du detecteur + int detecteurAnterieur=Integer.parseInt(sequenceDeteceur[0]); + // On parcours la sequence de detecteur et on flip 锟� chaque fois que ce n'est pas le num锟絩o de ce deteceur + for (int j=0; j() { + + @Override + public int compare(ImagePlus arg0, ImagePlus arg1) { + double tag0 = Double.parseDouble(DicomTools.getTag(arg0, "0008,0032")); + double tag1 = Double.parseDouble(DicomTools.getTag(arg1, "0008,0032")); + return (int) (tag0 - tag1); + } + }); + return retour; + } +} +// Fin Vue_Shunpo diff --git a/Vue_VG_Dynamique.java b/Vue_VG_Dynamique.java new file mode 100644 index 00000000..4023f4c7 --- /dev/null +++ b/Vue_VG_Dynamique.java @@ -0,0 +1,416 @@ +/** +Copyright (C) 2017 PING Xie and KANOUN Salim +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public v.3 License as published by +the Free Software Foundation; +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.HashMap; +import java.util.Map.Entry; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; +import ij.*; +import ij.gui.*; +import ij.plugin.Concatenator; +import ij.plugin.HyperStackConverter; +import ij.plugin.PlugIn; +import ij.plugin.StackReverser; +import ij.plugin.ZProjector; +import ij.plugin.frame.RoiManager; +import ij.util.DicomTools; + +public class Vue_VG_Dynamique implements PlugIn { + + public HashMap lesBoutons;// touts les buttons de l'interface + + private Label img_inst; + + protected RoiManager leRoi; + + protected ImagePlus imp; + + private Controleur_VG_Dynamique leControleur = new Controleur_VG_Dynamique(this, new Modele_VG_Dynamique()); + + protected String nomProgramme = "VG Dynamic"; + + private boolean imageOuverte = false;// signifie si les images dynamiques sont ouverts + + protected Label instructions;//pour afficher les instructions + + protected CustomStackWindow windowstack;//la fenetre principale + + protected Overlay overlay; + + private Dimension dimensionPanelPrincipal; + + + @Override + public void run(String arg) { + RoiManager rm = new RoiManager(false); + this.leRoi = rm; + this.instructions = new Label(""); + this.img_inst=new Label(""); + initBoutons(); + IJ.setTool(Toolbar.POLYGON); + + this.lesBoutons.get("Valider").addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e){ + Button b = (Button) e.getSource(); + //si les images dynamiques sont ouvert au fenetre principal et il y a des images sont ouverts et il est pas des images au fenetre principal + //c'est a dire ce qu'on a ouvert est l'image des oeufs + //donc on ouvert l'image des oeufs au fenetre principal + if (WindowManager.getCurrentImage() != null && WindowManager.getCurrentImage() != imp && imageOuverte ) { + ImagePlus imp=WindowManager.getCurrentImage(); + ((Frame) b.getParent().getParent()).dispose(); + ouvertureImageOeuf(imp); + lesBoutons.get("Suivant").setEnabled(true); + } + //si les images dynamiques sont pas ouvert au fenetre principal et il y a des images sont ouverts + //donc on ouvert les images dynamiques au fenetre principal + if (WindowManager.getCurrentImage() != null && !imageOuverte ) { + imageOuverte = true; + ((Frame) b.getParent().getParent()).dispose(); + ouvertureImage(); + } + }; + }); + + if (!this.imageOuverte) + ouvrirImage("all the acquisitions images of Stomachs-Intestines"); + } + + private void initBoutons() { + + this.lesBoutons = new HashMap<>(); + this.lesBoutons.put("Show", new Button("Show MG%")); + this.lesBoutons.put("Draw ROI", new Button(" Draw ROI ")); + this.lesBoutons.put("Quitter", new Button(" Quit")); + this.lesBoutons.put("Contrast", new Button(" Contrast ")); + this.lesBoutons.put("Precedent", new Button(" Previous ")); + this.lesBoutons.put("Suivant", new Button(" Next ")); + this.lesBoutons.put("Valider", new Button(" Confirm ")); + + } + + public void setInstructions(String inst) { + this.instructions.setText(inst); + } + + //permet de demander a l'utilisateur de ouvrir les images + public void ouvrirImage(String image) { + Frame f = new Frame(); + Panel pan = new Panel(); + pan.setLayout(new GridLayout(2, 1)); + this.img_inst.setText("Please open " + image + " then confirm."); + pan.add(this.img_inst); + pan.add(this.lesBoutons.get("Valider")); + f.add(pan); + f.setLocationRelativeTo(null); + Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); + f.setSize(200, 300); + f.setLocation(dim.width / 2 - f.getSize().width / 2, dim.height / 2 - f.getSize().height / 2); + f.setVisible(true); + f.setResizable(false); + f.setAlwaysOnTop(true); + f.pack(); + f.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent we) { + leRoi.close(); + f.dispose(); + System.gc(); + } + }); + if (this.imageOuverte==true){ + this.lesBoutons.get("Suivant").setEnabled(false); + } + } + + //permet de mettre les images dynamiques au fenetre principal + private void ouvertureImage() { + String[] imagesOuvertes =WindowManager.getImageTitles(); + ImagePlus[] projete=new ImagePlus[imagesOuvertes.length]; + + for (int i=0 ; i equivalent au setslice mais moins de bug + this.imp=imp; + //On change les titres + imp.setTitle(titre); + windowstack.setTitle(titre); + initOverlay(); + // La fenetre se place au premier plan + windowstack.toFront(); + //On fixe la taille de la fenetre et on redimensionne le cc + //Ajoute 30 et 90 pixel + windowstack.setSize(512+30, 512+dimensionPanelPrincipal.height+90); + windowstack.repaint(); + ImageCanvas ic=windowstack.getCanvas(); + ic.fitToWindow(); + windowstack.toFront(); + this.imageOuverte=true; + if (this.instructions.getText().equals("")) + this.instructions.setText("Delimit the Stomache"); + lesBoutons.get("Precedent").setEnabled(false); + windowstack.getImagePlus().setOverlay(this.overlay); + } + + //permet de mettre l'image des oeufs au fenetre principal + private void ouvertureImageOeuf(ImagePlus imp) { + ImagePlus oeufs=(ImagePlus) imp.clone(); + //on remplace l'image des oeufs au fenetre + windowstack.setImage(oeufs); + String serie = DicomTools.getTag(imp, "0008,103E"); + String patient = DicomTools.getTag(imp, "0010,0010"); + imp.getWindow().close(); + String titre = this.nomProgramme + " - " + patient + " - " + serie; + oeufs.setTitle(titre); + windowstack.setTitle(titre); + ImageCanvas cc=windowstack.getCanvas(); + this.imp=oeufs; + cc.fitToWindow(); + windowstack.toFront(); + windowstack.getImagePlus().deleteRoi(); + ActionEvent e = new ActionEvent(lesBoutons.get("Suivant"),ActionEvent.ACTION_PERFORMED,"Suivant"); + leControleur.actionPerformed(e); + } + + // Extraite du code de la classe Panel_Window de Wayne Rasband + // Source : https://imagej.nih.gov/ij/plugins/panel-window.html + class CustomCanvas extends ImageCanvas { + + private static final long serialVersionUID = -5710708795558320699L; + + CustomCanvas(ImagePlus imp) { + + super(imp); + + } // Fin constructeur CustomCanvas + + } // Fin CustomCanvas + + // Extraite puis modifiee du code de la classe Panel_Window de Wayne Rasband + // Source : https://imagej.nih.gov/ij/plugins/panel-window.html + class CustomStackWindow extends StackWindow { + + private static final long serialVersionUID = -6280620624574294247L; + + CustomStackWindow(ImagePlus imp) { + super(imp, new CustomCanvas(imp)); + addPanel(); + } // Fin constructeur CustomStackWindow + + // Construction de l'interface + void addPanel() { + // Agencement des composants + Panel panel = new Panel(); + panel.setLayout(new FlowLayout()); + addEcouteurs(); + + // Partie gauche + Panel gauche = new Panel(); + gauche.setLayout(new FlowLayout()); + + // Premiers boutons + Panel btns_glob = new Panel(); + btns_glob.setLayout(new GridLayout(1, 3)); + btns_glob.add(lesBoutons.get("Quitter")); + btns_glob.add(lesBoutons.get("Draw ROI")); + btns_glob.add(lesBoutons.get("Contrast")); + gauche.add(btns_glob); + + // Instructions + Panel instru = new Panel(); + instru.setLayout(new GridLayout(2, 1)); + instru.add(instructions); + Panel btns_instru = new Panel(); + btns_instru.setLayout(new GridLayout(1, 3)); + btns_instru.add(lesBoutons.get("Show")); + btns_instru.add(lesBoutons.get("Precedent")); + lesBoutons.get("Precedent").setEnabled(false); + btns_instru.add(lesBoutons.get("Suivant")); + instru.add(btns_instru); + instru.setBackground(Color.LIGHT_GRAY); + gauche.add(instru); + panel.add(gauche); + add(panel); + pack(); + dimensionPanelPrincipal=panel.getSize(); + + // Permet d'avoir la fenetre au meme endroit que le stack original + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + Point loc = getLocation(); + Dimension size = getSize(); + if (loc.y + size.height > screen.height) + getCanvas().zoomOut(0, 0); + } // Fin addPanel + + private void addEcouteurs() { + for (Entry entry : lesBoutons.entrySet()) + entry.getValue().addActionListener(leControleur); + } + + @Override + public void windowClosing(WindowEvent we) { + //On ferme le ROI manager en plus de la fenetre + leRoi.close(); + windowstack.close(); + System.gc(); + } + + } // Fin CustomStackWindow + + + // fenetre de fermeture du programme + public void end() { + int optionChoisie = JOptionPane.showConfirmDialog(null, "The program will now shut down", "", + JOptionPane.OK_CANCEL_OPTION); + if (optionChoisie == JOptionPane.OK_OPTION) { + windowstack.close(); + leRoi.close(); + } + } + + public void addOverlayGD() { + // Ajout de l'indication de la droite du patient + double xr = 10; + double y = imp.getCanvas().getHeight() / 2; + TextRoi right = new TextRoi(xr, y, ""); + overlay.add(right, "Right"); + // Ajout de la gauche du patient -20 pixel pour pas sortir de l'image + double xl = imp.getCanvas().getWidth() - 20; + TextRoi left = new TextRoi(xl, y, ""); + overlay.add(left, "Left"); + } + + //Initialisation de l'Overlay + protected void initOverlay() { + this.overlay = new Overlay(); + this.overlay.drawLabels(true); + this.overlay.drawNames(true); + this.overlay.setLabelFont(new Font("Arial", Font.PLAIN, 18)); + this.addOverlayGD(); + } + + // Dialog pour afficher les % de chaque oeuf et les modifier si necessaire + public void confirmerOeufPourc() { + JDialog dialog = new JDialog(); + dialog.setLayout(new GridLayout(2, 1)); + JPanel panelOeufPourc=new JPanel(); + panelOeufPourc.setLayout(new GridLayout(Controleur_VG_Dynamique.oeufsIngere/ 2, 2)); + JTextField[] textOeufPourc=new JTextField[Controleur_VG_Dynamique.oeufsIngere]; + for(int i=0; i lesBoutons;// touts les buttons de l'interface + + protected boolean imageOuverte = false;// signifie si les images sont ouverts + + private Label img_inst; + + protected RoiManager leRoi; + + protected ImagePlus imp; + + protected static String timeStart;//l'horaire où le patient commence a manger + + private Controleur_VG_Roi leControleur = new Controleur_VG_Roi(this, new Modele_VG_Roi()); + + private JTable tabRes;// le tableau pour afficher le pourcentage de chaque organe (une partie de resultat) + + private JPanel infoRes;//le panel pour afficher les resultats + + protected String nomProgramme = "Gastric Emptying"; + + protected Label instructions;//pour afficher les instructions + + protected CustomStackWindow windowstack;//la fenetre principale + + protected CustomWindow res;//la fenetre du resultat + + private Dimension dimensionPanelResultat; + + private Dimension dimensionPanelPrincipal; + + protected Overlay overlay; + + protected static boolean estArgume;//signifie si il y a des arguments pour ce programme + + protected static String[] resultatsDynamique;//resultats du programme VG_Ingestion_Dynamique transmettant en argument + + protected Label csv = new Label(); + + @Override + public void run(String arg) { + String argumentString = Macro.getOptions(); + estArgume = (argumentString != null); + // si il y a des arguments recupere de VG_Ingestion_Dynamique, passe le permier + // argument au timestart, pas les autres arguments des resultats + if (estArgume) { + String[] argumentTab = argumentString.split("\\;"); + timeStart = argumentTab[0]; + // on prend pas le dernier argument car il est null, donc on prend + // du deuxieme a l'avant dernier + resultatsDynamique = new String[argumentTab.length - 2]; + for (int i = 1; i < argumentTab.length - 1; i++) { + resultatsDynamique[i - 1] = argumentTab[i]; + } + } else { + //sinon on initialise resultatsDynamique + resultatsDynamique = new String[4]; + resultatsDynamique[0] = "0"; + resultatsDynamique[1] = "100.0"; + resultatsDynamique[2] = "100.0"; + resultatsDynamique[3] = "0.0"; + } + + // Initialisation des differents attributs + RoiManager rm = new RoiManager(false); + leRoi = rm; + instructions = new Label(""); + img_inst = new Label(); + initBoutons(); + ouvrirImage("all the acquisitions images of Stomachs-Intestines"); + + lesBoutons.get("Valider").addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent arg0) { + Button b = (Button) arg0.getSource(); + if (WindowManager.getCurrentImage() != null) { + imageOuverte = true; + ((Frame) b.getParent().getParent()).dispose(); + ouvertureImage(); + } + } + + }); + + } + + private void initBoutons() { + + lesBoutons = new HashMap<>(); + lesBoutons.put("Show", new Button("Show MG%")); + lesBoutons.put("Draw ROI", new Button(" Draw ROI ")); + lesBoutons.put("Quitter", new Button(" Quit")); + lesBoutons.put("Contrast", new Button(" Contrast ")); + lesBoutons.put("Precedent", new Button(" Previous ")); + lesBoutons.put("Suivant", new Button(" Next ")); + lesBoutons.put("Valider", new Button(" Confirm ")); + lesBoutons.put("Sauvegarder", new Button("Save Results")); + lesBoutons.put("Return", new Button("Return to adjust ROIs")); + } + + public void setInstructions(String inst) { + instructions.setText(inst); + } + + // Extraite du code de la classe Panel_Window de Wayne Rasband + // Source : https://imagej.nih.gov/ij/plugins/panel-window.html + class CustomCanvas extends ImageCanvas { + + private static final long serialVersionUID = -5710708795558320699L; + + CustomCanvas(ImagePlus imp) { + + super(imp); + + } // Fin constructeur CustomCanvas + + } // Fin CustomCanvas + + // Extraite puis modifiee du code de la classe Panel_Window de Wayne Rasband + // Source : https://imagej.nih.gov/ij/plugins/panel-window.html + // Cette classe permet d'avoir une image et des elements graphiques sur une + // fenêtre + // On l'utilise pour la fenêtre de resultats + class CustomWindow extends ImageWindow { + + private static final long serialVersionUID = -9097595151860174657L; + + CustomWindow(ImagePlus imp) { + super(imp, new CustomCanvas(imp)); + //setLayout(new GridLayout(1,2)); + setLayout(new FlowLayout()); + addPanel(); + } // Fin constructeur CustomWindow + + // Permet d'ajouter les boutons a la fenêtre + private void addPanel() { + + // Agencement des composants + Panel panel = new Panel(); + panel.setLayout(new BorderLayout()); + + Panel panelResultats = new Panel(); + panelResultats.setLayout(new FlowLayout()); + panelResultats.add(infoRes); + panelResultats.add(tabRes); + + Panel panelNonResultats = new Panel(); + panelNonResultats.setLayout(new GridLayout(2, 1)); + + // On test la presence d'un repertoire CSV defini + String path = Prefs.get("dir.preferred", null); + if (path == null) { + csv.setText(" No CSV output"); + csv.setForeground(Color.RED); + } else { + csv.setText(" CSV Save OK"); + } + panelNonResultats.add(csv); + + Panel panelButtons = new Panel(); + panelButtons.setLayout(new FlowLayout()); + + panelButtons.add(lesBoutons.get("Return")); + panelButtons.add(lesBoutons.get("Sauvegarder")); + panelNonResultats.add(panelButtons); + + panel.add(panelResultats, BorderLayout.CENTER); + panel.add(panelNonResultats, BorderLayout.SOUTH); + add(panel); + pack(); + dimensionPanelResultat=panel.getSize(); + + } // Fin addPanel + + } // Fin CustomWindow + + // Extraite puis modifiee du code de la classe Panel_Window de Wayne Rasband + // Source : https://imagej.nih.gov/ij/plugins/panel-window.html + // Cette classe permet d'avoir une pile d'images et des elements graphiques + // sur une fenêtre + class CustomStackWindow extends StackWindow { + + private static final long serialVersionUID = -6280620624574294247L; + + CustomStackWindow(ImagePlus imp) { + super(imp, new CustomCanvas(imp)); + addPanel(); + } // Fin constructeur CustomStackWindow + + // Construction de l'interface + void addPanel() { + // Agencement des composants + Panel panel = new Panel(); + panel.setLayout(new FlowLayout()); + addEcouteurs(); + + // Partie gauche + Panel gauche = new Panel(); + gauche.setLayout(new FlowLayout()); + + // Premiers boutons + Panel btns_glob = new Panel(); + btns_glob.setLayout(new GridLayout(1, 3)); + btns_glob.add(lesBoutons.get("Quitter")); + btns_glob.add(lesBoutons.get("Draw ROI")); + btns_glob.add(lesBoutons.get("Contrast")); + gauche.add(btns_glob); + + // Instructions + Panel instru = new Panel(); + instru.setLayout(new GridLayout(2, 1)); + instru.add(instructions); + Panel btns_instru = new Panel(); + btns_instru.setLayout(new GridLayout(1, 3)); + btns_instru.add(lesBoutons.get("Show")); + btns_instru.add(lesBoutons.get("Precedent")); + lesBoutons.get("Precedent").setEnabled(false); + btns_instru.add(lesBoutons.get("Suivant")); + instru.add(btns_instru); + instru.setBackground(Color.LIGHT_GRAY); + gauche.add(instru); + panel.add(gauche); + add(panel); + pack(); + dimensionPanelPrincipal=panel.getSize(); + + // Permet d'avoir la fenêtre ouverte au même endroit que l'image + // selectionnee par l'utilisateur + Dimension screen = Toolkit.getDefaultToolkit().getScreenSize(); + Point loc = getLocation(); + Dimension size = getSize(); + if (loc.y + size.height > screen.height) + getCanvas().zoomOut(0, 0); + } // Fin addPanel + + private void addEcouteurs() { + for (Entry entry : lesBoutons.entrySet()) + entry.getValue().addActionListener(leControleur); + } + + public void windowClosing(WindowEvent we) { + // On ferme le ROI manager en plus de la fenetre + leRoi.close(); + windowstack.close(); + System.gc(); + } + + } // Fin CustomStackWindow + + protected void ouvrirImage(String image) { + Frame f = new Frame(); + Panel pan = new Panel(); + pan.setLayout(new GridLayout(2, 1)); + img_inst.setText("Please open " + image + " then confirm."); + pan.add(img_inst, CENTER_ALIGNMENT); + pan.add(lesBoutons.get("Valider")); + f.add(pan); + f.setLocationRelativeTo(null); + Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); + f.setSize(200, 300); + f.setLocation(dim.width / 2 - f.getSize().width / 2, dim.height / 2 - f.getSize().height / 2); + f.setVisible(true); + f.setResizable(false); + f.setAlwaysOnTop(true); + f.toFront(); + f.pack(); + f.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent we) { + end("dialog"); + f.dispose(); + } + }); + } + + private void ouvertureImage() { + + // Recupation de une serie des images + String[] imageTitles = WindowManager.getImageTitles(); + ImagePlus[] imagesOpened = new ImagePlus[imageTitles.length]; + for (int i = 0; i < imageTitles.length; i++) { + ImagePlus currentimp = WindowManager.getImage(imageTitles[i]); + // On verifie qu'il y a 2 images par stack + if (currentimp.getStackSize() != 2) { + IJ.log("Error is Image Ant/Post ? if it is Notify Salim.Kanoun@gmail.com"); + currentimp.close(); + continue; + } + // On trie les images + imagesOpened[i] = Vue_Shunpo.trierImage(currentimp); + currentimp.close(); + } + + // trie les images de la serie + ImagePlus[] imagesOrdred = Vue_Shunpo.ordonnerSerie(imagesOpened); + Concatenator enchainer = new Concatenator(); + + // enchaine les images + ImagePlus imp = enchainer.concatenate(imagesOrdred, false); + imp.show(); + HyperStackConverter convert = new HyperStackConverter(); + convert.run("hstostack"); + + String serie = DicomTools.getTag(imp, "0008,103E"); + String titre = nomProgramme + " - "; + String tag = DicomTools.getTag(imp, "0010,0010"); + titre = titre + tag + " - " + serie; + // met la LUT preferee si existe + Vue_Shunpo.setCustomLut(imp); + // Son cree une fenetre pour la pile d'images + windowstack = new CustomStackWindow(imp); + // On demande la 1ere image du stack + windowstack.showSlice(1); + this.imp = imp; + // On change les titres + imp.setTitle(titre); + windowstack.setTitle(titre); + initOverlay(); + addOverlayGD(); + windowstack.getImagePlus().setOverlay(overlay); + // On fixe la taille de la fenetre et on redimensionne le cc + // Pour la taille de la fenetre on ajoute 30 pixel en largeur et 90 en hauteur pour les bordures de fenetres + windowstack.setSize(512 + 30, (512 + dimensionPanelPrincipal.height +90)); + windowstack.repaint(); + windowstack.getCanvas().fitToWindow(); + // La fenêtre se place au premier plan + windowstack.toFront(); + IJ.setTool(Toolbar.POLYGON); + if (!estArgume) { + String acquisitionTimeAP1 = DicomTools.getTag(windowstack.getImagePlus(), "0008,0032"); + lesBoutons.get("Suivant").setEnabled(false); + // saisie le temps de commence + saisiTempsCommence(acquisitionTimeAP1); + } + + if (instructions.getText().equals("")) + instructions.setText("Delimit the Stomache"); + } + + // permet de saisir le temps de commence + private void saisiTempsCommence(String timeAP1) { + JDialog dialog = new JDialog(); + dialog.setLayout(new GridLayout(3, 1)); + + // ajoute un panel pour afficher le temps de la premierer image et + // l'information de demande + JPanel panelTemPremier = new JPanel(); + panelTemPremier.setLayout(new GridLayout(2, 1)); + JLabel labelTempsPremier = new JLabel("The acquisition time of first image is " + timeAP1.substring(1, 3) + + " h " + timeAP1.substring(3, 5) + " m " + timeAP1.substring(5, 7) + " s."); + JLabel labelAlerte = new JLabel("please input the time of beginning!"); + panelTemPremier.add(labelTempsPremier); + panelTemPremier.add(labelAlerte); + dialog.add(panelTemPremier); + + // ajoute un panel du temps + JPanel panelTemps = new JPanel(); + panelTemps.setLayout(new GridLayout(1, 6)); + panelTemps.setBorder(new EmptyBorder(5, 5, 5, 5)); + // ajoute un label et un ComboBox de l'heure au panel du temps + JLabel labelHeure = new JLabel("heure:"); + panelTemps.add(labelHeure); + JComboBox comboBoxHeure = addComboBox(24); + panelTemps.add(comboBoxHeure); + // ajoute un label et un ComboBox de la minute au panel du temps + JLabel labelMinu = new JLabel("minute:"); + panelTemps.add(labelMinu); + JComboBox comboBoxMinu = addComboBox(60); + panelTemps.add(comboBoxMinu); + // ajoute un label et un ComboBox de la seconde au panel du temps + JLabel labelSec = new JLabel("second:"); + panelTemps.add(labelSec); + JComboBox comboBoxSec = addComboBox(60); + panelTemps.add(comboBoxSec); + dialog.add(panelTemps); + + // ajoute un panel du valide + JPanel panelValide = new JPanel(); + panelValide.setLayout(null); + // ajoute un button au panel du valide + JButton buttonOK = new JButton("OK"); + panelValide.add(buttonOK); + buttonOK.setBounds(120, 10, 60, 30); + //permet de recupere les valeurs qu'on saisie au comboBox + buttonOK.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent arg0) { + timeStart = (String) comboBoxHeure.getSelectedItem(); + timeStart += (String) comboBoxMinu.getSelectedItem(); + timeStart += (String) comboBoxSec.getSelectedItem(); + dialog.dispose(); + lesBoutons.get("Suivant").setEnabled(true); + } + }); + dialog.add(panelValide); + dialog.setTitle("Please input the time of beginning"); + dialog.setVisible(true); + dialog.setAlwaysOnTop(true); + dialog.setSize(330, 170); + dialog.setLocationRelativeTo(windowstack); + dialog.requestFocus(); + } + + private JComboBox addComboBox(int number) { + JComboBox comboBox = new JComboBox(); + String[] valeurs = new String[number]; + for (int i = 0; i < number; i++) { + if (i < 10) { + valeurs[i] = "0" + i; + } else { + valeurs[i] = "" + i; + } + } + for (int i = 0; i < valeurs.length; i++) { + comboBox.addItem(valeurs[i]); + } + return comboBox; + } + + // Interface graphique pour resultats + public void UIResultats(ImagePlus screen) { + ImageProcessor ip=screen.getProcessor(); + ip.setInterpolate(true); + ip.setInterpolationMethod(ImageProcessor.BICUBIC); + ip=screen.getProcessor().resize((int) (screen.getWidth()*1.5)); + ImagePlus screen2=new ImagePlus("Final Capture",ip); + // On cree la fenetre resultat avec le panel resultat + res = new CustomWindow(screen2); + res.setLocation(new Point(20, 20)); + // On prend le focuse et on rescale le tout + ImageCanvas ic = res.getCanvas(); + ic.hideZoomIndicator(true); + ic.setScaleToFit(true); + //On resize en laissant la place pour le tableau de resultat en largeur + res.setSize(screen2.getWidth()+dimensionPanelResultat.width+30, screen2.getHeight()+60); + // On implemente le titre de la fenetre + res.setTitle(nomProgramme + " - Results"); + res.setVisible(true); + // On ferme l'outil ROI + IJ.setTool("hand"); + } + + // Fenêtre de fermeture du programme + public void end(String dialog) { + int optionChoisie = JOptionPane.showConfirmDialog(null, "The program will now shut down", "", + JOptionPane.OK_CANCEL_OPTION); + if (optionChoisie == JOptionPane.OK_OPTION) { + if (dialog == null) { + imp.close(); + leRoi.close(); + } + if (dialog == "dialog") { + leRoi.close(); + } + } + } + //les resultats consistent a deux parties, la premiere est le pourcentage des MGs pour chaque serie, + //la deuxieme est les informations bases(nom du patient, date...) et les autres informations( debut de l'antre, retention a 1h...) + // Remplis le table qui affiche la premiere partie des resultats + public void tablesResultats(String[] resultats) { + //on cree un tableau avec 4 colonnes + tabRes = new JTable(0, 4); + DefaultTableModel tableModel = (DefaultTableModel) tabRes.getModel(); + String[] arr = new String[tableModel.getColumnCount()]; + //le nombre de ligne concerne a nombre du serie statique + nombre du serie dynamique + 1, cette ligne pour afficher les titres + for (int i = 0; i < (imp.getStackSize() / 2 + resultatsDynamique.length / 4 + 1); i++) { + for (int j = 0; j < tableModel.getColumnCount(); j++) { + arr[j] = resultats[i * tableModel.getColumnCount() + j]; + } + tableModel.insertRow(i, arr); + } + tabRes.setRowHeight(30); + MatteBorder border = new MatteBorder(1, 1, 1, 1, Color.BLACK); + tabRes.setBorder(border); + } + + // Remplis le panel qui affiche la deuxieme partie des resultats + public void infoResultats(String[] resultats) { + infoRes = new JPanel(); + infoRes.setLayout(new GridLayout(14, 1)); + //la deuxime partir du resultats contient 13 ligne + for (int i = ((imp.getStackSize() / 2 + resultatsDynamique.length / 4 + 1) * 4); i < resultats.length; i++) { + infoRes.add(new Label(resultats[i])); + // on ajoute deux lignes vides pour separer les informations + // bases(nom du patient, date...) et les autres informations + if (i == (resultats.length - 9)) { + infoRes.add(new Label(" ")); + infoRes.add(new Label(" ")); + + } + } + } + + public void setNonVisibleButtons() { + lesBoutons.get("Sauvegarder").setVisible(false); + lesBoutons.get("Return").setVisible(false); + } + + // Ajout overlayGD + protected void addOverlayGD() { + // Creer overlay Droit et gauche + Font font = new Font("Arial", Font.PLAIN, 19); + overlay.setLabelFont(font); + // Ajout de l'indication de la droite du patient + double xr = 10; + double y = (windowstack.getImagePlus().getHeight()) / 2; + TextRoi right = new TextRoi(xr, y, ""); + overlay.add(right, "Right"); + // Ajout de la gauche du patient + double xl = windowstack.getImagePlus().getWidth() - 20; + TextRoi left = new TextRoi(xl, y, ""); + overlay.add(left, "Left"); + } + + // Cree l'overlay et defini la police et la taille + protected void initOverlay() { + overlay = new Overlay(); + overlay.drawLabels(true); + overlay.drawNames(true); + overlay.setLabelFont(new Font("Arial", Font.PLAIN, 18)); + addOverlayGD(); + } + +} // Fin Vue_Shunpo