//
// ROIHandler.java
//
/*
LOCI Plugins for ImageJ: a collection of ImageJ plugins including the
Bio-Formats Importer, Bio-Formats Exporter, Bio-Formats Macro Extensions,
Data Browser and Stack Slicer. Copyright (C) 2005-@year@ Melissa Linkert,
Curtis Rueden and Christopher Peterson.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package loci.plugins.util;
import ij.ImagePlus;
import ij.gui.Line;
import ij.gui.OvalRoi;
import ij.gui.PolygonRoi;
import ij.gui.Roi;
import ij.gui.ShapeRoi;
import ij.gui.TextRoi;
import ij.plugin.frame.RoiManager;
import java.awt.Color;
import java.awt.Rectangle;
import loci.formats.meta.IMetadata;
import loci.formats.meta.MetadataStore;
import loci.formats.ome.OMEXMLMetadata;
import ome.xml.model.Ellipse;
import ome.xml.model.Image;
import ome.xml.model.OME;
import ome.xml.model.Point;
import ome.xml.model.Polyline;
import ome.xml.model.Shape;
import ome.xml.model.Union;
// TODO: Stored ROIs are not correctly linked to Image.
/**
* Utility class for managing regions of interest within ImageJ.
* Capable of constructing ROIs within ImageJ's ROI manager matching
* those specified in an OME metadata store, and vice versa.
*
* <dl><dt><b>Source code:</b></dt>
* <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/loci-plugins/src/loci/plugins/util/ROIHandler.java">Trac</a>,
* <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/loci-plugins/src/loci/plugins/util/ROIHandler.java;hb=HEAD">Gitweb</a></dd></dl>
*
* @author Melissa Linkert melissa at glencoesoftware.com
*/
public class ROIHandler {
// -- ROIHandler API methods --
/**
* Look for ROIs in the given OMEXMLMetadata; if any are present, apply
* them to the given images and display them in the ROI manager.
*/
public static void openROIs(IMetadata retrieve, ImagePlus[] images) {
if (!(retrieve instanceof OMEXMLMetadata)) return;
int nextRoi = 0;
RoiManager manager = RoiManager.getInstance();
OME root = (OME) retrieve.getRoot();
int imageCount = images.length;
for (int imageNum=0; imageNum<imageCount; imageNum++) {
int roiCount = root.sizeOfROIList();
if (roiCount > 0 && manager == null) {
manager = new RoiManager();
}
for (int roiNum=0; roiNum<roiCount; roiNum++) {
Union shapeSet = root.getROI(roiNum).getUnion();
int shapeCount = shapeSet.sizeOfShapeList();
for (int shape=0; shape<shapeCount; shape++) {
Shape shapeObject = shapeSet.getShape(shape);
Roi roi = null;
if (shapeObject instanceof Ellipse) {
Ellipse ellipse = (Ellipse) shapeObject;
int cx = ellipse.getX().intValue();
int cy = ellipse.getY().intValue();
int rx = ellipse.getRadiusX().intValue();
int ry = ellipse.getRadiusY().intValue();
roi = new OvalRoi(cx - rx, cy - ry, rx * 2, ry * 2);
}
else if (shapeObject instanceof ome.xml.model.Line) {
ome.xml.model.Line line = (ome.xml.model.Line) shapeObject;
int x1 = line.getX1().intValue();
int x2 = line.getX2().intValue();
int y1 = line.getY1().intValue();
int y2 = line.getY2().intValue();
roi = new Line(x1, y1, x2, y2);
}
else if (shapeObject instanceof Point) {
Point point = (Point) shapeObject;
int x = point.getX().intValue();
int y = point.getY().intValue();
roi = new OvalRoi(x, y, 0, 0);
}
else if (shapeObject instanceof Polyline) {
Polyline polyline = (Polyline) shapeObject;
String points = polyline.getPoints();
int[][] coordinates = parsePoints(points);
boolean closed = polyline.getClosed();
roi = new PolygonRoi(coordinates[0], coordinates[1],
coordinates[0].length, closed ? Roi.POLYGON : Roi.POLYLINE);
}
else if (shapeObject instanceof ome.xml.model.Rectangle) {
ome.xml.model.Rectangle rectangle =
(ome.xml.model.Rectangle) shapeObject;
int x = rectangle.getX().intValue();
int y = rectangle.getY().intValue();
int w = rectangle.getWidth().intValue();
int h = rectangle.getHeight().intValue();
String label = shapeObject.getLabel();
if (label != null) {
roi = new TextRoi(x, y, label);
}
else {
roi = new Roi(x, y, w, h);
}
}
if (roi != null) {
Roi.setColor(Color.WHITE);
roi.setImage(images[imageNum]);
manager.add(images[imageNum], roi, nextRoi++);
}
}
}
}
}
/** Save ROIs in the ROI manager to the given MetadataStore. */
public static void saveROIs(MetadataStore store) {
RoiManager manager = RoiManager.getInstance();
if (manager == null) return;
Roi[] rois = manager.getRoisAsArray();
for (int i=0; i<rois.length; i++) {
if (rois[i] instanceof Line) {
storeLine((Line) rois[i], store, i, 0);
}
else if (rois[i] instanceof PolygonRoi) {
storePolygon((PolygonRoi) rois[i], store, i, 0);
}
else if (rois[i] instanceof ShapeRoi) {
Roi[] subRois = ((ShapeRoi) rois[i]).getRois();
for (int q=0; q<subRois.length; q++) {
if (subRois[q] instanceof Line) {
storeLine((Line) subRois[q], store, i, q);
}
else if (subRois[q] instanceof PolygonRoi) {
storePolygon((PolygonRoi) subRois[q], store, i, q);
}
else if (subRois[q] instanceof OvalRoi) {
storeOval((OvalRoi) subRois[q], store, i, q);
}
else storeRectangle(subRois[q], store, i, q);
}
}
else if (rois[i] instanceof OvalRoi) {
storeOval((OvalRoi) rois[i], store, i, 0);
}
else storeRectangle(rois[i], store, i, 0);
}
}
// -- Helper methods --
/** Store a Line ROI in the given MetadataStore. */
private static void storeLine(Line roi, MetadataStore store,
int roiNum, int shape)
{
store.setLineX1(new Double(roi.x1), roiNum, shape);
store.setLineX2(new Double(roi.x2), roiNum, shape);
store.setLineY1(new Double(roi.y1), roiNum, shape);
store.setLineY2(new Double(roi.y2), roiNum, shape);
}
/** Store an Roi (rectangle) in the given MetadataStore. */
private static void storeRectangle(Roi roi, MetadataStore store,
int roiNum, int shape)
{
Rectangle bounds = roi.getBounds();
store.setRectangleX(new Double(bounds.x), roiNum, shape);
store.setRectangleY(new Double(bounds.y), roiNum, shape);
store.setRectangleWidth(new Double(bounds.width), roiNum, shape);
store.setRectangleHeight(new Double(bounds.height), roiNum, shape);
}
/** Store a Polygon ROI in the given MetadataStore. */
private static void storePolygon(PolygonRoi roi, MetadataStore store,
int roiNum, int shape)
{
Rectangle bounds = roi.getBounds();
int[] xCoordinates = roi.getXCoordinates();
int[] yCoordinates = roi.getYCoordinates();
StringBuffer points = new StringBuffer();
for (int i=0; i<xCoordinates.length; i++) {
points.append(xCoordinates[i] + bounds.x);
points.append(",");
points.append(yCoordinates[i] + bounds.y);
if (i < xCoordinates.length - 1) points.append(" ");
}
store.setPolylinePoints(points.toString(), roiNum, shape);
store.setPolylineClosed(Boolean.TRUE, roiNum, shape);
}
/** Store an Oval ROI in the given MetadataStore. */
@SuppressWarnings("unused")
private static void storeOval(OvalRoi roi,
MetadataStore store, int roiNum, int shape)
{
// TODO: storeOval
}
/**
* Parse (x, y) coordinates from a String returned by
* MetadataRetrieve.getPolygonpoints(...) or
* MetadataRetrieve.getPolylinepoints(...)
*/
private static int[][] parsePoints(String points) {
// assuming points are stored like this:
// x0,y0 x1,y1 x2,y2 ...
String[] pointList = points.split(" ");
int[][] coordinates = new int[2][pointList.length];
for (int q=0; q<pointList.length; q++) {
pointList[q] = pointList[q].trim();
int delim = pointList[q].indexOf(",");
coordinates[0][q] =
(int) Double.parseDouble(pointList[q].substring(0, delim));
coordinates[1][q] =
(int) Double.parseDouble(pointList[q].substring(delim + 1));
}
return coordinates;
}
}