diff --git a/src/main/java/gadgets/DataContainer.java b/src/main/java/gadgets/DataContainer.java index d3dfa89..613e6a3 100644 --- a/src/main/java/gadgets/DataContainer.java +++ b/src/main/java/gadgets/DataContainer.java @@ -1,319 +1,334 @@ package gadgets; import algorithms.Algorithm; import algorithms.AutoThresholdRegression; import algorithms.InputCheck; import algorithms.MissingPreconditionException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import net.imglib2.RandomAccessibleInterval; import net.imglib2.algorithm.math.ImageStatistics; import net.imglib2.type.logic.BitType; import net.imglib2.type.numeric.RealType; /** - * The DataContainer keeps all the source data, pre-processing results and - * algorithm results that have been computed. It allows a client to get most of - * its content and makes the source image and channel information available to a - * client. + * The DataContainer keeps all the source data, jobName, pre-processing results + * and algorithm results that have been computed. It allows a client to get most + * of its content and makes the source image and channel information available + * to a client. * * @param */ public class DataContainer> { // enumeration of different mask types public enum MaskType { Regular, Irregular, None }; // some general image statistics double meanCh1, meanCh2, minCh1, maxCh1, minCh2, maxCh2, integralCh1, integralCh2; // The source images that the results are based on RandomAccessibleInterval sourceImage1, sourceImage2; // The names of the two source images String sourceImage1Name, sourceImage2Name; + // The name of the colocalisation run job + public String jobName; // The mask for the images RandomAccessibleInterval mask; // Type of the used mask protected MaskType maskType; // The channels of the source images that the result relate to int ch1, ch2; // The masks bounding box protected long[] maskBBSize = null; protected long[] maskBBOffset = null; InputCheck inputCheck = null; AutoThresholdRegression autoThreshold = null; // a list that contains all added algorithms List< Algorithm > algorithms = new ArrayList< Algorithm >(); /** * Creates a new {@link DataContainer} for a specific image channel * combination. We create default thresholds here that are the max and min of * the data type of the source image channels. * * @param src1 The channel one image source * @param src2 The channel two image source * @param ch1 The channel one image channel * @param ch2 The channel two image channel */ public DataContainer(RandomAccessibleInterval src1, RandomAccessibleInterval src2, int ch1, int ch2, String name1, String name2) { sourceImage1 = src1; sourceImage2 = src2; sourceImage1Name = name1; sourceImage2Name = name2; + // create a jobName so ResultHandler instances can all use the same + // object for the job name. + jobName = "Colocalization_of_" + name1 + "_versus_" + name2; // create a mask that is true at all pixels. final long[] dims = new long[src1.numDimensions()]; src1.dimensions(dims); mask = MaskFactory.createMask(dims, true); this.ch1 = ch1; this.ch2 = ch2; // fill mask dimension information, here the whole image maskBBOffset = new long[mask.numDimensions()]; Arrays.fill(maskBBOffset, 0); maskBBSize = new long[mask.numDimensions()]; mask.dimensions(maskBBSize); // indicated that there is actually no mask maskType = MaskType.None; calculateStatistics(); } /** * Creates a new {@link DataContainer} for a specific set of image and * channel combination. It will give access to the image according to * the mask passed. It is expected that the mask is of the same size * as an image slice. Default thresholds, min, max and mean will be set * according to the mask as well. * * @param src1 The channel one image source * @param src2 The channel two image source * @param ch1 The channel one image channel * @param ch2 The channel two image channel * @param mask The mask to use * @param offset The offset of the ROI in each dimension * @param size The size of the ROI in each dimension * @throws MissingPreconditionException */ public DataContainer(RandomAccessibleInterval src1, RandomAccessibleInterval src2, int ch1, int ch2, String name1, String name2, final RandomAccessibleInterval mask, final long[] offset, final long[] size) throws MissingPreconditionException { sourceImage1 = src1; sourceImage2 = src2; this.ch1 = ch1; this.ch2 = ch2; sourceImage1Name = name1; sourceImage2Name = name2; + // create a jobName so ResultHandler instances can all use the same + // object for the job name. + jobName = "Colocalization_of_" + name1 + "_versus_" + name2; final int numDims = src1.numDimensions(); maskBBOffset = new long[numDims]; maskBBSize = new long[numDims]; final long[] dim = new long[numDims]; src1.dimensions(dim); this.mask = MaskFactory.createMask(dim.clone(), mask); // this constructor supports irregular masks maskType = MaskType.Irregular; adjustRoiOffset(offset, maskBBOffset, dim); adjustRoiSize(size, maskBBSize, dim, maskBBOffset); calculateStatistics(); } /** * Creates a new {@link DataContainer} for a specific set of image and * channel combination. It will give access to the image according to * the region of interest (ROI) passed. Default thresholds, min, max and * mean will be set according to the ROI as well. * * @param src1 The channel one image source * @param src2 The channel two image source * @param ch1 The channel one image channel * @param ch2 The channel two image channel * @param offset The offset of the ROI in each dimension * @param size The size of the ROI in each dimension */ public DataContainer(RandomAccessibleInterval src1, RandomAccessibleInterval src2, int ch1, int ch2, String name1, String name2, final long[] offset, final long size[]) throws MissingPreconditionException { sourceImage1 = src1; sourceImage2 = src2; sourceImage1Name = name1; sourceImage1Name = name2; + // create a jobName so ResultHandler instances can all use the same + // object for the job name. + jobName = "Colocalization_of_" + name1 + "_versus_" + name2; final int numDims = src1.numDimensions(); final long[] dim = new long[numDims]; src1.dimensions(dim); long[] roiOffset = new long[numDims]; long[] roiSize = new long[numDims]; adjustRoiOffset(offset, roiOffset, dim); adjustRoiSize(size, roiSize, dim, roiOffset); // create a mask that is everywhere valid mask = MaskFactory.createMask(dim, roiOffset, roiSize); maskBBOffset = roiOffset.clone(); maskBBSize = roiSize.clone(); // this constructor only supports regular masks maskType = MaskType.Regular; this.ch1 = ch1; this.ch2 = ch2; calculateStatistics(); } protected void calculateStatistics() { meanCh1 = ImageStatistics.getImageMean(sourceImage1, mask); meanCh2 = ImageStatistics.getImageMean(sourceImage2, mask); minCh1 = ImageStatistics.getImageMin(sourceImage1, mask).getRealDouble(); minCh2 = ImageStatistics.getImageMin(sourceImage2, mask).getRealDouble(); maxCh1 = ImageStatistics.getImageMax(sourceImage1, mask).getRealDouble(); maxCh2 = ImageStatistics.getImageMax(sourceImage2, mask).getRealDouble(); integralCh1 = ImageStatistics.getImageIntegral(sourceImage1, mask); integralCh2 = ImageStatistics.getImageIntegral(sourceImage2, mask); } /** * Make sure that the ROI offset has the same dimensionality * as the image. The method fills it up with zeros if needed. * * @param oldOffset The offset with the original dimensionality * @param newOffset The output array with the new dimensionality * @param dimensions An array of the dimensions * @throws MissingPreconditionException */ protected void adjustRoiOffset(long[] oldOffset, long[] newOffset, long[] dimensions) throws MissingPreconditionException { for (int i=0; i dimensions[i]) throw new MissingPreconditionException("Dimension " + i + " of ROI offset is larger than image dimension."); newOffset[i] = oldOffset[i]; } else { newOffset[i] = 0; } } } /** * Transforms a ROI size array to a dimensionality. The method * fill up with image (dimension - offset in that dimension) if * needed. * * @param oldSize Size array of old dimensionality * @param newSize Output size array of new dimensionality * @param dimensions Dimensions representing the new dimensionality * @param offset Offset of the new dimensionality * @throws MissingPreconditionException */ protected void adjustRoiSize(long[] oldSize, long[] newSize, long[] dimensions, long[] offset) throws MissingPreconditionException { for (int i=0; i (dimensions[i] - offset[i])) throw new MissingPreconditionException("Dimension " + i + " of ROI size is larger than what fits in."); newSize[i] = oldSize[i]; } else { newSize[i] = dimensions[i] - offset[i]; } } } public MaskType getMaskType() { return maskType; } public RandomAccessibleInterval getSourceImage1() { return sourceImage1; } public RandomAccessibleInterval getSourceImage2() { return sourceImage2; } public String getSourceImage1Name() { return sourceImage1Name; } public String getSourceImage2Name() { return sourceImage2Name; } + public String getJobName() { + return jobName; + } + public RandomAccessibleInterval getMask() { return mask; } public long[] getMaskBBOffset() { return maskBBOffset.clone(); } public long[] getMaskBBSize() { return maskBBSize.clone(); } public int getCh1() { return ch1; } public int getCh2() { return ch2; } public double getMeanCh1() { return meanCh1; } public double getMeanCh2() { return meanCh2; } public double getMinCh1() { return minCh1; } public double getMaxCh1() { return maxCh1; } public double getMinCh2() { return minCh2; } public double getMaxCh2() { return maxCh2; } public double getIntegralCh1() { return integralCh1; } public double getIntegralCh2() { return integralCh2; } public InputCheck getInputCheck() { return inputCheck; } public Algorithm setInputCheck(InputCheck inputCheck) { this.inputCheck = inputCheck; return inputCheck; } public AutoThresholdRegression getAutoThreshold() { return autoThreshold; } public Algorithm setAutoThreshold(AutoThresholdRegression autoThreshold) { this.autoThreshold = autoThreshold; return autoThreshold; } } diff --git a/src/main/java/results/PDFWriter.java b/src/main/java/results/PDFWriter.java index aa2d6e7..a086a07 100644 --- a/src/main/java/results/PDFWriter.java +++ b/src/main/java/results/PDFWriter.java @@ -1,228 +1,228 @@ package results; import algorithms.Histogram2D; import com.itextpdf.text.BadElementException; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.PageSize; import com.itextpdf.text.Paragraph; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfWriter; import gadgets.DataContainer; import gadgets.DataContainer.MaskType; import ij.IJ; import ij.ImagePlus; import ij.io.SaveDialog; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import net.imglib2.RandomAccessibleInterval; import net.imglib2.algorithm.math.ImageStatistics; import net.imglib2.img.display.imagej.ImageJFunctions; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.LongType; public class PDFWriter> implements ResultHandler { // indicates if we want to produce US letter or A4 size boolean isLetter = false; // indicates if the content is the first item on the page boolean isFirst = true; // show the name of the image static boolean showName=true; // a static counter for this sessions created PDFs static int succeededPrints = 0; // show the size in pixels of the image static boolean showSize=true; // a reference to the data container DataContainer container; PdfWriter writer; Document document; // a list of the available result images, no matter what specific kinds protected List listOfPDFImages = new ArrayList(); protected List listOfPDFTexts = new ArrayList(); /** * Creates a new PDFWriter that can access the container. * * @param container The data container for source image data */ public PDFWriter(DataContainer container) { this.container = container; } public void handleImage(RandomAccessibleInterval image, String name) { ImagePlus imp = ImageJFunctions.wrapFloat( image, name ); // set the display range double max = ImageStatistics.getImageMax(image).getRealDouble(); imp.setDisplayRange(0.0, max); addImageToList(imp, name); } /** * Handles a histogram the following way: create snapshot, log data, reset the * display range, apply the Fire LUT and finally store it as an iText PDF image. * Afterwards the image is reset to its orignal state again */ public void handleHistogram(Histogram2D histogram, String name) { RandomAccessibleInterval image = histogram.getPlotImage(); ImagePlus imp = ImageJFunctions.wrapFloat( image, name ); // make a snapshot to be able to reset after modifications imp.getProcessor().snapshot(); imp.getProcessor().log(); imp.updateAndDraw(); imp.getProcessor().resetMinAndMax(); IJ.run(imp,"Fire", null); addImageToList(imp, name); // reset the imp from the log scaling we applied earlier imp.getProcessor().reset(); } protected void addImageToList(ImagePlus imp, String name) { java.awt.Image awtImage = imp.getImage(); try { com.itextpdf.text.Image pdfImage = com.itextpdf.text.Image.getInstance(awtImage, null); pdfImage.setAlt(name); // iText-1.3 setMarkupAttribute("name", name); listOfPDFImages.add(pdfImage); } catch (BadElementException e) { IJ.log("Could not convert image to correct format for PDF generation"); IJ.handleException(e); } catch (IOException e) { IJ.log("Could not convert image to correct format for PDF generation"); IJ.handleException(e); } } public void handleWarning(Warning warning) { listOfPDFTexts.add(new Paragraph("Warning! " + warning.getShortMessage() + " - " + warning.getLongMessage())); } @Override public void handleValue(String name, String value) { // send (output parameter name, value) to IJ.log for scraping batch results IJ.log(name + ", " + value); listOfPDFTexts.add(new Paragraph(name + ": " + value)); } public void handleValue(String name, double value) { handleValue(name, value, 3); } public void handleValue(String name, double value, int decimals) { listOfPDFTexts.add(new Paragraph(name + ": " + IJ.d2s(value, decimals))); } /** * Prints an image into the opened PDF. * @param img The image to print. * @param printName The name to print under the image. */ protected void addImage(com.itextpdf.text.Image image) throws DocumentException, IOException { if (! isFirst) { document.add(new Paragraph("\n")); float vertPos = writer.getVerticalPosition(true); if (vertPos - document.bottom() < image.getHeight()) { document.newPage(); } else { PdfContentByte cb = writer.getDirectContent(); cb.setLineWidth(1f); if (isLetter) { cb.moveTo(PageSize.LETTER.getLeft(50), vertPos); cb.lineTo(PageSize.LETTER.getRight(50), vertPos); } else { cb.moveTo(PageSize.A4.getLeft(50), vertPos); cb.lineTo(PageSize.A4.getRight(50), vertPos); } cb.stroke(); } } if (showName) { Paragraph paragraph = new Paragraph(image.getAlt()); // iText-1.3: getMarkupAttribute("name")); paragraph.setAlignment(Paragraph.ALIGN_CENTER); document.add(paragraph); //spcNm = 40; } if (showSize) { Paragraph paragraph = new Paragraph(image.getWidth() + " x " + image.getHeight()); paragraph.setAlignment(Paragraph.ALIGN_CENTER); document.add(paragraph); //spcSz = 40; } image.setAlignment(com.itextpdf.text.Image.ALIGN_CENTER); document.add(image); isFirst = false; } public void process() { try { // produce default name String nameCh1 = container.getSourceImage1Name(); String nameCh2 = container.getSourceImage2Name(); - ////send names of the 2 images to IJ.log for scraping batch results - IJ.log("ImageNames" + ", " + nameCh1 + ", " + nameCh2); + // Use the getJobName() in DataContainer for the job name. + String jobName = container.getJobName(); - String name = "coloc_" + nameCh1 + "_" + nameCh2; /* If a mask is in use, add a counter - * information to the name. + * information to the jobName. */ if (container.getMaskType() != MaskType.None) { - name += "_mask_"+ (succeededPrints + 1); + jobName += "_mask_"+ (succeededPrints + 1); } // get the path to the file we are about to create - SaveDialog sd = new SaveDialog("Save as PDF", name, ".pdf"); - name = sd.getFileName(); + SaveDialog sd = new SaveDialog("Save as PDF", jobName, ".pdf"); + // update jobName id the user changes it in the save file dialog. + jobName = sd.getFileName(); String directory = sd.getDirectory(); - // make sure we got what we need - if ((name == null) || (directory == null)) { + // make sure we have what we need next + if ((jobName == null) || (directory == null)) { return; } - String path = directory+name; + String path = directory+jobName; // create a new iText Document and add date and title document = new Document(isLetter ? PageSize.LETTER : PageSize.A4); document.addCreationDate(); - document.addTitle(name); + document.addTitle(jobName); // get a writer object to do the actual output writer = PdfWriter.getInstance(document, new FileOutputStream(path)); document.open(); // iterate over all produced images for (com.itextpdf.text.Image img : listOfPDFImages) { addImage(img); } //send name of analysis job to IJ.log for scraping batch results - IJ.log("ColocAnalysisJobName" + ", " + name); + IJ.log("ColocAnalysisJobName" + ", " + jobName); //iterate over all produced text objects for (Paragraph p : listOfPDFTexts) { document.add(p); } } catch(DocumentException de) { IJ.showMessage("PDF Writer", de.getMessage()); } catch(IOException ioe) { IJ.showMessage("PDF Writer", ioe.getMessage()); } finally { if (document !=null) { document.close(); succeededPrints++; } } } } diff --git a/src/main/java/results/SingleWindowDisplay.java b/src/main/java/results/SingleWindowDisplay.java index 54a2f29..d7214e3 100644 --- a/src/main/java/results/SingleWindowDisplay.java +++ b/src/main/java/results/SingleWindowDisplay.java @@ -1,635 +1,635 @@ package results; import algorithms.AutoThresholdRegression; import algorithms.Histogram2D; import fiji.util.gui.JImagePanel; import gadgets.DataContainer; import ij.IJ; import ij.ImageJ; import ij.ImagePlus; import ij.gui.Line; import ij.gui.Overlay; import ij.process.ImageProcessor; import ij.text.TextWindow; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Panel; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.ClipboardOwner; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JScrollPane; import net.imglib2.RandomAccess; import net.imglib2.RandomAccessibleInterval; import net.imglib2.img.display.imagej.ImageJFunctions; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.integer.LongType; /** * This class displays the container contents in one single window * and offers features like the use of different LUTs. * */ public class SingleWindowDisplay> extends JFrame implements ResultHandler, ItemListener, ActionListener, ClipboardOwner, MouseMotionListener { private static final long serialVersionUID = -5642321584354176878L; protected static final int WIN_WIDTH = 350; protected static final int WIN_HEIGHT = 600; // a static list for keeping track of all other SingleWindowDisplays protected static ArrayList> displays = new ArrayList>(); // indicates if original images should be displayed or not protected boolean displayOriginalImages = false; // this is the image currently selected by the drop down menu protected RandomAccessibleInterval> currentlyDisplayedImageResult; // a list of the available result images, no matter what specific kinds protected List< NamedContainer< RandomAccessibleInterval>> > listOfImages = new ArrayList< NamedContainer< RandomAccessibleInterval>> >(); protected Map, Histogram2D> mapOf2DHistograms = new HashMap, Histogram2D>(); // a list of warnings protected List warnings = new ArrayList(); // a list of named values, collected from algorithms protected List valueResults = new ArrayList(); /* a map of images and corresponding LUTs. When an image is not in * there no LUT should be applied. */ protected Map listOfLUTs = new HashMap(); //make a cursor so we can get pixel values from the image protected RandomAccess> pixelAccessCursor; // A PDF writer to call if user wants PDF print protected PDFWriter pdfWriter; // The current image protected ImagePlus imp; // GUI elements protected JImagePanel imagePanel; protected JButton listButton, copyButton; protected JCheckBox log; /* The data container with general information about * source images */ protected DataContainer dataContainer = null; public SingleWindowDisplay(DataContainer container, PDFWriter pdfWriter){ - super("Colocalisation " + container.getSourceImage1Name() + " vs " + - container.getSourceImage2Name()); + // Show job name in title bar + super(container.getJobName()); setPreferredSize(new Dimension(WIN_WIDTH, WIN_HEIGHT)); // save a reference to the container dataContainer = container; this.pdfWriter = pdfWriter; // don't show ourself on instantiation this.setVisible(false); // add ourself to the list of single window displays displays.add(this); } public void setup() { JComboBox dropDownList = new JComboBox(); for(NamedContainer< RandomAccessibleInterval> > img : listOfImages) { dropDownList.addItem( new NamedContainer> >( img.object, img.name)); } dropDownList.addItemListener(this); imagePanel = new JImagePanel(ij.IJ.createImage("dummy", "8-bit", 10, 10, 1)); imagePanel.addMouseMotionListener(this); // Create something to display it in final JEditorPane editor = new JEditorPane(); editor.setEditable(false); // we're browsing not editing editor.setContentType("text/html"); // must specify HTML text editor.setText(makeHtmlText()); // specify the text to display // Put the JEditorPane in a scrolling window and add it JScrollPane scrollPane = new JScrollPane(editor); scrollPane.setPreferredSize(new Dimension(256, 150)); Panel buttons = new Panel(); buttons.setLayout(new FlowLayout(FlowLayout.RIGHT)); // add button for data display of histograms listButton = new JButton("List"); listButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { showList(); } }); buttons.add(listButton); // add button for data copy of histograms copyButton = new JButton("Copy"); copyButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { copyToClipboard(); } }); buttons.add(copyButton); // add button for PDF printing JButton pdfButten = new JButton("PDF"); pdfButten.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { pdfWriter.process(); } }); buttons.add(pdfButten); /* We want the image to be log scale by default * so the user can see something. */ log = new JCheckBox("Log"); log.setSelected(true); log.addActionListener(this); buttons.add(log); final GridBagLayout layout = new GridBagLayout(); final Container pane = getContentPane(); getContentPane().setLayout(layout); final GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.weightx = 1; c.gridwidth = GridBagConstraints.BOTH; c.gridy++; pane.add(dropDownList, c); c.gridy++; c.weighty = 1; pane.add(imagePanel, c); c.gridy++; c.weighty = 1; pane.add(scrollPane, c); c.weighty = 0; c.gridy++; pane.add(buttons, c); pack(); } public void process() { // if wanted, display source images if ( displayOriginalImages ) { listOfImages.add( new NamedContainer> >( dataContainer.getSourceImage1(), dataContainer.getSourceImage1Name() ) ); listOfImages.add( new NamedContainer> >( dataContainer.getSourceImage2(), dataContainer.getSourceImage2Name() ) ); } // set up the GUI, which runs makeHtmlText() for the value results // formatting. setup(); // display the first image available, if any if (listOfImages.size() > 0) { adjustDisplayedImage(listOfImages.get(0).object); } // show the GUI this.setVisible(true); } public void handleImage(RandomAccessibleInterval image, String name) { listOfImages.add( new NamedContainer> >( image, name ) ); } public void handleHistogram(Histogram2D histogram, String name) { listOfImages.add( new NamedContainer> >( histogram.getPlotImage(), name ) ); mapOf2DHistograms.put(histogram.getPlotImage(), histogram); // link the histogram to a LUT listOfLUTs.put(histogram.getPlotImage(), "Fire"); } public void handleWarning(Warning warning) { warnings.add( warning ); } @Override public void handleValue(String name, String value) { valueResults.add( new ValueResult(name, value)); } public void handleValue(String name, double value) { handleValue(name, value, 3); } public void handleValue(String name, double value, int decimals) { valueResults.add( new ValueResult(name, value, decimals)); } /** * Prints an HTML table entry onto the stream. */ protected void printTableRow(PrintWriter out, String name, String text) { out.print("" + name + "" + text + ""); // add some style information out.print(""); out.print(""); // print out warnings, if any if ( warnings.size() > 0 ) { out.print("

Warnings

"); // Print out the table out.print(""); out.print(""); for (Warning w : warnings) { printTableRow(out, w.getShortMessage(), w.getLongMessage()); } out.println("
TypeMessage
"); } else { out.print("

No warnings occured

"); } // print out simple value results out.print("

Results

"); // Print out the table out.print(""); out.print(""); // Print table rows and spit results to the IJ log. for ( ValueResult vr : valueResults) { if (vr.isNumber) { printTableRow(out, vr.name, vr.number, vr.decimals); IJ.log(vr.name + ", " + IJ.d2s(vr.number, vr.decimals)); } else { printTableRow(out, vr.name, vr.value); IJ.log(vr.name + ", " + vr.value); } } out.println("
NameResult
"); out.print(""); out.close(); // Get the string of HTML from the StringWriter and return it. return sout.toString(); } /** * If the currently selected ImageResult is an HistrogramResult, * a table of x-values, y-values and the counts. */ protected void showList() { /* check if we are dealing with an histogram result * or a generic image result */ if (isHistogram(currentlyDisplayedImageResult)) { Histogram2D hr = mapOf2DHistograms.get(currentlyDisplayedImageResult); double xBinWidth = 1.0 / hr.getXBinWidth(); double yBinWidth = 1.0 / hr.getYBinWidth(); // check if we have bins of size one or other ones boolean xBinWidthIsOne = Math.abs(xBinWidth - 1.0) < 0.00001; boolean yBinWidthIsOne = Math.abs(yBinWidth - 1.0) < 0.00001; // configure table headings accordingly String vHeadingX = xBinWidthIsOne ? "X value" : "X bin start"; String vHeadingY = yBinWidthIsOne ? "Y value" : "Y bin start"; // get the actual histogram data String histogramData = hr.getData(); TextWindow tw = new TextWindow(getTitle(), vHeadingX + "\t" + vHeadingY + "\tcount", histogramData, 250, 400); tw.setVisible(true); } } /** * If the currently selected ImageResult is an HistogramRestult, * this method copies its data into to the clipboard. */ protected void copyToClipboard() { /* check if we are dealing with an histogram result * or a generic image result */ if (isHistogram(currentlyDisplayedImageResult)) { /* try to get the system clipboard and return * if we can't get it */ Clipboard systemClipboard = null; try { systemClipboard = getToolkit().getSystemClipboard(); } catch (Exception e) { systemClipboard = null; } if (systemClipboard==null) { IJ.error("Unable to copy to Clipboard."); return; } // copy histogram values IJ.showStatus("Copying histogram values..."); String text = mapOf2DHistograms.get(currentlyDisplayedImageResult).getData(); StringSelection contents = new StringSelection( text ); systemClipboard.setContents(contents, this); IJ.showStatus(text.length() + " characters copied to Clipboard"); } } @Override public void mouseDragged(MouseEvent e) { // nothing to do here } @Override public void mouseMoved(MouseEvent e) { if (e.getSource().equals(imagePanel)) { /* * calculate the mouse position relative to the upper left * corner of the displayed image. */ final int imgWidth = imagePanel.getSrcRect().width; final int imgHeight = imagePanel.getSrcRect().height; int displayWidth = (int)(imgWidth * imagePanel.getMagnification()); int displayHeight = (int)(imgHeight * imagePanel.getMagnification()); int offsetX = (imagePanel.getWidth() - displayWidth) / 2; int offsetY = (imagePanel.getHeight() - displayHeight) / 2; int onImageX = imagePanel.screenX(e.getX() - offsetX); int onImageY = imagePanel.screenY(e.getY() - offsetY); // make sure we stay within the image boundaries if (onImageX >= 0 && onImageX < imgWidth && onImageY >= 0 && onImageY < imgHeight ) { mouseMoved(onImageX, onImageY); } else { IJ.showStatus(""); } } } /** * Displays information about the pixel below the mouse cursor of * the currently displayed image result. The coordinates passed are * expected to be within the image boundaries. * * @param x * @param y */ public void mouseMoved( int x, int y) { final ImageJ ij = IJ.getInstance(); if (ij != null && currentlyDisplayedImageResult != null) { /* If Alt key is not pressed, display the calibrated data. * If not, display image positions and data. * Non log image intensity from original image or 2D histogram result is always shown in status bar, * not the log intensity that might actually be displayed in the image. */ if (!IJ.altKeyDown()) { // the alt key is not pressed use x and y values that are bin widths or calibrated intensities not the x y image coordinates. if (isHistogram(currentlyDisplayedImageResult)) { Histogram2D histogram = mapOf2DHistograms.get(currentlyDisplayedImageResult); synchronized( pixelAccessCursor ) { // set position of output cursor pixelAccessCursor.setPosition(x, 0); pixelAccessCursor.setPosition(y, 1); // for a histogram coordinate display we need to invert the Y axis y = (int)currentlyDisplayedImageResult.dimension(1) - 1 - y; // get current value at position RandomAccess cursor = (RandomAccess)pixelAccessCursor; long val = cursor.get().getIntegerLong(); double calibratedXBinBottom = histogram.getXMin() + x / histogram.getXBinWidth(); double calibratedXBinTop = histogram.getXMin() + (x + 1) / histogram.getXBinWidth(); double calibratedYBinBottom = histogram.getYMin() + y / histogram.getYBinWidth(); double calibratedYBinTop = histogram.getYMin() + (y + 1) / histogram.getYBinWidth(); IJ.showStatus("x = " + IJ.d2s(calibratedXBinBottom) + " to " + IJ.d2s(calibratedXBinTop) + ", y = " + IJ.d2s(calibratedYBinBottom) + " to " + IJ.d2s(calibratedYBinTop) + ", value = " + val ); } } else { RandomAccessibleInterval img = (RandomAccessibleInterval) currentlyDisplayedImageResult; ImagePlus imp = ImageJFunctions.wrapFloat( img, "TODO" ); imp.mouseMoved(x, y); } } else { // alt key is down, so show the image coordinates for x y in status bar. RandomAccessibleInterval img = (RandomAccessibleInterval) currentlyDisplayedImageResult; ImagePlus imp = ImageJFunctions.wrapFloat( img, "TODO" ); imp.mouseMoved(x, y); } } } /** * Draws the passed ImageResult on the ImagePlus of this class. * If the image is part of a CompositeImageResult then contained * lines will also be drawn */ protected void drawImage(RandomAccessibleInterval> img) { // get ImgLib image as ImageJ image imp = ImageJFunctions.wrapFloat( (RandomAccessibleInterval) img, "TODO" ); imagePanel.updateImage(imp); // set the display range // check if a LUT should be applied if ( listOfLUTs.containsKey(img) ) { // select linked look up table IJ.run(imp, listOfLUTs.get(img), null); } imp.getProcessor().resetMinAndMax(); boolean overlayModified = false; Overlay overlay = new Overlay(); // if it is the 2d histogram, we want to show the regression line if (isHistogram(img)) { Histogram2D histogram = mapOf2DHistograms.get(img); /* check if we should draw a regression line for the * current histogram. */ if ( histogram.getDrawingSettings().contains(Histogram2D.DrawingFlags.RegressionLine) ) { AutoThresholdRegression autoThreshold = dataContainer.getAutoThreshold(); if (histogram != null && autoThreshold != null) { if (img == histogram.getPlotImage()) { drawLine(overlay, img, autoThreshold.getAutoThresholdSlope(), autoThreshold.getAutoThresholdIntercept()); overlayModified = true; } } } } if (overlayModified) { overlay.setStrokeColor(java.awt.Color.WHITE); imp.setOverlay(overlay); } imagePanel.repaint(); } /** * Tests whether the given image is a histogram or not. * @param img The image to test * @return true if histogram, false otherwise */ protected boolean isHistogram(RandomAccessibleInterval> img) { return mapOf2DHistograms.containsKey(img); } /** * Draws the line on the overlay. */ protected void drawLine(Overlay overlay, RandomAccessibleInterval> img, double slope, double intercept) { double startX, startY, endX, endY; long imgWidth = img.dimension(0); long imgHeight = img.dimension(1); /* since we want to draw the line over the whole image * we can directly use screen coordinates for x values. */ startX = 0.0; endX = imgWidth; // check if we can get some exta information for drawing if (isHistogram(img)) { Histogram2D histogram = mapOf2DHistograms.get(img); // get calibrated start y coordinates double calibratedStartY = slope * histogram.getXMin() + intercept; double calibratedEndY = slope * histogram.getXMax() + intercept; // convert calibrated coordinates to screen coordinates startY = calibratedStartY * histogram.getYBinWidth(); endY = calibratedEndY * histogram.getYBinWidth(); } else { startY = slope * startX + intercept; endY = slope * endX + intercept; } /* since the screen origin is in the top left * of the image, we need to x-mirror our line */ startY = ( imgHeight - 1 ) - startY; endY = ( imgHeight - 1 ) - endY; // create the line ROI and add it to the overlay Line lineROI = new Line(startX, startY, endX, endY); /* Set drawing width of line to one, in case it has * been changed globally. */ lineROI.setStrokeWidth(1.0f); overlay.add(lineROI); } protected void adjustDisplayedImage (RandomAccessibleInterval> img) { /* when changing the result image to display * need to set the image we were looking at * back to not log scale, * so we don't log it twice if its reselected. */ if (log.isSelected()) toggleLogarithmic(false); currentlyDisplayedImageResult = img; pixelAccessCursor = img.randomAccess(); // Currently disabled, due to lag of non-histograms :-) // disable list and copy button if it is no histogram result listButton.setEnabled( isHistogram(img) ); copyButton.setEnabled( isHistogram(img) ); drawImage(img); toggleLogarithmic(log.isSelected()); // ensure a valid layout, we changed the image getContentPane().validate(); getContentPane().repaint(); } public void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { RandomAccessibleInterval> img = ((NamedContainer> >)(e.getItem())).getObject(); adjustDisplayedImage(img); } } protected void toggleLogarithmic(boolean enabled){ if (imp == null) return; ImageProcessor ip = imp.getProcessor(); if (enabled) { ip.snapshot(); ip.log(); ip.resetMinAndMax(); } else ip.reset(); imagePanel.repaint(); } public void actionPerformed(ActionEvent e) { if (e.getSource() == log) { toggleLogarithmic(log.isSelected()); } } public void lostOwnership(Clipboard clipboard, Transferable contents) { // nothing to do here } }