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

fix: Fix rendering of alternative representation if there are more components in the alternative representation than in ROI #1240

Merged
merged 6 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/project_dict.pws
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
personal_ws-1.1 en 14
personal_ws-1.1 en 15
napari
autoupdate
aspell
Expand All @@ -13,3 +13,4 @@ bool
changelog
czi
PartSeg
ROI
6 changes: 4 additions & 2 deletions package/PartSeg/common_gui/napari_image_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,10 @@ def get_roi_view_parameters(self, image_info: ImageInfo) -> ColorInfo:
or colors.size == 0
):
return {0: [0, 0, 0, 0], None: [0, 0, 0, 0]}

res = {x: colors[(x - 1) % colors.shape[0]] for x in range(1, image_info.roi_count + 1)}
res = {
x: colors[(x - 1) % colors.shape[0]]
for x in range(1, image_info.roi_info.get_components_num(self.roi_alternative_selection) + 1)
}
res[0] = [0, 0, 0, 0]
res[None] = [0, 0, 0, 0]
return res
Expand Down
18 changes: 18 additions & 0 deletions package/PartSegCore/roi_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def __init__(
self.roi = roi
self.bound_info = self.calc_bounds(roi)
self.sizes = np.bincount(roi.flat)
self._alternative_component_size = {}

def fit_to_image(self, image: Image) -> "ROIInfo":
if self.roi is None:
Expand All @@ -70,6 +71,23 @@ def fit_to_image(self, image: Image) -> "ROIInfo":
alternatives = {k: image.fit_array_to_image(v) for k, v in self.alternative.items()}
return ROIInfo(roi, self.annotations, alternatives)

def get_components_num(self, name):
"""
Get the number of components for a given representation.

Args:
name (str): Name of the representation. Use "ROI" for the main ROI,
or the name of an alternative representation.

Returns:
int: Maximum component number in the specified representation.
"""
if name == "ROI" or name not in self.alternative:
return max(self.bound_info)
if name not in self._alternative_component_size:
self._alternative_component_size[name] = np.max(self.alternative[name])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: Consider handling the case where self.alternative[name] is empty to avoid ValueError from np.max

np.max will raise a ValueError on empty arrays. Consider returning 0 for empty arrays or adding a check using if self.alternative[name].size > 0.

return self._alternative_component_size[name]

def __str__(self):
return f"ROIInfo; components: {len(self.bound_info)}, sizes: {self.sizes}"

Expand Down
17 changes: 16 additions & 1 deletion package/tests/test_PartSegCore/test_segmentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ def test__convex_fill(self):
assert _convex_fill(arr) is None


class TestSegmentationInfo:
class TestROIInfo:
def test_none(self):
si = ROIInfo(None)
assert si.roi is None
Expand Down Expand Up @@ -817,6 +817,21 @@ def test_multiple_components(self, comp_num):
assert np.all(si.bound_info[1].lower == 2)
assert np.all(si.bound_info[1].upper == [10 * comp_num - 1, 8])

def test_alternative(self):
data = np.zeros((10, 10), dtype=np.uint8)
data[2:8, 2:4] = 1
data[2:8, 4:8] = 2
alt1 = np.copy(data)
alt1[data == 1] = 4
alt1[data == 2] = 1
alt2 = np.zeros((10, 10), dtype=np.uint8)

ri = ROIInfo(data, alternative={"alt1": alt1, "alt2": alt2})
assert ri.get_components_num("ROI") == 2
assert ri.get_components_num("alt1") == 4
assert ri.get_components_num("alt2") == 0
assert ri.get_components_num("alt3") == 2


def test_bound_info():
bi = BoundInfo(lower=np.array([1, 1, 1]), upper=np.array([5, 6, 7]))
Expand Down
Loading