|
12 | 12 | import numpy as np
|
13 | 13 | import pandas as pd
|
14 | 14 | import pytest
|
| 15 | +import tifffile |
15 | 16 | import torch
|
| 17 | +import zarr |
| 18 | +from defusedxml import ElementTree as ET # noqa: N817 |
16 | 19 | from PIL import Image
|
17 | 20 | from requests import HTTPError
|
18 | 21 | from shapely.geometry import Polygon
|
| 22 | +from tifffile import TiffFile |
19 | 23 |
|
20 | 24 | from tests.test_annotation_stores import cell_polygon
|
21 | 25 | from tiatoolbox import rcParam, utils
|
@@ -1858,3 +1862,123 @@ def test_torch_compile_compatibility(caplog: pytest.LogCaptureFixture) -> None:
|
1858 | 1862 |
|
1859 | 1863 | is_torch_compile_compatible()
|
1860 | 1864 | assert "torch.compile" in caplog.text
|
| 1865 | + |
| 1866 | + |
| 1867 | +# Tests for OME tiff writer |
| 1868 | + |
| 1869 | + |
| 1870 | +def get_ome_metadata(tiff_path: Path) -> str | None: |
| 1871 | + """Extracts the OME metadata string from a TIFF file.""" |
| 1872 | + with TiffFile(tiff_path) as tif: |
| 1873 | + if tif.ome_metadata: |
| 1874 | + return tif.ome_metadata |
| 1875 | + return None |
| 1876 | + |
| 1877 | + |
| 1878 | +def assert_ome_metadata_value( |
| 1879 | + ome_xml: ET.Element, tag: str, expected_value: str |
| 1880 | +) -> None: |
| 1881 | + """Asserts the value of a specific OME metadata tag (as an attribute).""" |
| 1882 | + namespace = "{http://www.openmicroscopy.org/Schemas/OME/2016-06}" |
| 1883 | + image_elements = ome_xml.findall(f".//{namespace}Image") |
| 1884 | + if image_elements: |
| 1885 | + pixels_elements = image_elements[0].findall(f"./{namespace}Pixels") |
| 1886 | + if pixels_elements: |
| 1887 | + actual_value = pixels_elements[0].get(tag) |
| 1888 | + assert actual_value == expected_value, ( |
| 1889 | + f"Expected attribute '{tag}' to be '{expected_value}', " |
| 1890 | + f"but got '{actual_value}'." |
| 1891 | + ) |
| 1892 | + return |
| 1893 | + |
| 1894 | + # If we reach here, the tag or attribute was not found |
| 1895 | + pytest.fail(f"Attribute '{tag}' not found in OME metadata.") |
| 1896 | + |
| 1897 | + |
| 1898 | +def test_iwrite_probability_heatmap_as_ome_tiff_errors(tmp_path: Path) -> None: |
| 1899 | + """Test expected errors in `write_probability_heatmap_as_ome_tiff`.""" |
| 1900 | + probability = np.zeros(shape=(256, 256, 3)) |
| 1901 | + |
| 1902 | + # Input image must have 2 (CY) dimensions. |
| 1903 | + with pytest.raises(ValueError, match=r".*must have 2 \(YX\).*"): |
| 1904 | + misc.write_probability_heatmap_as_ome_tiff( |
| 1905 | + image_path=tmp_path / "failed_test.tif", |
| 1906 | + probability=probability, |
| 1907 | + ) |
| 1908 | + |
| 1909 | + probability = np.zeros(shape=(256, 256, 3)) |
| 1910 | + probability = torch.from_numpy(probability) |
| 1911 | + |
| 1912 | + # Input image must be a NumPy array or a Zarr array. |
| 1913 | + with pytest.raises(TypeError, match=r".*must be a NumPy array or a Zarr.*"): |
| 1914 | + misc.write_probability_heatmap_as_ome_tiff( |
| 1915 | + image_path=tmp_path / "failed_test.tif", |
| 1916 | + probability=probability, |
| 1917 | + ) |
| 1918 | + |
| 1919 | + |
| 1920 | +def test_save_numpy_array_proability_ome_tiff( |
| 1921 | + tmp_path: Path, source_image: Path |
| 1922 | +) -> None: |
| 1923 | + """Tests saving a basic NumPy array.""" |
| 1924 | + image_path = tmp_path / "numpy_image.ome.tif" |
| 1925 | + probability = utils.imread(source_image) |
| 1926 | + probability_0 = probability[:, :, 0] |
| 1927 | + misc.write_probability_heatmap_as_ome_tiff( |
| 1928 | + image_path=image_path, |
| 1929 | + probability=probability_0, |
| 1930 | + tile_size=(64, 64), |
| 1931 | + mpp=(0.5, 0.5), |
| 1932 | + levels=2, |
| 1933 | + colormap=cv2.COLORMAP_JET, |
| 1934 | + ) |
| 1935 | + assert image_path.is_file() |
| 1936 | + saved_img = tifffile.imread(image_path) |
| 1937 | + assert probability.shape == saved_img.shape |
| 1938 | + assert probability.dtype == saved_img.dtype |
| 1939 | + ome_xml = ET.fromstring(get_ome_metadata(image_path)) |
| 1940 | + assert ome_xml is not None |
| 1941 | + |
| 1942 | + assert_ome_metadata_value(ome_xml, "SizeY", str(probability.shape[0])) |
| 1943 | + assert_ome_metadata_value(ome_xml, "SizeX", str(probability.shape[1])) |
| 1944 | + assert_ome_metadata_value(ome_xml, "SizeC", str(3)) |
| 1945 | + assert_ome_metadata_value(ome_xml, "DimensionOrder", "XYCZT") |
| 1946 | + assert_ome_metadata_value(ome_xml, "PhysicalSizeX", "0.5") |
| 1947 | + assert_ome_metadata_value(ome_xml, "PhysicalSizeY", "0.5") |
| 1948 | + assert_ome_metadata_value(ome_xml, "PhysicalSizeXUnit", "µm") |
| 1949 | + assert_ome_metadata_value(ome_xml, "PhysicalSizeYUnit", "µm") |
| 1950 | + |
| 1951 | + |
| 1952 | +def test_save_zarr_array_probability_ome_tiff( |
| 1953 | + tmp_path: Path, source_image: Path |
| 1954 | +) -> None: |
| 1955 | + """Tests saving a Zarr array with uint8 dtype.""" |
| 1956 | + image_path = tmp_path / "zarr_uint8_image.ome.tif" |
| 1957 | + |
| 1958 | + img = utils.imread(source_image) |
| 1959 | + probability = img[:, 0:200, 0] |
| 1960 | + img_zarr = zarr.zeros(shape=probability.shape, dtype=np.uint8) |
| 1961 | + img_zarr[:] = probability |
| 1962 | + |
| 1963 | + misc.write_probability_heatmap_as_ome_tiff( |
| 1964 | + image_path, |
| 1965 | + img_zarr, |
| 1966 | + tile_size=(32, 32), |
| 1967 | + levels=2, |
| 1968 | + colormap=cv2.COLORMAP_INFERNO, |
| 1969 | + ) |
| 1970 | + assert image_path.is_file() |
| 1971 | + saved_img = tifffile.imread(image_path, squeeze=True) |
| 1972 | + assert img_zarr.shape == saved_img.shape[0:2] |
| 1973 | + assert img_zarr.dtype == saved_img.dtype |
| 1974 | + ome_xml = ET.fromstring(get_ome_metadata(image_path)) |
| 1975 | + assert ome_xml is not None |
| 1976 | + |
| 1977 | + assert_ome_metadata_value(ome_xml, "SizeY", str(img_zarr.shape[0])) |
| 1978 | + assert_ome_metadata_value(ome_xml, "SizeX", str(img_zarr.shape[1])) |
| 1979 | + assert_ome_metadata_value(ome_xml, "SizeC", str(3)) |
| 1980 | + assert_ome_metadata_value(ome_xml, "DimensionOrder", "XYCZT") |
| 1981 | + assert_ome_metadata_value(ome_xml, "PhysicalSizeX", "0.25") |
| 1982 | + assert_ome_metadata_value(ome_xml, "PhysicalSizeY", "0.25") |
| 1983 | + assert_ome_metadata_value(ome_xml, "PhysicalSizeXUnit", "µm") |
| 1984 | + assert_ome_metadata_value(ome_xml, "PhysicalSizeYUnit", "µm") |
0 commit comments