Thanks to visit codestin.com
Credit goes to github.com

Skip to content
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
1 change: 1 addition & 0 deletions .github/project_dict.pws
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ numpy
pre
bool
changelog
czi
4 changes: 2 additions & 2 deletions package/PartSegImage/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -916,8 +916,8 @@ def get_imagej_colors(self):
res.append(color)
return res

def get_colors(self):
res = []
def get_colors(self) -> list[str | list[int]]:
res: list[str | list[int]] = []
for color in self.default_coloring:
if isinstance(color, str):
res.append(color)
Expand Down
40 changes: 18 additions & 22 deletions package/PartSegImage/image_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@
CZI_MAX_WORKERS = None


def li_if_no(value):
if value is None:
return []
return value


class ZSTD1Header(typing.NamedTuple):
"""
ZSTD1 header structure
Expand Down Expand Up @@ -129,15 +123,23 @@ def return_order(cls) -> str:
"""
return cls.image_class.axis_order

def __init__(self, callback_function=None):
def __init__(self, callback_function: typing.Optional[typing.Callable[[str, int], typing.Any]] = None) -> None:
self.default_spacing = 10**-6, 10**-6, 10**-6
self.spacing = self.default_spacing
self.channel_names = None
self.channel_names: typing.List[str] = []
self.colors: typing.List[typing.Optional[typing.Any]] = []
self.ranges: typing.List[typing.Tuple[float, float]] = []
if callback_function is None:
self.callback_function = _empty
else:
self.callback_function = callback_function

def _get_channel_info(self) -> typing.List[ChannelInfo]:
return [
ChannelInfo(name=name, color_map=color, contrast_limits=contrast_limits)
for name, color, contrast_limits in zip_longest(self.channel_names, self.colors, self.ranges)
]

def set_default_spacing(self, spacing):
spacing = tuple(spacing)
if len(spacing) == 2:
Expand Down Expand Up @@ -368,11 +370,13 @@ def read(self, image_path: typing.Union[str, BytesIO, Path], mask_path=None, ext
scale_info.get("Y", self.default_spacing[1]),
scale_info.get("X", self.default_spacing[2]),
)
with suppress(KeyError):
channel_meta = metadata["ImageDocument"]["Metadata"]["DisplaySetting"]["Channels"]["Channel"]
if isinstance(channel_meta, dict):
# single channel saved in czifile
channel_meta = [channel_meta]
self.channel_names = [x["Name"] for x in channel_meta]
self.channel_names = [x.get("Name", f"Channel_{i}") for i, x in enumerate(channel_meta, start=1)]
self.colors = [x.get("Color") for x in channel_meta]
# TODO add mask reading
if isinstance(image_path, BytesIO):
image_path = ""
Expand All @@ -383,7 +387,7 @@ def read(self, image_path: typing.Union[str, BytesIO, Path], mask_path=None, ext
file_path=image_path,
axes_order=self.return_order(),
metadata_dict=metadata,
channel_info=[ChannelInfo(name=name) for name in self.channel_names or []],
channel_info=self._get_channel_info(),
)

@classmethod
Expand Down Expand Up @@ -462,8 +466,6 @@ class TiffImageReader(BaseImageReaderBuffer):

def __init__(self, callback_function=None):
super().__init__(callback_function)
self.colors = None
self.ranges = None
self.shift = (0, 0, 0)
self.name = ""
self.metadata = {}
Expand All @@ -472,7 +474,7 @@ def read(self, image_path: typing.Union[str, BytesIO, Path], mask_path=None, ext
"""
Read tiff image from tiff_file
"""
self.spacing, self.colors, self.channel_names, self.ranges = self.default_spacing, None, None, None
self.spacing, self.colors, self.channel_names, self.ranges = self.default_spacing, [], [], []
with tifffile.TiffFile(image_path) as image_file:
total_pages_num = len(image_file.series[0])

Expand Down Expand Up @@ -522,17 +524,11 @@ def report_func():

if not isinstance(image_path, (str, Path)):
image_path = ""
channel_info = [
ChannelInfo(name=name, color_map=color, contrast_limits=contrast_limits)
for name, color, contrast_limits in zip_longest(
li_if_no(self.channel_names), li_if_no(self.colors), li_if_no(self.ranges)
)
]
return self.image_class(
image_data,
spacing=self.spacing,
mask=mask_data,
channel_info=channel_info,
channel_info=self._get_channel_info(),
file_path=os.path.abspath(image_path),
axes_order=self.return_order(),
shift=self.shift,
Expand Down Expand Up @@ -599,8 +595,8 @@ def read_imagej_metadata(self, image_file):
z_spacing = self.default_spacing[0]
x_spacing, y_spacing = self.read_resolution_from_tags(image_file)
self.spacing = z_spacing, y_spacing, x_spacing
self.colors = image_file.imagej_metadata.get("LUTs")
self.channel_names = image_file.imagej_metadata.get("Labels")
self.colors = image_file.imagej_metadata.get("LUTs", [])
self.channel_names = image_file.imagej_metadata.get("Labels", [])
if "Ranges" in image_file.imagej_metadata:
ranges = image_file.imagej_metadata["Ranges"]
self.ranges = list(zip(ranges[::2], ranges[1::2]))
Expand Down
2 changes: 2 additions & 0 deletions package/tests/test_PartSegImage/test_image_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ def test_tiff_image_read_buffer(self):
assert np.all(np.isclose(image.spacing, (7.752248561753867e-08,) * 2))

def test_czi_file_read(self, data_test_dir):
"""Check if czi file is read correctly."""
image = CziImageReader.read_image(os.path.join(data_test_dir, "test_czi.czi"))
assert np.count_nonzero(image.get_channel(0))
assert image.channels == 4
assert image.layers == 1
assert image.get_colors() == ["#FFFFFF", "#FF0000", "#00FF00", "#0000FF"]
Comment on lines 42 to +43
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (testing): Consider adding more comprehensive tests for the new color reading functionality

While the added assertion checks for the correct colors, it would be beneficial to add more tests to cover different scenarios. For example, test cases for CZI files with a single channel, files without color information, or files with non-standard color representations. This would ensure the new functionality is robust across various input types.

        assert image.layers == 1
        assert image.get_colors() == ["#FFFFFF", "#FF0000", "#00FF00", "#0000FF"]

        # Test single channel CZI file
        single_channel_image = load_image(single_channel_czi_path)
        assert single_channel_image.layers == 1
        assert len(single_channel_image.get_colors()) == 1

        # Test CZI file without color information
        no_color_image = load_image(no_color_czi_path)
        assert no_color_image.get_colors() == []


assert image.file_path == os.path.join(data_test_dir, "test_czi.czi")

Expand Down
Loading