Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ShapeRoi does not work correctly with "line-like" (`isLine()`) rois #83

Closed
mountain-maennlein opened this issue Sep 10, 2019 · 2 comments

Comments

@mountain-maennlein
Copy link

commented Sep 10, 2019

ShapeRoi handles "open" ("line-like") rois incorrectly, most notably
when performing set arithmetic using functions such as or() (union),
or and() (intersection) (and others). The root cause of this is that
ShapeRoi delegates set arithmetic to java.awt.geom.Area and Area
explicitly limits itself to representing "an enclosed area of 2-dimensional
space."

(The ShapeRoi (Shape) constructor presumably suffers from an analogous
issue, and a similar fix is likely to work. Also, RoiManager performs
significant preprocessing before delegating some of the set arithmetic
it performs to ShaepRoi. It would make sense to fix ShapeRoi properly,
and then have RoiManager delegate all of its set arithmetic directly to
ShapeRoi without any non-trivial preprocessing.)

Here is a link to a contemporaneous forum thread that further discusses
this issue and the proposed fix:

https://forum.image.sc/t/proposed-fix-for-shaperoi-issue/29391

Here is an earlier forum thread about this issue:

https://forum.image.sc/t/shaperoi-or-and-shaperoi-getcontainedpoints-fail-with-line-line-roi/27964

The core issue is illustrated by the following test script:

from ij.gui import Line
from ij.gui import PolygonRoi
from ij.gui import Roi
from ij.gui import ShapeRoi

rect = Roi (0, 0, 20, 20)
sr_rect = ShapeRoi (rect)
# good
print 'len (rect.getContainedPoints()) =', len (rect.getContainedPoints())
# good
print 'len (sr_rect.getContainedPoints()) =', len (sr_rect.getContainedPoints())
sr_rect_or_rect = sr_rect.or (ShapeRoi (Roi (0, 30, 20, 20)))
# good
print 'len (sr_rect_or_rect.getContainedPoints()) =', len (sr_rect_or_rect.getContainedPoints())

line = Line (0, 0, 20, 0)
sr_line = ShapeRoi (line)
# good
print 'len (line.getContainedPoints()) =', len (line.getContainedPoints())
# wrong -- getContainedPoints is empty
print 'len (sr_line.getContainedPoints()) =', len (sr_line.getContainedPoints())
sr_line_or_line = sr_line.or (ShapeRoi (Line (0, 10, 20, 10)))
# wrong -- conversion to Area in or() causes Line to vanish entirely
print 'len (sr_line_or_line.getContainedPoints()) =', len (sr_line_or_line.getContainedPoints())

polyline = PolygonRoi ([0, 10, 10], [0, 0, 10], Roi.POLYLINE)
sr_polyline = ShapeRoi (polyline)
# good
print 'len (polyline.getContainedPoints()) =', len (polyline.getContainedPoints())
# wrong -- ShapeRoi acts like the interior of a closed triangle
print 'len (sr_polyline.getContainedPoints()) =', len (sr_polyline.getContainedPoints())
sr_polyline_or_polyline = sr_polyline.or (ShapeRoi (PolygonRoi ([0, 10, 10], [20, 20, 30], Roi.POLYLINE)))
# wrong -- conversion to Area still acts like the interior of a closed triangle
print 'len (sr_polyline_or_polyline.getContainedPoints()) =', len (sr_polyline_or_polyline.getContainedPoints())

The output of the script (run with ImageJ 2.0.0-rc-69/1.52p;
Java 1.8.0_172 [64-bit]) is:

len (rect.getContainedPoints()) = 400
len (sr_rect.getContainedPoints()) = 400
len (sr_rect_or_rect.getContainedPoints()) = 800
len (line.getContainedPoints()) = 21
len (sr_line.getContainedPoints()) = 0
len (sr_line_or_line.getContainedPoints()) = 0
len (polyline.getContainedPoints()) = 21
len (sr_polyline.getContainedPoints()) = 45
len (sr_polyline_or_polyline.getContainedPoints()) = 90

The proposed fix for this issue is to modify ShapeRoi.roiToShape() so
that it converts any line-like Roi into an Area composed of rectangles,
one for each point (pixel) in the original Roi. (This is what roiToShape()
already does for PointRoi.)

Here is the substantive part of the fix to ShapeRoi.roiToShape():

		// size of rectangular "points" for building open-shape areas
		final double rectanglePointSize = 0.75;
		// ...
		if (!roi.isArea()) {   // assumed equivalent to  isLine() || isPoint()
		    Area area = new Area();
		    Point[] aPoints = roi.getContainedPoints();
		    for (Point p : aPoints) {
			area.add (new Area (new Rectangle2D.Double ((double) (p.x - r.x), (double) (p.y - r.y), rectanglePointSize, rectanglePointSize)));
		    }
		    shape = area;
		}

If people think this makes sense, my plan would be to solicit input
from interested parties, finish a more complete version of a fixed
ShapeRoi.java, and make it available for further testing and review.

@ctrueden

This comment has been minimized.

Copy link
Member

commented Sep 10, 2019

See my reply on the forum. I think it makes more sense to use the latest ImgLib2 ROI library, rather than potentially breaking backwards compatibility of existing ImageJ1 extensions.

@rasband

This comment has been minimized.

Copy link
Member

commented Sep 11, 2019

This bug is fixed in the latest ImageJ daily build (1.52q47). Code changes are at
cd3dd6e

@rasband rasband closed this Sep 11, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.