/* *------------------------------------------------------------------------------ * Copyright (C) 2015 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * 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. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.util.roi.io; import ij.ImagePlus; import ij.Prefs; import ij.WindowManager; import ij.gui.EllipseRoi; import ij.gui.Line; import ij.gui.OvalRoi; import ij.gui.Overlay; import ij.gui.PointRoi; import ij.gui.PolygonRoi; import ij.gui.Roi; import ij.gui.ShapeRoi; import ij.gui.TextRoi; import ij.measure.Measurements; import ij.measure.ResultsTable; import ij.plugin.filter.Analyzer; import ij.plugin.frame.RoiManager; import java.awt.Color; import java.awt.Rectangle; import java.awt.geom.Point2D; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import org.openmicroscopy.shoola.util.CommonsLangUtils; import ome.formats.model.UnitsFactory; import omero.model.ImageI; import omero.model.LengthI; import omero.gateway.model.EllipseData; import omero.gateway.model.LineData; import omero.gateway.model.PointData; import omero.gateway.model.PolygonData; import omero.gateway.model.PolylineData; import omero.gateway.model.ROIData; import omero.gateway.model.RectangleData; import omero.gateway.model.ShapeData; import omero.gateway.model.ShapeSettingsData; import omero.gateway.model.TextData; /** * Reads ROI from ImageJ. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @since 5.0 */ public class ROIReader { private static final String PRECISION = "precision"; private static final String MEASUREMENTS = "measurements"; /** * Converts the line. * * @param shape The line to convert. * @return See above. */ private LineData convertLine(Line shape) { LineData r = new LineData(shape.x1d, shape.y1d, shape.x2d, shape.y2d); r.setText(shape.getName()); if (formatShape(shape, r)) { return r; } return null; } /** * Converts the ellipse. * * @param shape The ellipse to convert. * @return See above. */ private EllipseData convertEllipse(OvalRoi shape) { Rectangle bounds = shape.getBounds(); double rx = bounds.getWidth(); double ry = bounds.getHeight(); EllipseData r = new EllipseData(bounds.getX()+rx/2, bounds.getY()+ry/2, rx/2, ry/2); r.setText(shape.getName()); if (formatShape(shape, r)) { return r; } return null; } /** * Converts the rectangle. * * @param shape The rectangle to convert. * @return See above. */ private RectangleData convertRectangle(Roi shape) { Rectangle bounds = shape.getBounds(); RectangleData r = new RectangleData( bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); r.setText(shape.getName()); if (formatShape(shape, r)) { return r; } return null; } /** * Converts the point. * * @param shape The point to convert. * @return See above. */ private void convertPoint(PointRoi shape, ROIData roi) { int[] xc = shape.getPolygon().xpoints; int[] yc = shape.getPolygon().ypoints; PointData p; for (int i = 0; i < xc.length; i++) { p = new PointData((double) xc[i], (double) yc[i]); p.setText(shape.getName()); if (formatShape(shape, p)) { roi.addShapeData(p); } } } /** * Converts the polygon. * * @param shape The point to convert. * @return See above. */ private ShapeData convertPolygon(PolygonRoi shape) { int[] xc = shape.getPolygon().xpoints; int[] yc = shape.getPolygon().ypoints; String type = shape.getTypeAsString(); List<Point2D.Double> points = new LinkedList<Point2D.Double>(); List<Integer> masks = new ArrayList<Integer>(); for (int i = 0; i < xc.length; i++) { points.add(new Point2D.Double(xc[i], yc[i])); } ShapeData data; if (type.matches("Polyline") || type.matches("Freeline") || type.matches("Angle")) { data = new PolylineData(points, points, points, masks); ((PolylineData) data).setText(shape.getName()); } else if (type.matches("Polygon") || type.matches("Freehand") || type.matches("Traced")){ data = new PolygonData(points, points, points, masks); ((PolygonData) data).setText(shape.getName()); } else { data = new PolygonData(points, points, points, masks); ((PolygonData) data).setText(shape.getName()); } if (formatShape(shape, data)){ return data; } return null; } /** * Converts the text roi. * * @param shape The point to convert. * @return See above. */ private TextData convertText(TextRoi shape) { Rectangle b = shape.getPolygon().getBounds(); TextData data = new TextData(shape.getText(), b.getX(), b.getY()); return data; } /** * Formats the shape. * * @param roi The ImageJ roi. * @param shape The internal roi. * @return <code>true</code> if the shape is valid, <code>false</code> * otherwise. */ private boolean formatShape(Roi roi, ShapeData shape) { ShapeSettingsData settings = shape.getShapeSettings(); if (roi.getStrokeWidth() > 0) { settings.setStrokeWidth(new LengthI((double) roi.getStrokeWidth(), UnitsFactory.Shape_StrokeWidth)); } Color color; if (roi.getStrokeColor() != null) { color = roi.getStrokeColor(); settings.setStroke(new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha())); } if (roi.getFillColor() != null) { color = roi.getFillColor(); settings.setFill(new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha())); } int pos = roi.getPosition(); int c = roi.getCPosition(); int z = roi.getZPosition(); int t = roi.getTPosition(); ImagePlus image = roi.getImage(); int imageC = image.getNChannels(); int imageT = image.getNFrames(); int imageZ = image.getNSlices(); if (imageC == 1 && imageZ == 1) { shape.setC(0); shape.setZ(0); z = 0; c = 0; t = pos; } else if (imageZ == 1 && imageT == 1) { c = pos; z = 0; t = 0; shape.setZ(0); shape.setT(0); } else if (imageC == 1 && imageT == 1) { z = pos; t = 0; c = 0; shape.setC(0); shape.setT(0); } if (c > imageC || z > imageZ || t > imageT) { return false; } if (c != 0) { shape.setC(c-1); } if (z != 0) { shape.setZ(z-1); } if (t != 0) { shape.setT(t-1); } return true; } /** * Links image and rois. * * @param rois The rois to handle. */ private void setImage(Roi[] rois) { ImagePlus img = WindowManager.getCurrentImage(); for (int i = 0; i < rois.length; i++) { int id = rois[i].getImageID(); if (id <= 0) { //no image set. rois[i].setImage(img); } else { rois[i].setImage(WindowManager.getImage(id)); } } } /** * Reads the roi linked to the imageJ object. * * @param imageID The identifier of the image to link the ROI to. * @param rois The rois to convert. * @return See above. */ private List<ROIData> read(long imageID, Roi[] rois) { if (rois == null || rois.length == 0) return null; Roi r; List<ROIData> pojos = new ArrayList<ROIData>(); //check ij version String type; ROIData roiData; ShapeData shape; for (int i = 0; i < rois.length; i++) { r = rois[i]; roiData = new ROIData(); type = r.getTypeAsString(); if (imageID >= 0) { roiData.setImage(new ImageI(imageID, false)); } pojos.add(roiData); if (r.isDrawingTool()) {//Checks if the given roi is a Text box/Arrow/Rounded Rectangle if (type.matches("Text")){ roiData.addShapeData(convertText((TextRoi) r)); } else if (type.matches("Rectangle")) { shape = convertRectangle(r); if (shape != null) { roiData.addShapeData(shape); } } } else if (r instanceof OvalRoi) { shape = convertEllipse((OvalRoi) r); if (shape != null) { roiData.addShapeData(shape); } } else if (r instanceof Line) { shape = convertLine((Line) r); if (shape != null) { roiData.addShapeData(shape); } } else if (r instanceof PolygonRoi || r instanceof EllipseRoi) { if (type.matches("Point")) { convertPoint((PointRoi) r, roiData); } else if (type.matches("Polyline") || type.matches("Freeline") || type.matches("Angle") || type.matches("Polygon") || type.matches("Freehand") || type.matches("Traced") || type.matches("Oval")) { shape = convertPolygon((PolygonRoi) r); if (shape != null) { roiData.addShapeData(shape); } } } else if (r instanceof ShapeRoi) { Roi[] subRois = ((ShapeRoi) r).getRois(); Roi shapeij; for (int j = 0; j < subRois.length; j++) { shapeij = subRois[j]; // Set ImagePlus reference in subROIs for the check in L216 to work ImagePlus imp = r.getImage(); shapeij.setImage(imp); // Transfer correct ROI positions (according to IJ) from superROI int pos = r.getPosition(); int c = r.getCPosition(); int z = r.getZPosition(); int t = r.getTPosition(); if (imp.getNChannels() == 1 && imp.getNSlices() == 1) { shapeij.setPosition(pos); } else if (imp.getNChannels() == 1 && imp.getNFrames() == 1) { shapeij.setPosition(pos); } else if (imp.getNSlices() == 1 && imp.getNFrames() == 1) { shapeij.setPosition(pos); } else if (imp.isHyperStack()) { shapeij.setPosition(c, z, t); } type = shapeij.getTypeAsString(); if (shapeij instanceof Line) { shape = convertLine((Line) shapeij); if (shape != null) { roiData.addShapeData(shape); } } else if (shapeij instanceof OvalRoi) { shape = convertEllipse((OvalRoi) shapeij); if (shape != null) { roiData.addShapeData(shape); } } else if (shapeij instanceof PolygonRoi || r instanceof EllipseRoi) { if (type.matches("Point")) { convertPoint((PointRoi) shapeij, roiData); } else if (type.matches("Polyline") || type.matches("Freeline") || type.matches("Angle") || type.matches("Polygon") || type.matches("Freehand") || type.matches("Traced") || type.matches("Oval")) { shape = convertPolygon((PolygonRoi) shapeij); if (shape != null) { roiData.addShapeData(shape); } } } } } else if (type.matches("Rectangle")) { shape = convertRectangle(r); if (shape != null) { roiData.addShapeData(shape); } } } return pojos; } /** * Reads the roi linked to the imageJ object. * * @param imageID The identifier of the image to link the ROI to. * @param image The ImageJ object. * @return See above. */ public List<ROIData> readImageJROI(long imageID, ImagePlus image) { if (image == null) return null; Overlay overlay = image.getOverlay(); Roi[] rois; if (overlay != null) { rois = overlay.toArray(); for (Roi roi : rois) { roi.setImage(image); } return read(imageID, rois); } RoiManager manager = RoiManager.getInstance(); if (manager == null) return null; rois = manager.getRoisAsArray(); for (Roi roi : rois) { roi.setImage(image); } return read(imageID, rois); } /** * Reads the roi linked to the imageJ object. * * @param imageID The identifier of the image to link the ROI to. * @return See above. */ public List<ROIData> readImageJROI(long imageID) { RoiManager manager = RoiManager.getInstance(); if (manager == null) return null; Roi[] rois = manager.getRoisAsArray(); setImage(rois); return read(imageID, rois); } /** * Reads the roi linked to the imageJ object. * First checks the overlays then the roi manager. * * @param imageID The identifier of the image to link the ROI to. * @param image The ImageJ object. * @return See above. */ public List<ROIData> readImageJROIFromSources(long imageID, ImagePlus image) { if (image == null) return null; Overlay overlay = image.getOverlay(); if (overlay != null) { Roi[] rois = overlay.toArray(); for (int i = 0; i < rois.length; i++) { rois[i].setImage(image); } return read(imageID, rois); } return readImageJROI(imageID, image); } /** * Reads the rois from the ROI manager. * * @return See above. */ public List<ROIData> readImageJROI() { RoiManager manager = RoiManager.getInstance(); if (manager == null) return null; Roi[] rois = manager.getRoisAsArray(); setImage(rois); return read(-1, rois); } /** * Reads the results and save them to the specified file. * * @param f The file to save the results to. */ public void readROIMeasurement(File f) throws IOException { if (f == null) return; readROIMeasurement(f.getAbsolutePath()); } /** * Reads the results and save them to the specified file. * * @param f The file to save the results to. */ public void readROIMeasurement(String f) throws IOException { if (CommonsLangUtils.isBlank(f)) return; int totint = Measurements.AREA+Measurements.AREA_FRACTION+ Measurements.CENTER_OF_MASS+Measurements.CENTROID+ Measurements.CIRCULARITY+Measurements.ELLIPSE+ Measurements.FERET+Measurements.KURTOSIS+Measurements.LIMIT+ Measurements.MAX_STANDARDS+Measurements.MEAN+ Measurements.MEDIAN+Measurements.MIN_MAX+Measurements.MODE+ Measurements.PERIMETER+Measurements.RECT+ Measurements.SHAPE_DESCRIPTORS+Measurements.SKEWNESS+ Measurements.SLICE+Measurements.STACK_POSITION+ Measurements.STD_DEV; int precision = Prefs.getInt(PRECISION, 5); RoiManager.getInstance().runCommand("Measure"); Analyzer.setMeasurement(Prefs.getInt(MEASUREMENTS,totint), true); Analyzer.setPrecision(precision); ResultsTable rt = Analyzer.getResultsTable(); rt.updateResults(); rt.show("Results"); rt.saveAs(f); } /** * Reads the results and save them to the specified file. * Returns <code>false</code> if no results to read, <code>true</code> * otherwise. * * @param f The file to save the results to. * @return Returns <c */ public boolean readResults(File f) throws IOException { if (f == null) return false; return readResults(f.getAbsolutePath()); } /** * Reads the results and save them to the specified file. * Returns <code>false</code> if no results to read, <code>true</code> * otherwise. * * @param f The file to save the results to. */ public boolean readResults(String f) throws IOException { ResultsTable rt = ResultsTable.getResultsTable(); if (rt == null || rt.getCounter() == 0) return false; rt.updateResults(); rt.saveAs(f); return true; } }