imagej / ImageJ

Public domain software for processing and analyzing scientific images
http://imagej.org
Other
576 stars 227 forks source link

Inconsistent point coordinates from 'Polygon' vs. 'Segmented Line' tool (among others) #182

Closed imagingbook closed 2 years ago

imagingbook commented 2 years ago

While both tools create ROIs of type ij.gui.PolygonRoi, the point coordinates returned by getFloatPolygon() are different by 0.5 pixels in x/y. This makes it difficult do render PolygonRoi instances consistently.

In the attached examples, I drew various ROIs on a 10x10 noise image, then rendered the float coordinates of the resulting vertices (red circles). Original TIFFs (with selections) are found in the attached ZIP file. Is the obvious inconsistency between Polygon/Segmented intended?

Example 1: Polygon tool img-PolygonM

Example 2: Segmented Line tool img-SegmentLineM

Looking at other ROI types shows the following situation (see roi-test.zip):

Running IJ 1.53u on Win7, Java 12.0.2 (64-bit)

rasband commented 2 years ago

These inconsistencies are intentional. They date from ImageJ 1.52u, released in March of 2020.

Here are the relevant release notes:

imagingbook commented 2 years ago

Understood, and all this has a long history, but the implementation makes it really hard to determine the "actual" ROI coordinates if needed. Ideally, the differentiation between "area" and "line" ROI (i.e., how coordinates should be interpreted) should be reflected in the class hierarchy of Roi, but this is clearly not the case. For example, a PolygonRoi may be either an "area" ROI or a "line" ROI, depending on the value of its type field! Similarly, a "rectangle" does not even have its own class but is an instance of Roi with type RECTANGLE. Just to illustrate, this is what I currently need to do to find out if a ROI should be rendered with a 0.5 pixel offset or not:

public static boolean isAreaRoi(Roi roi) {
  int type = roi.getType();
  if (roi instanceof EllipseRoi) return true;
  if (roi instanceof OvalRoi) return true;
  if (roi instanceof PolygonRoi && type == Roi.POLYGON) return true;
  if (roi instanceof Roi && type == Roi.RECTANGLE) return true;
  if (roi instanceof RotatedRectRoi) return true;
  return false;
}

This is certainly NOT elegant but I can live with it as long as things remain stable. However, given the circumstances, a better solution would be to add a boolean isAreaRoi() method to each ROI class. A pull request follows ...

rasband commented 2 years ago

The ImageJ 1.53v15 daily build makes the Roi.isLineOrPoint() method public and adds a Roi.translate() method.

Here is a JavaScript example:

  img = IJ.createImage("Untitled", "8-bit noise", 10, 10, 1);
  img.show();
  Zoom.set(img, 32);
  xpoints = [1,8.4,3.5];
  ypoints = [1,3.6,7.7];
  overlay = new Overlay();
  polygon = new PolygonRoi(xpoints,ypoints,Roi.POLYGON);
  overlay.add(polygon);
  points = new PointRoi(xpoints,ypoints,"red extra large circle");
  if (points.isLineOrPoint())  points.translate(-0.5, -0.5);
  overlay.add(points);
  img.setOverlay(overlay);

Screenshot