diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3d47f986c41db29ec6dc0d5036bf760b3a1cf366 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.idea/ +target/ +.settings/ +*.iml +.project +.classpath \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..b8ffa4f9cc71d9646a8e0ed9a2e1ce07cbc9c756 --- /dev/null +++ b/pom.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <!-- Inherited Icy Parent POM --> + <parent> + <groupId>org.bioimageanalysis.icy</groupId> + <artifactId>parent-pom-plugin</artifactId> + <version>1.0.3</version> + </parent> + + <!-- Project Information --> + <artifactId>image-to-workbook</artifactId> + <version>2.5.3</version> + + <packaging>jar</packaging> + + <name>Image to Workbook</name> + <description>Convert a 2D Image to workbook</description> + <url>http://icy.bioimageanalysis.org/plugin/image-to-workbook/</url> + <inceptionYear>2020</inceptionYear> + + <organization> + <name>Institut Pasteur</name> + <url>https://pasteur.fr</url> + </organization> + + <licenses> + <license> + <name>GNU GPLv3</name> + <url>https://www.gnu.org/licenses/gpl-3.0.en.html</url> + <distribution>repo</distribution> + </license> + </licenses> + + <developers> + <developer> + <id>sdallongeville</id> + <name>Stéphane Dallongeville</name> + <url>https://research.pasteur.fr/fr/member/stephane-dallongeville/</url> + <roles> + <role>founder</role> + <role>lead</role> + <role>architect</role> + <role>developer</role> + <role>debugger</role> + <role>tester</role> + <role>maintainer</role> + <role>support</role> + </roles> + </developer> + </developers> + + <!-- Project properties --> + <properties> + + </properties> + + <!-- Project build configuration --> + <build> + + </build> + + <!-- List of project's dependencies --> + <dependencies> + <!-- The core of Icy --> + <dependency> + <groupId>org.bioimageanalysis.icy</groupId> + <artifactId>icy-kernel</artifactId> + </dependency> + + <dependency> + <groupId>org.bioimageanalysis.icy</groupId> + <artifactId>workbooks</artifactId> + <version>3.4.10</version> + </dependency> + </dependencies> + + <!-- Icy Maven repository (to find parent POM) --> + <repositories> + <repository> + <id>icy</id> + <name>Icy's Nexus</name> + <url>https://icy-nexus.pasteur.fr/repository/Icy/</url> + </repository> + </repositories> +</project> \ No newline at end of file diff --git a/src/main/java/plugins/worm/image2workbook/Image2Workbook.java b/src/main/java/plugins/worm/image2workbook/Image2Workbook.java new file mode 100644 index 0000000000000000000000000000000000000000..0585b378844070f3b0d6b2f5ef7f107e9e0960d9 --- /dev/null +++ b/src/main/java/plugins/worm/image2workbook/Image2Workbook.java @@ -0,0 +1,464 @@ +package plugins.worm.image2workbook; + +import java.awt.BasicStroke; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; + +import javax.swing.JComponent; +import javax.swing.JLayeredPane; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.jdesktop.swingx.JXButton; + +import icy.canvas.IcyCanvas; +import icy.file.FileUtil; +import icy.gui.dialog.MessageDialog; +import icy.gui.dialog.SaveDialog; +import icy.gui.frame.IcyFrame; +import icy.gui.frame.progress.ToolTipFrame; +import icy.gui.viewer.Viewer; +import icy.gui.viewer.ViewerEvent; +import icy.gui.viewer.ViewerEvent.ViewerEventType; +import icy.gui.viewer.ViewerListener; +import icy.image.IcyBufferedImage; +import icy.painter.Overlay; +import icy.plugin.abstract_.PluginActionable; +import icy.sequence.Sequence; +import icy.sequence.SequenceEvent; +import icy.sequence.SequenceListener; +import icy.system.IcyHandledException; +import icy.type.point.Point5D; +import plugins.adufour.vars.gui.swing.WorkbookEditor; +import plugins.adufour.vars.lang.VarWorkbook; + +public class Image2Workbook extends PluginActionable +{ + public static final HashMap<Viewer, MagnifierOverlay> magnifiers = new HashMap<Viewer, Image2Workbook.MagnifierOverlay>(); + // squence chooser define + // SequenceChooser sq = new SequenceChooser( true, "Input Sequence"); + Sequence sequence; + // workbook define + VarWorkbook wb = new VarWorkbook("workbook", "Result"); + WorkbookEditor ed = new WorkbookEditor(wb); + // save button define + JXButton buttonSave = new JXButton("Save workbook as..."); + IcyFrame mainFrame = new IcyFrame("Image2Workbook Result"); + + @Override + public void run() + { + Viewer viewer = getActiveViewer(); + + if (viewer == null) + throw new IcyHandledException("Open an image before using the magnifier"); + + if (magnifiers.containsKey(viewer)) + return; + + if (viewer.getSequence() == null) + throw new IcyHandledException("The active viewer contains no sequence"); + + magnifiers.put(viewer, new MagnifierOverlay(viewer, viewer.getSequence().getSizeC() - 1, 4, wb, mainFrame)); + + ed.setEnabled(true); + // sq.setActiveSequenceSelected(); + // main editor + + mainFrame.setResizable(true); + mainFrame.setLayout(new BorderLayout()); + // north panel + // JXPanel NorthPanel = new JXPanel(); + // //sq.addActionListener(new ActionListener() + // { + // + // @Override + // public void actionPerformed(ActionEvent e) { + // sequence = sq.getSelectedSequence(); + // //ImageToWorkbook(sequence,wb); + // } + // }); + + // NorthPanel.add(sq); + // South panel + // JXPanel SouthPanel = new JXPanel(); + // buttonSave.addActionListener(new ActionListener() + // { + // @Override + // public void actionPerformed(ActionEvent e) + // { + // String path = SaveDialog.chooseFile("Save workbook as...", null, "Workbook", ".xls"); + // if (path == null) return; + // + // try + // { + // FileOutputStream fos = new FileOutputStream(path); + // wb.getValue().write(fos); + // fos.close(); + // } + // catch (IOException e1) + // { + // MessageDialog.showDialog(e1.getMessage(), MessageDialog.ERROR_MESSAGE); + // } + // } + // + // + // }); + // SouthPanel.add(buttonSave); + // show the workbook + JComponent cp = ed.getEditorComponent(); + cp.setPreferredSize(new Dimension(600, 600)); + mainFrame.add(cp, BorderLayout.CENTER); + // show the button and squencechooser + // mainFrame.add(NorthPanel, BorderLayout.NORTH); + // mainFrame.add(SouthPanel,BorderLayout.NORTH); + // Pack and show the window + mainFrame.pack(); + mainFrame.addToDesktopPane(JLayeredPane.DEFAULT_LAYER); + mainFrame.setVisible(true); + + // } + } + + private static final class MagnifierOverlay extends Overlay implements SequenceListener, ViewerListener + { + private final Sequence sequence; + private final ToolTipFrame frame = new ToolTipFrame( + "<b>Image2Workbook instructions:</b><br/><ul><li>Press 'c' to toggle between channels</li>" + + "<li>Press 'a' to save the sequence data</li>" + + "<li>Press 's' to save current image data</li>" + "<li>Press 'q' to close</li>" + + "<li>Press 'space' to toggle the workbook refresh mode</li></ul>"); + + float currentPosX, currentPosY; + int currentChannel = 0; + + private int scale; + AffineTransform xForm = new AffineTransform(); + + int radius = 50; + int diameter = radius * 2; + GradientPaint paintBlack = new GradientPaint(0, 0, Color.black, radius, radius, Color.darkGray.darker(), true); + GradientPaint paintGreen = new GradientPaint(0, 0, Color.green, radius, radius, Color.lightGray.darker(), true); + + Rectangle2D.Float lens = new Rectangle2D.Float(); + final Viewer viewer; + Boolean RefreshWorkbookMode = true; + VarWorkbook wb; + IcyFrame mainFrame; + + public MagnifierOverlay(Viewer viewer, int initialChannel, int magnifierScale, VarWorkbook w, IcyFrame frame) + { + super("Magnifier"); + wb = w; + mainFrame = frame; + this.viewer = viewer; + this.sequence = viewer.getSequence(); + this.currentChannel = initialChannel; + sequence.addOverlay(this); + radius = Math.min(Math.min(sequence.getSizeX() / 2, sequence.getSizeY()) / 2, 50); + diameter = radius * 2; + paintBlack = new GradientPaint(0, 0, Color.black, radius, radius, Color.darkGray.darker(), true); + paintGreen = new GradientPaint(0, 0, Color.green, radius, radius, Color.lightGray.darker(), true); + + sequence.addListener(this); + viewer.addListener(this); + setScale(magnifierScale); + } + + private void setScale(int scale) + { + this.scale = scale; + new Font("Tahoma", Font.PLAIN, scale).deriveFont(scale * 0.1f); + } + + private void updateImageCache() + { + + currentChannel = Math.min(currentChannel, sequence.getSizeC()); + + } + + @Override + public void paint(Graphics2D g, Sequence sequence, IcyCanvas canvas) + { + // don't paint on other viewers + if (viewer.getCanvas() != canvas) + return; + boolean displayValues = RefreshWorkbookMode;// currentZoom >= 2f; + + float xo = currentPosX - radius; + float yo = currentPosY - radius; + + // canvas.getScaleX(); + + // draw the lens + Graphics2D graphics = (Graphics2D) g.create(); + graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); + + if (displayValues) + graphics.setPaint(paintGreen); + else + graphics.setPaint(paintBlack); + + if (radius >= 5) + graphics.setStroke(new BasicStroke((float) 1)); + else + graphics.setStroke(new BasicStroke((float) 0.1)); + + graphics.draw(lens); + + // draw the "zoomed" image inside the lens + graphics.clip(lens); + // graphics.translate(xForm.getTranslateX(), xForm.getTranslateY()); + // graphics.scale(scale, scale); + // canvas.getImageLayer().getOverlay().paint(graphics, sequence, canvas); + + graphics.dispose(); + // draw pixel values + + if (displayValues) + { + IcyBufferedImage im = canvas.getCurrentImage(); + Sheet sheet = wb.getValue().getSheetAt(0); + + // int k=0;c + for (int j = (int) yo; j < yo + diameter; j++) + { + Row row = sheet.createRow((int) (j - yo)); + + // int m=0; + for (int i = (int) xo; i < xo + diameter; i++) + { + Cell cell = row.createCell((int) (i - xo)); + + if (im.getBounds().contains(i, j)) + cell.setCellValue(im.getData(i, j, currentChannel)); + else + cell.setCellValue(0); + } + } + } + } + + @Override + public void mouseMove(MouseEvent e, Point5D.Double imagePoint, IcyCanvas canvas) + { + boolean displayValues = RefreshWorkbookMode; + if (!displayValues) + return; + currentPosX = (float) imagePoint.getX(); + currentPosY = (float) imagePoint.getY(); + + if (currentPosX < radius) + currentPosX = radius; + if (currentPosY < radius) + currentPosY = radius; + if (currentPosX > (viewer.getSequence().getSizeX() + radius)) + currentPosX = viewer.getSequence().getSizeX() + radius; + if (currentPosY > (viewer.getSequence().getSizeY() + radius)) + currentPosY = viewer.getSequence().getSizeY() + radius; + radius = Math.min(Math.min(sequence.getSizeX() / 2, sequence.getSizeY() / 2), 50); + diameter = radius * 2; + paintBlack = new GradientPaint(0, 0, Color.black, radius, radius, Color.darkGray.darker(), true); + paintGreen = new GradientPaint(0, 0, Color.green, radius, radius, Color.lightGray.darker(), true); + + xForm.setToTranslation(-currentPosX * scale + currentPosX, -currentPosY * scale + currentPosY); + lens.setFrame(currentPosX - radius, currentPosY - radius, diameter, diameter); + + mainFrame.setTitle(sequence.getName() + " @Channel " + new Integer(currentChannel).toString() + + " Location @ " + '(' + new Double(Math.round((double) (currentPosX - radius))).toString() + " " + + ',' + " " + new Double(Math.round((double) (currentPosY - radius))).toString() + ')'); + canvas.repaint(); + mainFrame.repaint(); + + super.mouseMove(e, imagePoint, canvas); + } + + @Override + public void keyPressed(KeyEvent e, Point2D imagePoint, IcyCanvas canvas) + { + if (canvas.getViewer() != viewer) + return; + + if ((e.getKeyChar() == 'c') || (e.getKeyChar() == 'C')) + { + currentChannel = (currentChannel + 1) % (canvas.getCurrentImage().getSizeC()); + mainFrame.setTitle(sequence.getName() + " @Channel " + new Integer(currentChannel).toString() + + " Location @ " + '(' + new Double(Math.round((double) (currentPosX - radius))).toString() + + " " + ',' + " " + new Double(Math.round((double) (currentPosY - radius))).toString() + ')'); + canvas.repaint(); + mainFrame.repaint(); + } + else if ((e.getKeyChar() == 's') || (e.getKeyChar() == 'S')) + { + saveWorkbook(); + } + else if ((e.getKeyChar() == 'a') || (e.getKeyChar() == 'A')) + { + saveAllData(sequence); + } + else if ((e.getKeyChar() == 'q') || (e.getKeyChar() == 'Q')) + { + mainFrame.close(); + remove(); + } + else if (e.getKeyCode() == KeyEvent.VK_SPACE) + { + RefreshWorkbookMode = !RefreshWorkbookMode; + } + } + + @Override + public void remove() + { + frame.close(); + super.remove(); + if (sequence != null) + sequence.removeListener(this); + if (viewer != null) + viewer.removeListener(this); + magnifiers.remove(viewer); + } + + @Override + public void sequenceChanged(SequenceEvent sequenceEvent) + { + switch (sequenceEvent.getSourceType()) + { + case SEQUENCE_DATA: + case SEQUENCE_COLORMAP: + + updateImageCache(); + + default: + break; + } + } + + @Override + public void sequenceClosed(Sequence sequence) + { + remove(); + } + + @Override + public void viewerChanged(ViewerEvent event) + { + if (event.getType() == ViewerEventType.CANVAS_CHANGED) + remove(); + } + + @Override + public void viewerClosed(Viewer viewer) + { + remove(); + } + + public void saveWorkbook() + { + String path = SaveDialog.chooseFile("Save workbook as...", null, "Workbook", ".xls"); + if (path == null) + return; + + try + { + FileOutputStream fos = new FileOutputStream(path); + wb.getValue().write(fos); + fos.close(); + } + catch (IOException e1) + { + MessageDialog.showDialog(e1.getMessage(), MessageDialog.ERROR_MESSAGE); + } + } + + public void saveImageData(String path, Sequence sequence, int t, int z) + { + + if (path == null) + return; + if (!(path.contains(".csv"))) + { + path = path.concat(".csv"); + } + for (int channel = 0; channel < sequence.getSizeC(); channel++) + { + IcyBufferedImage im = sequence.getImage(t, z, channel); + + try + { + FileWriter fw = new FileWriter( + path.replace(".csv", '_' + "channel" + new Integer(channel).toString() + ".csv"), false); + + BufferedWriter bw = new BufferedWriter(fw); + for (int y = 0; y < im.getSizeY(); y++) + { + + for (int x = 0; x < im.getSizeX() - 1; x++) + { + bw.write(new Double(im.getData(x, y, 0)).toString()); + bw.write(','); + } + bw.write(new Double(im.getData(im.getSizeX() - 1, y, 0)).toString()); + bw.write('\n'); + bw.flush(); + } + bw.close(); + fw.close(); + } + catch (IOException e1) + { + MessageDialog.showDialog(e1.getMessage(), MessageDialog.ERROR_MESSAGE); + } + } + + } + + public void saveAllData(Sequence sequence) + { + String pathchanged; + + String path = SaveDialog.chooseFile("Save current image as...", null, + sequence.getName().toString() + ".csv"); + if (path == null) + return; + path = path.replace(".csv", "").replace(".CSV", ""); + + FileUtil.createDir(path); + path = path + '/' + sequence.getName().toString(); + for (int T = 0; T < sequence.getSizeT(); T++) + { + for (int Z = 0; Z < sequence.getSizeZ(); Z++) + { + + if (!(path.contains(".csv"))) + { + path = path.concat(".csv"); + } + + pathchanged = path.replace(".csv", + '_' + "t" + new Integer(T).toString() + '_' + "z" + new Integer(Z).toString() + ".csv"); + saveImageData(pathchanged, sequence, T, Z); + + } + } + } + + } +}