|
15 | 15 | import unittest |
16 | 16 | from unittest.mock import patch |
17 | 17 |
|
| 18 | +import numpy as np |
| 19 | + |
18 | 20 | from monai.data.image_reader import DICOM_READER_ENV_MAP, get_preferred_dicom_reader_key, is_dicom_path |
19 | 21 | from monai.transforms import LoadImage |
20 | 22 | from monai.transforms.io.array import SUPPORTED_READERS, get_default_reader_registration_order |
@@ -216,6 +218,87 @@ def test_is_nvimgcodec_available_with_cuda(self): |
216 | 218 |
|
217 | 219 |
|
218 | 220 | class TestNvImgCodecPydicomReaderIntegration(unittest.TestCase): |
| 221 | + @SkipIfNoModule("pydicom") |
| 222 | + @SkipIfNoModule("pylibjpeg") |
| 223 | + @SkipIfNoModule("openjpeg") # pylibjpeg-openjpeg |
| 224 | + @SkipIfNoModule("jpeg_ls") # pyjpegls |
| 225 | + @SkipIfNoModule("gdcm") # python-gdcm |
| 226 | + @SkipIfNoModule("nvidia.nvimgcodec.tools.dicom.pydicom_plugin") |
| 227 | + def test_pixels_match_pydicom_reader(self): |
| 228 | + from pydicom import dcmread |
| 229 | + from pydicom.data import get_testdata_file |
| 230 | + |
| 231 | + from monai.data import NvImgCodecPydicomReader, PydicomReader |
| 232 | + from monai.data.nvimgcodec_pydicom_plugin import ( |
| 233 | + NVIMGCODEC_PLUGIN_LABEL, |
| 234 | + SUPPORTED_DECODER_CLASSES, |
| 235 | + SUPPORTED_TRANSFER_SYNTAXES, |
| 236 | + is_nvimgcodec_available, |
| 237 | + unregister_as_decoder_plugin, |
| 238 | + ) |
| 239 | + |
| 240 | + if not is_nvimgcodec_available(): |
| 241 | + self.skipTest("nvImageCodec with CUDA support is not available.") |
| 242 | + |
| 243 | + # One bundled pydicom decoder test file for each nvImageCodec-supported |
| 244 | + # transfer syntax for which pydicom ships a local sample. |
| 245 | + test_files = ( |
| 246 | + "SC_rgb_jpeg_lossy_gdcm.dcm", # JPEG Baseline |
| 247 | + "SC_rgb_jpeg_gdcm.dcm", # JPEG Lossless SV1 |
| 248 | + "MR_small_jp2klossless.dcm", # JPEG 2000 Lossless |
| 249 | + "JPEG2000.dcm", # JPEG 2000 Lossy |
| 250 | + ) |
| 251 | + supported_uids = {str(uid) for uid in SUPPORTED_TRANSFER_SYNTAXES} |
| 252 | + tested = 0 |
| 253 | + pydicom_failures = [] |
| 254 | + |
| 255 | + for filename in test_files: |
| 256 | + path = get_testdata_file(filename, download=False) |
| 257 | + self.assertIsNotNone(path, f"{filename} is not bundled with pydicom") |
| 258 | + transfer_syntax = str(dcmread(path, stop_before_pixels=True).file_meta.TransferSyntaxUID) |
| 259 | + if transfer_syntax not in supported_uids: |
| 260 | + continue |
| 261 | + |
| 262 | + with self.subTest(filename=filename, transfer_syntax=transfer_syntax): |
| 263 | + # Decode the reference before registering nvImageCodec, ensuring |
| 264 | + # pydicom uses one of its standard compressed-pixel decoders. |
| 265 | + unregister_as_decoder_plugin() |
| 266 | + pydicom_reader = PydicomReader() |
| 267 | + try: |
| 268 | + expected, _ = pydicom_reader.get_data(pydicom_reader.read(path)) |
| 269 | + except Exception as error: |
| 270 | + pydicom_failures.append(f"{filename}: {error}") |
| 271 | + continue |
| 272 | + |
| 273 | + # Force nvImageCodec to be the only available plugin for its |
| 274 | + # supported decoder classes, then restore the registry afterward. |
| 275 | + old_available = {decoder.UID: decoder._available for decoder in SUPPORTED_DECODER_CLASSES} |
| 276 | + try: |
| 277 | + for decoder in SUPPORTED_DECODER_CLASSES: |
| 278 | + decoder._available = {} |
| 279 | + |
| 280 | + nvimgcodec_reader = NvImgCodecPydicomReader() |
| 281 | + decoder = next( |
| 282 | + decoder for decoder in SUPPORTED_DECODER_CLASSES if str(decoder.UID) == transfer_syntax |
| 283 | + ) |
| 284 | + self.assertIn(NVIMGCODEC_PLUGIN_LABEL, decoder._available) |
| 285 | + actual, _ = nvimgcodec_reader.get_data(nvimgcodec_reader.read(path)) |
| 286 | + |
| 287 | + self.assertEqual(actual.shape, expected.shape) |
| 288 | + np.testing.assert_allclose(actual, expected, rtol=0.0, atol=2) |
| 289 | + finally: |
| 290 | + unregister_as_decoder_plugin() |
| 291 | + for decoder in SUPPORTED_DECODER_CLASSES: |
| 292 | + decoder._available = old_available[decoder.UID] |
| 293 | + tested += 1 |
| 294 | + |
| 295 | + if not tested: |
| 296 | + failure_details = "; ".join(pydicom_failures) |
| 297 | + self.skipTest( |
| 298 | + "pydicom could not decode any bundled samples for nvImageCodec-supported transfer syntaxes." |
| 299 | + f" Failures: {failure_details}" |
| 300 | + ) |
| 301 | + |
219 | 302 | @SkipIfNoModule("pydicom") |
220 | 303 | @SkipIfNoModule("nvidia.nvimgcodec.tools.dicom.pydicom_plugin") |
221 | 304 | def test_load_dicom_with_pydicom_env(self): |
|
0 commit comments