diff --git a/lib/galaxy/config/sample/datatypes_conf.xml.sample b/lib/galaxy/config/sample/datatypes_conf.xml.sample
index 42f728dd1d17..4008d53befd7 100644
--- a/lib/galaxy/config/sample/datatypes_conf.xml.sample
+++ b/lib/galaxy/config/sample/datatypes_conf.xml.sample
@@ -313,6 +313,7 @@
+
@@ -1445,6 +1446,7 @@
+
diff --git a/lib/galaxy/datatypes/images.py b/lib/galaxy/datatypes/images.py
index 5b3d5832a2cc..515860a66b9d 100644
--- a/lib/galaxy/datatypes/images.py
+++ b/lib/galaxy/datatypes/images.py
@@ -3,6 +3,7 @@
"""
import base64
+import io
import json
import logging
import math
@@ -17,6 +18,7 @@
import mrcfile
import numpy as np
import png
+import pydicom
import tifffile
from typing_extensions import Literal
@@ -225,10 +227,12 @@ def set_meta(
dataset.metadata.num_unique_values = len(unique_values)
+@build_sniff_from_prefix
class Tiff(Image):
edam_format = "format_3591"
file_ext = "tiff"
display_behavior = "download" # TIFF files trigger browser downloads
+
MetadataElement(
name="offsets",
desc="Offsets File",
@@ -239,6 +243,27 @@ class Tiff(Image):
optional=True,
)
+ def sniff_prefix(self, file_prefix: FilePrefix) -> bool:
+ """
+ Determine if the file is in TIFF format by checking the file header.
+
+ For a successful check, the first 4 bytes must be the TIFF magic number. See [1] for a list of magic numbers.
+
+ Manual checking of the file header, as opposed to trying to read the file with tifffile, is required due to an
+ ambiguity with DICOM files. This is because the DICOM standard allows *any content* for the first 128 bytes of
+ the file, followed by the DICOM prefix (see §7.1 in [2] for details).
+
+ [1] https://gist.github.com/leommoore/f9e57ba2aa4bf197ebc5
+ [2] https://dicom.nema.org/medical/dicom/current/output/html/part10.html
+ """
+ return file_prefix.contents_header_bytes[:4] in (
+ b"\x4d\x4d\x00\x2a", # TIFF format (Motorola - big endian)
+ b"\x49\x49\x2a\x00", # TIFF format (Intel - little endian)
+ ) and (
+ len(file_prefix.contents_header_bytes) < 132 # file is too short to be a DICOM
+ or file_prefix.contents_header_bytes[128:132] != b"DICM" # file does not contain the DICOM prefix
+ )
+
def set_meta(
self, dataset: DatasetProtocol, overwrite: bool = True, metadata_tmp_files_dir: Optional[str] = None, **kwd
) -> None:
@@ -385,19 +410,14 @@ def _read_segments(page: Union[tifffile.TiffPage, tifffile.TiffFrame]) -> Iterat
yield segment
- def sniff(self, filename: str) -> bool:
- with tifffile.TiffFile(filename):
- return True
-
class OMETiff(Tiff):
file_ext = "ome.tiff"
- def sniff(self, filename: str) -> bool:
- with tifffile.TiffFile(filename) as tif:
- if tif.is_ome:
- return True
- return False
+ def sniff_prefix(self, file_prefix: FilePrefix) -> bool:
+ buf = io.BytesIO(file_prefix.contents_header_bytes)
+ with tifffile.TiffFile(buf) as tif:
+ return tif.is_ome
class OMEZarr(data.ZarrDirectory):
@@ -519,6 +539,117 @@ def sniff(self, filename: str) -> bool:
return fh.read(4) == b"%PDF"
+@build_sniff_from_prefix
+class Dicom(Image):
+ """
+ DICOM medical imaging format (.dcm)
+
+ >>> from galaxy.datatypes.sniff import get_test_fname
+ >>> fname = get_test_fname('ct_image.dcm')
+ >>> Dicom().sniff(fname)
+ True
+ """
+
+ MetadataElement(
+ name="is_tiled",
+ desc="Is this a WSI DICOM?",
+ readonly=True,
+ visible=True,
+ optional=True,
+ )
+
+ edam_format = "format_3548"
+ file_ext = "dcm"
+
+ def sniff_prefix(self, file_prefix: FilePrefix) -> bool:
+ """
+ Determine if the file is in DICOM format according to §7.1 in [1].
+
+ [1] https://dicom.nema.org/medical/dicom/current/output/html/part10.html
+ """
+ return len(file_prefix.contents_header_bytes) >= 132 and file_prefix.contents_header_bytes[128:132] == b"DICM"
+
+ def get_mime(self) -> str:
+ """
+ Returns the mime type of the datatype.
+ """
+ return "application/dicom"
+
+ def set_meta(
+ self, dataset: DatasetProtocol, overwrite: bool = True, metadata_tmp_files_dir: Optional[str] = None, **kwd
+ ) -> None:
+ """
+ Populate the metadata of the DICOM file using the pydicom library.
+
+ The following metadata fields are populated, if possible:
+ - `width`
+ - `height`
+ - `channels`
+ - `dtype`
+ - `num_unique_values` in some cases
+ - `is_tiled`
+
+ Currently, `frames` and `depth` are not populated. This is because "frames" in DICOM are a generic entity,
+ that can be used for different purposes, including slices in 3-D images, frames in temporal sequences, and
+ tiles of a mosaic or pyramid (WSI DICOM). Distinguishing these cases is not straight-forward (and, as a
+ consequence, neither is determining the `axes` of the image). This can be implemented in the future.
+ """
+ try:
+ dcm = pydicom.dcmread(dataset.get_file_name(), stop_before_pixels=True)
+ except pydicom.errors.InvalidDicomError:
+ return # Ignore errors if metadata cannot be read
+
+ # Determine the number of channels (0 if no channel info is present)
+ dataset.metadata.channels = dcm.get("SamplesPerPixel", 0)
+
+ # Determine if the DICOM file is tiled (likely WSI DICOM)
+ dataset.metadata.is_tiled = hasattr(dcm, "TotalPixelMatrixColumns") and hasattr(dcm, "TotalPixelMatrixRows")
+
+ # Determine the width and height of the dataset. If the DICOM file is not tiled, the width and height
+ # directly. For tiled DICOM, these values correspond to the size of the tiles.
+ if dataset.metadata.is_tiled:
+ dataset.metadata.width = dcm.TotalPixelMatrixColumns
+ dataset.metadata.height = dcm.TotalPixelMatrixRows
+ else:
+ dataset.metadata.width = dcm.get("Columns")
+ dataset.metadata.height = dcm.get("Rows")
+
+ # Try to infer the `dtype` from metadata
+ if dcm.BitsAllocated == 1:
+ dataset.metadata.dtype = "bool" # 1bit
+ else:
+ dtype_lut = [
+ ["uint8", "int8"],
+ ["uint16", "int16"],
+ ["uint32", "int32"],
+ ]
+ dtype_lut_pos = (
+ round(math.log2(dcm.BitsAllocated) - 3), # 8bit -> 0, 16bit -> 1, 32bit -> 2
+ dcm.PixelRepresentation,
+ )
+ if 0 <= dtype_lut_pos[0] < len(dtype_lut):
+ dataset.metadata.dtype = dtype_lut[dtype_lut_pos[0]][dtype_lut_pos[1]]
+ else:
+ dataset.metadata.dtype = None # unknown `dtype`
+
+ # Try to infer `num_unique_values` from metadata
+ try:
+ if dcm.SOPClassUID == "1.2.840.10008.5.1.4.1.1.66.4": # https://www.dicomlibrary.com/dicom/sop
+
+ # The DICOM file contains segmentation, count +1 for the image background
+ dataset.metadata.num_unique_values = 1 + len(dcm.SegmentSequence)
+
+ else:
+
+ # Otherwise, `num_unique_values` is not available from metadata
+ dataset.metadata.num_unique_values = None
+
+ except AttributeError:
+
+ # Ignore errors if metadata cannot be read
+ dataset.metadata.num_unique_values = None
+
+
@build_sniff_from_prefix
class Tck(Binary):
"""
diff --git a/lib/galaxy/datatypes/test/ct_image.dcm b/lib/galaxy/datatypes/test/ct_image.dcm
new file mode 120000
index 000000000000..cfc43f6acb27
--- /dev/null
+++ b/lib/galaxy/datatypes/test/ct_image.dcm
@@ -0,0 +1 @@
+../../../../test-data/highdicom/ct_image.dcm
\ No newline at end of file
diff --git a/lib/galaxy/datatypes/test/seg_image_ct_binary.dcm b/lib/galaxy/datatypes/test/seg_image_ct_binary.dcm
new file mode 120000
index 000000000000..5121b39a3e69
--- /dev/null
+++ b/lib/galaxy/datatypes/test/seg_image_ct_binary.dcm
@@ -0,0 +1 @@
+../../../../test-data/highdicom/seg_image_ct_binary.dcm
\ No newline at end of file
diff --git a/lib/galaxy/datatypes/test/sm_image.dcm b/lib/galaxy/datatypes/test/sm_image.dcm
new file mode 120000
index 000000000000..f5fb19c2f36d
--- /dev/null
+++ b/lib/galaxy/datatypes/test/sm_image.dcm
@@ -0,0 +1 @@
+../../../../test-data/highdicom/sm_image.dcm
\ No newline at end of file
diff --git a/lib/galaxy/dependencies/pinned-requirements.txt b/lib/galaxy/dependencies/pinned-requirements.txt
index d0ce8a15cd03..2d474ab9e063 100644
--- a/lib/galaxy/dependencies/pinned-requirements.txt
+++ b/lib/galaxy/dependencies/pinned-requirements.txt
@@ -163,6 +163,8 @@ pycryptodome==3.23.0
pydantic==2.12.5
pydantic-core==2.41.5
pydantic-tes==0.2.0
+pydicom==2.4.4 ; python_full_version < '3.10'
+pydicom==3.0.1 ; python_full_version >= '3.10'
pydot==4.0.1
pyeventsystem==0.1.0
pyfaidx==0.9.0.3
diff --git a/packages/data/setup.cfg b/packages/data/setup.cfg
index a0d4d27acc66..c78a2f632867 100644
--- a/packages/data/setup.cfg
+++ b/packages/data/setup.cfg
@@ -55,6 +55,7 @@ install_requires =
parsley
pycryptodome
pydantic[email]>=2.7.4
+ pydicom
pylibmagic
pypng
python-magic
diff --git a/pyproject.toml b/pyproject.toml
index b67c4eebcf9d..59679959242f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -75,6 +75,7 @@ dependencies = [
"pulsar-galaxy-lib>=0.15.10",
"pycryptodome",
"pydantic[email]>=2.7.4", # https://github.com/pydantic/pydantic/pull/9639
+ "pydicom",
"PyJWT",
"pykwalify",
"pylibmagic",
diff --git a/test-data/highdicom/LICENSE b/test-data/highdicom/LICENSE
new file mode 100644
index 000000000000..e2420f10fb81
--- /dev/null
+++ b/test-data/highdicom/LICENSE
@@ -0,0 +1,7 @@
+Copyright 2020 MGH Computational Pathology
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/test-data/highdicom/ct_image.dcm b/test-data/highdicom/ct_image.dcm
new file mode 100644
index 000000000000..39516763e1d7
Binary files /dev/null and b/test-data/highdicom/ct_image.dcm differ
diff --git a/test-data/highdicom/seg_image_ct_binary.dcm b/test-data/highdicom/seg_image_ct_binary.dcm
new file mode 100644
index 000000000000..04a19ae00988
Binary files /dev/null and b/test-data/highdicom/seg_image_ct_binary.dcm differ
diff --git a/test-data/highdicom/sm_image.dcm b/test-data/highdicom/sm_image.dcm
new file mode 100644
index 000000000000..a17764bb06d4
Binary files /dev/null and b/test-data/highdicom/sm_image.dcm differ
diff --git a/test/unit/data/datatypes/test_images.py b/test/unit/data/datatypes/test_images.py
index 929c754b363b..3b3e4a1f06a3 100644
--- a/test/unit/data/datatypes/test_images.py
+++ b/test/unit/data/datatypes/test_images.py
@@ -1,13 +1,14 @@
-from typing import (
- Any,
-)
+from typing import Any
from galaxy.datatypes.images import (
+ Dicom,
Image,
+ OMETiff,
Pdf,
Png,
Tiff,
)
+from galaxy.datatypes.sniff import get_test_fname
from .util import (
get_dataset,
MockDatasetDataset,
@@ -35,11 +36,18 @@ def test():
# Define test factory
-def __create_test(image_cls: type[Image], input_filename: str, metadata_key: str, expected_value: Any):
+def __create_test(image_cls: type[Image], input_filename: str, **expected_metadata: Any):
@__test(image_cls, input_filename)
def test(metadata):
- assert getattr(metadata, metadata_key) == expected_value
+ for metadata_key, expected_value in expected_metadata.items():
+ metadata_value = getattr(metadata, metadata_key)
+ cond = (
+ (metadata_value is expected_value)
+ if expected_value is None or type(expected_value) is bool
+ else (metadata_value == expected_value)
+ )
+ assert cond, f"expected: {repr(expected_value)}, actual: {repr(metadata_value)}"
return test
@@ -63,24 +71,24 @@ def __assert_empty_metadata(metadata):
# Tests for `Tiff` class
-test_tiff_axes_yx = __create_test(Tiff, "im1_uint8.tif", "axes", "YX")
-test_tiff_axes_zcyx = __create_test(Tiff, "im6_uint8.tif", "axes", "ZCYX")
-test_tiff_dtype_uint8 = __create_test(Tiff, "im6_uint8.tif", "dtype", "uint8")
-test_tiff_dtype_uint16 = __create_test(Tiff, "im8_uint16.tif", "dtype", "uint16")
-test_tiff_dtype_float64 = __create_test(Tiff, "im4_float.tif", "dtype", "float64")
-test_tiff_num_unique_values_2 = __create_test(Tiff, "im3_b.tif", "num_unique_values", 2)
-test_tiff_num_unique_values_618 = __create_test(Tiff, "im4_float.tif", "num_unique_values", 618)
-test_tiff_width_16 = __create_test(Tiff, "im7_uint8.tif", "width", 16) # axes: ZYX
-test_tiff_width_32 = __create_test(Tiff, "im3_b.tif", "width", 32) # axes: YXS
-test_tiff_height_8 = __create_test(Tiff, "im7_uint8.tif", "height", 8) # axes: ZYX
-test_tiff_height_32 = __create_test(Tiff, "im3_b.tif", "height", 32) # axes: YXS
-test_tiff_channels_0 = __create_test(Tiff, "im1_uint8.tif", "channels", 0)
-test_tiff_channels_2 = __create_test(Tiff, "im5_uint8.tif", "channels", 2) # axes: CYX
-test_tiff_channels_3 = __create_test(Tiff, "im3_b.tif", "channels", 3) # axes: YXS
-test_tiff_depth_0 = __create_test(Tiff, "im1_uint8.tif", "depth", 0) # axes: YXS
-test_tiff_depth_25 = __create_test(Tiff, "im7_uint8.tif", "depth", 25) # axes: ZYX
-test_tiff_frames_0 = __create_test(Tiff, "im1_uint8.tif", "frames", 0) # axes: YXS
-test_tiff_frames_5 = __create_test(Tiff, "im8_uint16.tif", "frames", 5) # axes: TYX
+test_tiff_axes_yx = __create_test(Tiff, "im1_uint8.tif", axes="YX")
+test_tiff_axes_zcyx = __create_test(Tiff, "im6_uint8.tif", axes="ZCYX")
+test_tiff_dtype_uint8 = __create_test(Tiff, "im6_uint8.tif", dtype="uint8")
+test_tiff_dtype_uint16 = __create_test(Tiff, "im8_uint16.tif", dtype="uint16")
+test_tiff_dtype_float64 = __create_test(Tiff, "im4_float.tif", dtype="float64")
+test_tiff_num_unique_values_2 = __create_test(Tiff, "im3_b.tif", num_unique_values=2)
+test_tiff_num_unique_values_618 = __create_test(Tiff, "im4_float.tif", num_unique_values=618)
+test_tiff_width_16 = __create_test(Tiff, "im7_uint8.tif", width=16) # axes: ZYX
+test_tiff_width_32 = __create_test(Tiff, "im3_b.tif", width=32) # axes: YXS
+test_tiff_height_8 = __create_test(Tiff, "im7_uint8.tif", height=8) # axes: ZYX
+test_tiff_height_32 = __create_test(Tiff, "im3_b.tif", height=32) # axes: YXS
+test_tiff_channels_0 = __create_test(Tiff, "im1_uint8.tif", channels=0)
+test_tiff_channels_2 = __create_test(Tiff, "im5_uint8.tif", channels=2) # axes: CYX
+test_tiff_channels_3 = __create_test(Tiff, "im3_b.tif", channels=3) # axes: YXS
+test_tiff_depth_0 = __create_test(Tiff, "im1_uint8.tif", depth=0) # axes: YXS
+test_tiff_depth_25 = __create_test(Tiff, "im7_uint8.tif", depth=25) # axes: ZYX
+test_tiff_frames_0 = __create_test(Tiff, "im1_uint8.tif", frames=0) # axes: YXS
+test_tiff_frames_5 = __create_test(Tiff, "im8_uint16.tif", frames=5) # axes: TYX
@__test(Tiff, "im_empty.tif")
@@ -88,64 +96,144 @@ def test_tiff_empty(metadata):
__assert_empty_metadata(metadata)
-@__test(Tiff, "1.tiff")
-def test_tiff_unsupported_compression(metadata):
+test_tiff_unsupported_compression = __create_test(
+ Tiff,
+ "1.tiff",
# If the compression of a TIFF is unsupported, some fields should still be there
- assert metadata.axes == "YX"
- assert metadata.dtype == "bool"
- assert metadata.width == 1728
- assert metadata.height == 2376
- assert metadata.channels == 0
- assert metadata.depth == 0
- assert metadata.frames == 0
-
+ axes="YX",
+ dtype="bool",
+ width=1728,
+ height=2376,
+ channels=0,
+ depth=0,
+ frames=0,
# The other fields should be missing
- assert getattr(metadata, "num_unique_values", None) is None
+ num_unique_values=None,
+)
+
+
+test_tiff_unsupported_multiseries = __create_test(
+ Tiff,
+ "im9_multiseries.tif", # TODO: rename to .tiff
+ axes=["YXS", "YX"],
+ dtype=["uint8", "uint16"],
+ num_unique_values=[2, 255],
+ width=[32, 256],
+ height=[32, 256],
+ channels=[3, 0],
+ depth=[0, 0],
+ frames=[0, 0],
+)
+
+
+def test_tiff_sniff():
+ for filename in (
+ "im4_float.tif",
+ "im1_uint8.tif",
+ "im_empty.tif",
+ "im7_uint8.tif",
+ "im5_uint8.tif",
+ "1.tiff",
+ "im9_multiseries.tif",
+ "im8_uint16.tif",
+ "im6_uint8.tif",
+ "im3_b.tif",
+ ):
+ fname = get_test_fname(filename)
+ assert not Dicom().sniff(fname), f"filename: {filename}"
+ assert not Png().sniff(fname), f"filename: {filename}"
+ assert not OMETiff().sniff(fname), f"filename: {filename}"
+ assert Tiff().sniff(fname), f"filename: {filename}"
-@__test(Tiff, "im9_multiseries.tif")
-def test_tiff_multiseries(metadata):
- assert metadata.axes == ["YXS", "YX"]
- assert metadata.dtype == ["uint8", "uint16"]
- assert metadata.num_unique_values == [2, 255]
- assert metadata.width == [32, 256]
- assert metadata.height == [32, 256]
- assert metadata.channels == [3, 0]
- assert metadata.depth == [0, 0]
- assert metadata.frames == [0, 0]
+# Tests for `OMETiff` class
+
+
+def test_ome_tiff_sniff():
+ fname = get_test_fname("1.ome.tiff")
+ assert not Dicom().sniff(fname)
+ assert not Png().sniff(fname)
+ assert Tiff().sniff(fname)
+ assert OMETiff().sniff(fname)
# Tests for `Image` class
-test_png_axes_yx = __create_test(Image, "im1_uint8.png", "axes", "YX")
-test_png_axes_yxc = __create_test(Image, "im3_a.png", "axes", "YXC")
-test_png_dtype_uint8 = __create_test(Image, "im1_uint8.png", "dtype", "uint8")
-test_png_num_unique_values_1 = __create_test(Image, "im2_a.png", "num_unique_values", None)
-test_png_num_unique_values_2 = __create_test(Image, "im2_b.png", "num_unique_values", None)
-test_png_width_32 = __create_test(Image, "im2_b.png", "width", 32)
-test_png_height_32 = __create_test(Image, "im2_b.png", "height", 32)
-test_png_channels_0 = __create_test(Image, "im1_uint8.png", "channels", 0)
-test_png_channels_3 = __create_test(Image, "im3_a.png", "channels", 3)
-test_png_depth_0 = __create_test(Image, "im1_uint8.png", "depth", 0)
-test_png_frames_1 = __create_test(Image, "im1_uint8.png", "frames", 1)
+test_png_axes_yx = __create_test(Image, "im1_uint8.png", axes="YX")
+test_png_axes_yxc = __create_test(Image, "im3_a.png", axes="YXC")
+test_png_dtype_uint8 = __create_test(Image, "im1_uint8.png", dtype="uint8")
+test_png_num_unique_values_1 = __create_test(Image, "im2_a.png", num_unique_values=None)
+test_png_num_unique_values_2 = __create_test(Image, "im2_b.png", num_unique_values=None)
+test_png_width_32 = __create_test(Image, "im2_b.png", width=32)
+test_png_height_32 = __create_test(Image, "im2_b.png", height=32)
+test_png_channels_0 = __create_test(Image, "im1_uint8.png", channels=0)
+test_png_channels_3 = __create_test(Image, "im3_a.png", channels=3)
+test_png_depth_0 = __create_test(Image, "im1_uint8.png", depth=0)
+test_png_frames_1 = __create_test(Image, "im1_uint8.png", frames=1)
# Tests for `Png` class
-test_png_axes_yx = __create_test(Png, "im1_uint8.png", "axes", "YX")
-test_png_axes_yxc = __create_test(Png, "im3_a.png", "axes", "YXC")
-test_png_dtype_uint8 = __create_test(Png, "im1_uint8.png", "dtype", "uint8")
-test_png_num_unique_values_1 = __create_test(Png, "im2_a.png", "num_unique_values", 1)
-test_png_num_unique_values_2 = __create_test(Png, "im2_b.png", "num_unique_values", 2)
-test_png_width_32 = __create_test(Png, "im2_b.png", "width", 32)
-test_png_height_32 = __create_test(Png, "im2_b.png", "height", 32)
-test_png_channels_0 = __create_test(Png, "im1_uint8.png", "channels", 0)
-test_png_channels_3 = __create_test(Png, "im3_a.png", "channels", 3)
-test_png_depth_0 = __create_test(Png, "im1_uint8.png", "depth", 0)
-test_png_frames_1 = __create_test(Png, "im1_uint8.png", "frames", 1)
+test_png_axes_yx = __create_test(Png, "im1_uint8.png", axes="YX")
+test_png_axes_yxc = __create_test(Png, "im3_a.png", axes="YXC")
+test_png_dtype_uint8 = __create_test(Png, "im1_uint8.png", dtype="uint8")
+test_png_num_unique_values_1 = __create_test(Png, "im2_a.png", num_unique_values=1)
+test_png_num_unique_values_2 = __create_test(Png, "im2_b.png", num_unique_values=2)
+test_png_width_32 = __create_test(Png, "im2_b.png", width=32)
+test_png_height_32 = __create_test(Png, "im2_b.png", height=32)
+test_png_channels_0 = __create_test(Png, "im1_uint8.png", channels=0)
+test_png_channels_3 = __create_test(Png, "im3_a.png", channels=3)
+test_png_depth_0 = __create_test(Png, "im1_uint8.png", depth=0)
+test_png_frames_1 = __create_test(Png, "im1_uint8.png", frames=1)
+
+
+# Tests for `Dicom` class
+
+test_2d_singlechannel = __create_test(
+ Dicom,
+ "ct_image.dcm",
+ width=128,
+ height=128,
+ channels=1,
+ dtype="int16",
+ num_unique_values=None,
+ is_tiled=False,
+)
+
+
+test_tiled_multichannel = __create_test(
+ Dicom,
+ "sm_image.dcm",
+ width=50,
+ height=50,
+ channels=3,
+ dtype="uint8",
+ num_unique_values=None,
+ is_tiled=True,
+)
+
+
+test_3d_binary = __create_test(
+ Dicom,
+ "seg_image_ct_binary.dcm",
+ width=16,
+ height=16,
+ channels=1,
+ dtype="bool",
+ num_unique_values=2,
+ is_tiled=False,
+)
+
+
+def test_dicom_sniff():
+ fname = get_test_fname("ct_image.dcm")
+ assert Dicom().sniff(fname)
+ assert not OMETiff().sniff(fname)
+ assert not Tiff().sniff(fname)
+ assert not Png().sniff(fname)
-# Test with files that neither Pillow nor tifffile can open
+# Test with files that neither Pillow, tifffile, nor pydicom can open
@__test(Pdf, "454Score.pdf")