Skip to content

Commit

Permalink
Don't read multiplane ndpi files with openslide
Browse files Browse the repository at this point in the history
It explicitly only reads the focal plane at 0 and skips the others
silently.

Improve the tifffile source reporting of ndpi internal metadata.
  • Loading branch information
manthey committed Jan 10, 2025
1 parent 840de66 commit c3ec197
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Better report if rasterized vector files are geospatial ([#1769](../../pull/1769))
- Provide some latitude in vips multiframe detection ([#1770](../../pull/1770))
- Don't read multiplane ndpi files with openslide ([#1772](../../pull/1772))

## 1.30.6

Expand Down
7 changes: 7 additions & 0 deletions sources/openslide/large_image_source_openslide/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ def __init__(self, path, **kwargs): # noqa
tifftools.Tag.ICCProfile.value]['data']]
except Exception:
pass
if hasattr(self, '_tiffinfo'):
for ifd in self._tiffinfo['ifds']:
if (tifftools.Tag.NDPI_FOCAL_PLANE.value in ifd['tags'] and
ifd['tags'][tifftools.Tag.NDPI_FOCAL_PLANE.value]['data'][0] != 0):
msg = ('File will not be opened via OpenSlide; '
'non-zero focal planes would be missed.')
raise TileSourceError(msg)

svsAvailableLevels = self._getAvailableLevels(self._largeImagePath)
if not len(svsAvailableLevels):
Expand Down
18 changes: 18 additions & 0 deletions sources/tifffile/large_image_source_tifffile/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,20 @@ def _handle_svs(self):
except Exception:
pass

def _handle_internal_ndpi(self, intmeta):
try:
ndpi = intmeta.pop('65449')
intmeta['ndpi'] = {}
for line in ndpi.replace('\r', '\n').split('\n'):
if '=' in line:
key, value = line.split('=', 1)
key = key.strip()
value = value.strip()
if key and key not in intmeta['ndpi'] and value:
intmeta['ndpi'][key] = value
except Exception:
pass

def getNativeMagnification(self):
"""
Get the magnification at a particular level.
Expand Down Expand Up @@ -499,6 +513,10 @@ def getInternalMetadata(self, **kwargs):
json.dumps(result[key][subkey])
except Exception:
del result[key][subkey]
for key in dir(self._tf):
if (key.startswith('is_') and hasattr(self, '_handle_internal_' + key[3:]) and
getattr(self._tf, key)):
getattr(self, '_handle_internal_' + key[3:])(result)
if hasattr(self, '_xml') and 'xml' not in result:
result.pop('ImageDescription', None)
result['xml'] = self._xml
Expand Down
6 changes: 4 additions & 2 deletions test/datastore.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,11 @@
'18f6378f-433c-42bf-9373-1ff9c808c118.dcm': 'sha512:36432183380eb7d44417a2210a19d550527abd1181255e19ed5c1d17695d8bb8ca42f5b426a63fa73b84e0e17b770401a377ae0c705d0ed7fdf30d571ef60e2d', # noqa
'a131592c-a069-4aa7-8031-398654aa8a3d.dcm': 'sha512:99bd3da4b8e11ce7b4f7ed8a294ed0c37437320667a06c40c383f4b29be85fe8e6094043e0600bee0ba879f2401de4c57285800a4a23da2caf2eb94e5b847ee0', # noqa
# Synthetic newer ndpi with binary data and nonblank image labelled as RGB
'synthetic_ndpi_2025.ndpi': 'sha512:28752f35790b62fb712eb93f370be4e2f44799d44d6bb1f34fea9d3a6f01c7b49b2c510fc34ebaa829cb1beef0bc5b12d26a4cb3060835ff49aada7d99f61e44', # noqa
'synthetic_ndpi_2025.ndpi': 'sha512:b9b2c420cde9fd988786afc02efb761a7d425ce542cc68f10f5878bdc7177d61952e0d52508501c82d5664a07a87e7486ec4bb0b6634c556400cfc91fc3f52ec', # noqa
# Synthetic ndpi with multiple focal planes
'synthetic_ndpi_multiplane_2025.ndpi': 'sha512:1025d6ddd74070d0bb2c3ab398bc5e6ae05390651f84df22c37e3dbe547bb16b4eba0a0a3cefc7ac824ff2e87320782b676b2049a602bc0d5dbb105bbc94e888', # noqa
# Synthetic uint16 untiled tiff that can be read with the tiff source
'synthetic_untiled_16.tiff': 'sha512:f4773fcfa749ba9c2db25319c9e8ad8586dd148de4366dae0393a3703906dace9f11233eafdb24418b598170d6372ef1ca861bf8d7a8212cac21a0eb8636ee77', # noqa
'synthetic_untiled_16.tiff': 'sha512:1025d6ddd74070d0bb2c3ab398bc5e6ae05390651f84df22c37e3dbe547bb16b4eba0a0a3cefc7ac824ff2e87320782b676b2049a602bc0d5dbb105bbc94e888', # noqa
# DICOM with int16 data
# Source: TCIA/CMB-LCA_v07_20240828/CMB-LCA/MSB-01459/
# 12-22-1959-XR Chest-59125/1002.000000-43033/1-1.dcm
Expand Down
2 changes: 1 addition & 1 deletion test/test_source_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
'openjpeg': {'read': r'\.(jp2)$'},
'openslide': {
'read': r'\.(ptif|svs|ndpi|tif.*|qptiff|dcm)$',
'noread': r'(oahu|DDX58_AXL|huron\.image2_jpeg2k|landcover_sample|d042-353\.crop|US_Geo\.|extraoverview|imagej|bad_axes|synthetic_untiled|indica|tcia.*dcm)', # noqa
'noread': r'(oahu|DDX58_AXL|huron\.image2_jpeg2k|landcover_sample|d042-353\.crop|US_Geo\.|extraoverview|imagej|bad_axes|synthetic_untiled|indica|tcia.*dcm|multiplane.*ndpi)', # noqa
'skip': r'nokeyframe\.ome\.tiff|TCGA-55.*\.ome\.tiff$',
'skipTiles': r'one_layer_missing',
},
Expand Down

0 comments on commit c3ec197

Please sign in to comment.