Skip to content

Commit

Permalink
Merge pull request #486 from girder/openslide-associated-images
Browse files Browse the repository at this point in the history
Show additional associated image for some openslides.
  • Loading branch information
manthey authored Oct 1, 2020
2 parents 2bba875 + 6b7929a commit 2849d0d
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 23 deletions.
16 changes: 2 additions & 14 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ ENV DEBIAN_FRONTEND=noninteractive \
LANG=en_US.UTF-8 \
PYENV_ROOT="/.pyenv" \
PATH="/.pyenv/bin:/.pyenv/shims:$PATH" \
GOSU_VERSION=1.10 \
PYTHON_VERSIONS="3.7.9 2.7.18 3.5.9 3.6.12 3.8.6" \
LOCAL_PYTHON_VERSION="3.7.9"
# PYTHON_VERSIONS="2.7.18 3.5.9 3.6.12 3.7.9 3.8.6 pypy2.7-7.3.1 pypy3.5-7.0.0 pypy3.6-7.3.1"
Expand All @@ -26,6 +25,7 @@ RUN apt-get update && \
fonts-dejavu \
fuse \
git \
gosu \
gpg-agent \
libbz2-dev \
libffi-dev \
Expand Down Expand Up @@ -61,22 +61,10 @@ RUN pyenv update && \
find $PYENV_ROOT/versions -type f '(' -name '*.py[co]' -o -name '*.exe' ')' -exec rm -fv '{}' + && \
echo $PYTHON_VERSIONS | tr " " "\n" > $PYENV_ROOT/version

# Create a user that can be used with gosu or chroot when running tox
RUN groupadd -r tox --gid=999 && \
useradd -m -r -g tox --uid=999 tox

# Install gosu to run tox as the "tox" user instead of as root.
# https://github.com/tianon/gosu#from-debian
RUN set -x && \
dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')" && \
wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch" && \
wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc" && \
export GNUPGHOME="$(mktemp -d)" && \
gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 && \
gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu && \
rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc && \
chmod +x /usr/local/bin/gosu && \
gosu nobody true

RUN pyenv local ${PYTHON_VERSIONS%% *} && \
python -m pip install -U pip && \
python -m pip install tox && \
Expand Down
64 changes: 55 additions & 9 deletions sources/openslide/large_image_source_openslide/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,22 @@
import PIL
from pkg_resources import DistributionNotFound, get_distribution

try:
import tifftools
except ImportError:
tifftools = None

from large_image import config
from large_image.cache_util import LruCacheMetaclass, methodcache
from large_image.constants import SourcePriority, TILE_FORMAT_PIL
from large_image.exceptions import TileSourceException
from large_image.tilesource import FileTileSource, nearPowerOfTwo

try:
from libtiff import libtiff_ctypes
except ImportError:
libtiff_ctypes = False


try:
__version__ = get_distribution(__name__).version
Expand Down Expand Up @@ -85,6 +95,11 @@ def __init__(self, path, **kwargs):
raise TileSourceException('File cannot be opened via OpenSlide.')
except openslide.lowlevel.OpenSlideError:
raise TileSourceException('File will not be opened via OpenSlide.')
if tifftools and libtiff_ctypes:
try:
self._tiffinfo = tifftools.read_tiff(largeImagePath)
except Exception:
pass

svsAvailableLevels = self._getAvailableLevels(largeImagePath)
if not len(svsAvailableLevels):
Expand Down Expand Up @@ -309,16 +324,38 @@ def getPreferredLevel(self, level):
scale /= 2
return level

def _getAssociatedImagesDict(self):
images = {}
try:
for key in self._openslide.associated_images:
images[key] = 'openslide'
except openslide.lowlevel.OpenSlideError:
pass
if hasattr(self, '_tiffinfo'):
vendor = self._openslide.properties['openslide.vendor']
for ifdidx, ifd in enumerate(self._tiffinfo['ifds']):
key = None
if vendor == 'hamamatsu':
if tifftools.Tag.NDPI_SOURCELENS.value in ifd['tags']:
lens = ifd['tags'][tifftools.Tag.NDPI_SOURCELENS.value]['data'][0]
key = {-1: 'macro', -2: 'nonempty'}.get(lens)
elif vendor == 'aperio':
if (ifd['tags'].get(tifftools.Tag.NewSubfileType.value) and
ifd['tags'][tifftools.Tag.NewSubfileType.value]['data'][0] &
tifftools.Tag.NewSubfileType.bitfield.Page.value):
key = 'label' if ifd['tags'][
tifftools.Tag.NewSubfileType.value]['data'][0] == 1 else 'macro'
if key and key not in images:
images[key] = ifdidx
return images

def getAssociatedImagesList(self):
"""
Get a list of all associated images.
:return: the list of image keys.
"""
try:
return sorted(self._openslide.associated_images)
except openslide.lowlevel.OpenSlideError:
return []
return sorted(self._getAssociatedImagesDict().keys())

def _getAssociatedImage(self, imageKey):
"""
Expand All @@ -327,9 +364,18 @@ def _getAssociatedImage(self, imageKey):
:param imageKey: the key of the associated image.
:return: the image in PIL format or None.
"""
try:
if imageKey in self._openslide.associated_images:
images = self._getAssociatedImagesDict()
if imageKey not in images:
return None
if images[imageKey] == 'openslide':
try:
return self._openslide.associated_images[imageKey]
except openslide.lowlevel.OpenSlideError:
pass
return None
except openslide.lowlevel.OpenSlideError:
return None
bytePath = self._getLargeImagePath()
if not isinstance(bytePath, six.binary_type):
bytePath = bytePath.encode('utf8')
_tiffFile = libtiff_ctypes.TIFF.open(bytePath)
_tiffFile.SetDirectory(images[imageKey])
img = _tiffFile.read_image()
return PIL.Image.fromarray(img)
1 change: 1 addition & 0 deletions sources/openslide/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def prerelease_local_scheme(version):
install_requires=[
'large-image>=1.0.0',
'openslide-python>=1.1.0',
'tifftools;python_version>="3.6"',
],
extras_require={
'girder': 'girder-large-image>=1.0.0',
Expand Down

0 comments on commit 2849d0d

Please sign in to comment.