-
Notifications
You must be signed in to change notification settings - Fork 41
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
Uncrossing polygon functions. #269
base: master
Are you sure you want to change the base?
Conversation
This addresses issue DigitalSlideArchive/HistomicsTK#481. When we eventually separate annotations from image routines, this code will be in the annotations repository. |
@kheffah could provide some good testing |
@cooperlab I still need to write CI tests. If @kheffah has a chance to test with real-world data, that would be better (especially if it turns up any issues that can be incorporated into the CI tests). |
954b5f1
to
f91307c
Compare
f91307c
to
9378395
Compare
@cooperlab, @kheffah I've added tests which uncovered a few edge cases (when a polygon had edges the were colinear and partly overlapping). I think this is ready for real use now. |
This is interesting to me because without the support of hole generation in large_image annotations at the moment, I have been trying to merge interior polygons together using the shapely library, then take the difference with the exterior polygon create a single polygon that can visualize hole regions. The holes can be seen in the middle but the tradeoff is we get all these intersecting lines. I'm hoping that the uncrossPolygon could uncross the lines of that polygon. Is uncrossPolygon a command that can be invoked in python code, ie and that variable can be put within largeAnnotations as seen below? { Thanks! |
9378395
to
4f3ae29
Compare
79ed827
to
7d44a99
Compare
7d44a99
to
096dfae
Compare
@manthey I'm curious if this has been integrated yet? When this becomes enough of a priority, I think it would be ideal if this function is automatically called as soon as the user creates a polygon annotation that crosses itself, so that it gets automatically uncrossed before it is displayed. I'm also assuming this is dependent on integration on "polygons with holes" functionality first. |
No, this hasn't been merged. It was awaiting someone trying it out or reviewing it. Separate from this, we could uncross polygons on the client side as they are being drawn. This is slightly complex, as the client doesn't expect the number of vertices in a polygon to change dynamically, and, ideally, you'd add two vertices at the crossing point. |
@manthey I see. I'll try to find time to try this on some real data when I get the chance. I don't think necessarily that dynamic uncrossing is necessary while the polygon is drawn, but certainly it would be great if the final displayed polygon (when the user releases the mouse) is uncrossed, with two vertices at the crossing point like you mentioned. |
@manthey I tested the uncross polygon on real data from a user who frequently had self-crossing polygons, see the gist below: https://gist.github.com/kheffah/6ff203132391d194021525c2faf0b15d Unfortunately, it seems that the uncrossing method somehow drops self-crossing polygons as opposed to actually uncrossing them. This may have to do with the specific nature of the polygons in the test examples used. If you have time and would like to check the issue, you may access the examples used here since they are public. |
Close-up on self-crossing region that is not handled in one slide (https://candygram.neurology.emory.edu:8080/histomicstk#?image=5e24c0dfddda5f8398695571&bounds=47157%2C24090%2C47870%2C24431%2C0): |
096dfae
to
94ae585
Compare
This adds some utility functions, the most significant of which is uncrossPolygon. This can handle polygons with or without holes. Without holes, it takes a polygon in the form of a list of vertices with each vertex a list or tuple of at least two elements. For a polygon with holes, it takes a list of such polygons, where the first element in the list is the outside polygon and all other elements are the holes. If a polygon self-crosses, additional vertices will be added and part of the sequence of vertices will be reversed to uncross the polygon. If the polygon contains holes, each hole is checked to make sure it doesn't cross other holes or the outline polygon. The resultant polygon will have consistently clockwise vertices (though if the original polygon has multiple loops that only have a vertex in common, not all of the polygon may be consistently oriented). Examples: - `uncrossPolygon([[0, 4], [0, 2], [2, 2], [2, 0], [0, 0], [2, 4]])` results in `[[0, 4], [2, 4], [1, 2], [2, 2], [2, 0], [0, 0], [1, 2], [0, 2]]` - `uncrossPolygon([[[0, 0], [0, 2], [2, 2], [2, 0]], [[1, 1], [1, 3], [3, 3], [3, 1]]])` results in `[[[0, 0], [0, 2], [1, 2], [1, 1], [2, 1], [2, 2], [1, 2], [1, 3], [3, 3], [3, 1], [2, 1], [2, 0]]]`
94ae585
to
5061098
Compare
This adds some utility functions, the most significant of which is
uncrossPolygon
. This can handle polygons with or without holes. Without holes, it takes a polygon in the form of a list of vertices with each vertex a list or tuple of at least two elements. For a polygon with holes, it takes a list of such polygons, where the first element in the list is the outside polygon and all other elements are the holes.If a polygon self-crosses, additional vertices will be added and part of the sequence of vertices will be reversed to uncross the polygon. If the polygon contains holes, each hole is checked to make sure it doesn't cross other holes or the outline polygon.
The resultant polygon will have consistently clockwise vertices (though if the original polygon has multiple loops that only have a vertex in common, not all of the polygon may be consistently oriented).
Examples:
uncrossPolygon([[0, 4], [0, 2], [2, 2], [2, 0], [0, 0], [2, 4]])
results in[[0, 4], [2, 4], [1, 2], [2, 2], [2, 0], [0, 0], [1, 2], [0, 2]]
uncrossPolygon([[[0, 0], [0, 2], [2, 2], [2, 0]], [[1, 1], [1, 3], [3, 3], [3, 1]]])
results in[[[0, 0], [0, 2], [1, 2], [1, 1], [2, 1], [2, 2], [1, 2], [1, 3], [3, 3], [3, 1], [2, 1], [2, 0]]]