Skip to content

Commit 8b0e1cf

Browse files
authored
🐛 Fix Rendering Annotations with Holes (#813)
This PR adds correct rendering of annotations with holes. Previously, annotations with holes would not be rendered. This PR fixes the issue. ![image](https://github.com/TissueImageAnalytics/tiatoolbox/assets/20169086/a6fb3576-9a58-4416-a676-e2d5d030bedc) Previously an overlay like the above, with a geometry with holes in it, would not display.
1 parent ec5a373 commit 8b0e1cf

File tree

2 files changed

+31
-21
lines changed

2 files changed

+31
-21
lines changed

tests/test_annotation_tilerendering.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
def cell_grid() -> list[Polygon]:
3434
"""Generate a grid of fake cell boundary polygon annotations."""
3535
return [
36-
cell_polygon(((i + 0.5) * 100, (j + 0.5) * 100)) for i, j in np.ndindex(5, 5)
36+
cell_polygon(((i + 0.5) * 100, (j + 0.5) * 100), radius=13)
37+
for i, j in np.ndindex(5, 5)
3738
]
3839

3940

@@ -168,7 +169,7 @@ def test_filter_by_expression(fill_store: Callable, tmp_path: Path) -> None:
168169
tg = AnnotationTileGenerator(wsi.info, store, renderer, tile_size=256)
169170
thumb = tg.get_thumb_tile()
170171
_, num = label(np.array(thumb)[:, :, 1])
171-
assert num == 25 # expect 25 cell objects, as the added one is too small
172+
assert num == 25 # expect 25 cell objects
172173

173174

174175
def test_zoomed_out_rendering(fill_store: Callable, tmp_path: Path) -> None:
@@ -190,7 +191,7 @@ def test_zoomed_out_rendering(fill_store: Callable, tmp_path: Path) -> None:
190191

191192
thumb = tg.get_tile(1, 0, 0)
192193
_, num = label(np.array(thumb)[:, :, 1]) # default color is green
193-
assert num == 25 # expect 25 cells in top left quadrant
194+
assert num == 25 # expect 25 cells in top left quadrant (added one too small)
194195

195196

196197
def test_decimation(fill_store: Callable, tmp_path: Path) -> None:
@@ -434,7 +435,7 @@ def test_unfilled_polys(fill_store: Callable, tmp_path: Path) -> None:
434435
renderer = AnnotationRenderer(thickness=1)
435436
tg = AnnotationTileGenerator(wsi.info, store, renderer, tile_size=256)
436437
tile_outline = np.array(tg.get_tile(1, 0, 0))
437-
tg.renderer.edge_thickness = -1
438+
tg.renderer.thickness = -1
438439
tile_filled = np.array(tg.get_tile(1, 0, 0))
439440
# expect sum of filled polys to be much greater than sum of outlines
440441
assert np.sum(tile_filled) > 2 * np.sum(tile_outline)

tiatoolbox/utils/visualization.py

+26-17
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,10 @@ def to_tile_coords(
661661
Array of coordinates in tile space in the form [x, y].
662662
663663
"""
664-
return ((np.reshape(coords, (-1, 2)) - top_left) / scale).astype(np.int32)
664+
return [
665+
((np.reshape(ring, (-1, 2)) - top_left) / scale).astype(np.int32)
666+
for ring in coords
667+
]
665668

666669
def get_color(
667670
self: AnnotationRenderer,
@@ -756,19 +759,26 @@ def render_poly(
756759
scale,
757760
)
758761
if self.thickness > -1:
759-
cv2.drawContours(
762+
cv2.polylines(
760763
tile,
761-
[cnt],
762-
0,
763-
col,
764-
self.edge_thickness,
764+
cnt,
765+
isClosed=True,
766+
color=col,
767+
thickness=self.edge_thickness,
765768
lineType=cv2.LINE_8,
766769
)
767770
else:
768-
cv2.drawContours(tile, [cnt], 0, col, self.thickness, lineType=cv2.LINE_8)
771+
cv2.fillPoly(tile, cnt, col)
769772
if self.thickness == -1 and self.edge_thickness > 0:
770773
edge_col = self.get_color(annotation, edge=True)
771-
cv2.drawContours(tile, [cnt], 0, edge_col, 1, lineType=cv2.LINE_8)
774+
cv2.polylines(
775+
tile,
776+
cnt,
777+
isClosed=True,
778+
color=edge_col,
779+
thickness=1,
780+
lineType=cv2.LINE_8,
781+
)
772782

773783
def render_multipoly(
774784
self: AnnotationRenderer,
@@ -782,7 +792,7 @@ def render_multipoly(
782792
geoms = annotation.coords
783793
for poly in geoms:
784794
cnt = self.to_tile_coords(poly, top_left, scale)
785-
cv2.drawContours(tile, [cnt], 0, col, self.thickness, lineType=cv2.LINE_8)
795+
cv2.fillPoly(tile, cnt, col)
786796

787797
def render_pt(
788798
self: AnnotationRenderer,
@@ -811,7 +821,7 @@ def render_pt(
811821
annotation.coords,
812822
top_left,
813823
scale,
814-
)[0],
824+
)[0][0],
815825
np.maximum(self.edge_thickness, 1),
816826
col,
817827
thickness=self.thickness,
@@ -838,15 +848,14 @@ def render_line(
838848
839849
"""
840850
col = self.get_color(annotation, edge=False)
851+
cnt = self.to_tile_coords(
852+
list(annotation.coords),
853+
top_left,
854+
scale,
855+
)
841856
cv2.polylines(
842857
tile,
843-
[
844-
self.to_tile_coords(
845-
list(annotation.coords),
846-
top_left,
847-
scale,
848-
),
849-
],
858+
[np.array(cnt)],
850859
isClosed=False,
851860
color=col,
852861
thickness=3,

0 commit comments

Comments
 (0)