Skip to content

Commit

Permalink
Add an open method to each source module.
Browse files Browse the repository at this point in the history
This also adds a canRead method to each source module and an open method
to the primary module.

Before, to open a file with a specific file source, you needed to do
something like ``large_image_source_tiff.TiffFileTileSource(path)``,
where the name of the tile source's class had to be used.  Now, you can
do ``large_image_source_tiff.open(path)``, which maps to the same thing.
The old way still works.  Similarly, ``canRead`` required something like
``large_image_source_tiff.TiffFileTileSource.canRead(path)`` and now
``large_image_source_tiff.canRead(path)`` works as well.

``open`` is now also a synonym for ``getTileSource``, so generically,
``large_image.getTileSource(path)`` can be written as
``large_image.open(path)``.

There is no requirement that tile source modules expose the ``open`` and
``canRead`` methods directly; it is more a convenience for developers.
  • Loading branch information
manthey committed Feb 19, 2021
1 parent abb8bc5 commit f4451c5
Show file tree
Hide file tree
Showing 26 changed files with 301 additions and 129 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,23 @@
- Added a `canRead` method to the core module (#512)
- Image conversion supports JPEG 2000 (jp2k) compression (#522)
- Image conversion can now convert images readable by large_image sources but not by vips (#529)
- Added an `open` method to the core module as an alias to `getTileSource` (#550)
- Added an `open` method to each file source module (#550)
- Numerous improvement to image converversion (#533, #535, #537, #541, #544, #545, #546, #549)

### Improvements
- Better release bioformats resources (#502)
- Better handling of tiff files with JPEG compression and RGB colorspace (#503)
- The openjpeg tile source can decode with parallelism (#511)
- Geospatial tile sources are preferred for geospatial files (#512)
- Support decoding JP2k compressed tiles in the tiff tile source (#514)
- Hardened tests against transient timing issues (#532, #536)

### Bug Fixes
- Harden updates of the item view after making a large image (#508, #515)
- Tiles in an unexpected color mode weren't consistently adjusted (#510)
- Harden trying to add an annotation before the viewer is ready (#547)
- Correctly report the tile size after resampling in the tileIterator (#538)

## Version 1.3.2

Expand Down
2 changes: 1 addition & 1 deletion large_image/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from pkg_resources import DistributionNotFound, get_distribution

from . import tilesource # noqa
from .tilesource import canRead, getTileSource # noqa
from .tilesource import canRead, getTileSource, open # noqa


try:
Expand Down
12 changes: 12 additions & 0 deletions large_image/tilesource/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,18 @@ def getTileSource(*args, **kwargs):
return getTileSourceFromDict(AvailableTileSources, *args, **kwargs)


def open(*args, **kwargs):
"""
Alternate name of getTileSource.
Get a tilesource using the known sources. If tile sources have not yet
been loaded, load them.
:returns: A tilesource for the passed arguments.
"""
return getTileSource(*args, **kwargs)


def canRead(*args, **kwargs):
"""
Check if large_image can read a path or uri.
Expand Down
14 changes: 14 additions & 0 deletions sources/bioformats/large_image_source_bioformats/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -538,3 +538,17 @@ def _getAssociatedImage(self, imageKey):
if javabridge.get_env():
javabridge.detach()
return large_image.tilesource.base._imageToPIL(image)


def open(*args, **kwargs):
"""
Create an instance of the module class.
"""
return BioformatsFileTileSource(*args, **kwargs)


def canRead(*args, **kwargs):
"""
Check if an input can be read by the module class.
"""
return BioformatsFileTileSource.canRead(*args, **kwargs)
14 changes: 14 additions & 0 deletions sources/dummy/large_image_source_dummy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,17 @@ def canRead(cls, *args, **kwargs):

def getTile(self, x, y, z, **kwargs):
return b''


def open(*args, **kwargs):
"""
Create an instance of the module class.
"""
return DummyTileSource(*args, **kwargs)


def canRead(*args, **kwargs):
"""
Check if an input can be read by the module class.
"""
return DummyTileSource.canRead(*args, **kwargs)
14 changes: 14 additions & 0 deletions sources/gdal/large_image_source_gdal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -940,3 +940,17 @@ def getPixel(self, **kwargs):
except RuntimeError:
pass
return pixel


def open(*args, **kwargs):
"""
Create an instance of the module class.
"""
return GDALFileTileSource(*args, **kwargs)


def canRead(*args, **kwargs):
"""
Check if an input can be read by the module class.
"""
return GDALFileTileSource.canRead(*args, **kwargs)
14 changes: 14 additions & 0 deletions sources/mapnik/large_image_source_mapnik/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,3 +414,17 @@ def getTile(self, x, y, z, **kwargs):
if overscan:
pilimg = pilimg.crop((1, 1, pilimg.width - overscan, pilimg.height - overscan))
return self._outputTile(pilimg, TILE_FORMAT_PIL, x, y, z, applyStyle=False, **kwargs)


def open(*args, **kwargs):
"""
Create an instance of the module class.
"""
return MapnikFileTileSource(*args, **kwargs)


def canRead(*args, **kwargs):
"""
Check if an input can be read by the module class.
"""
return MapnikFileTileSource.canRead(*args, **kwargs)
14 changes: 14 additions & 0 deletions sources/nd2/large_image_source_nd2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,17 @@ def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False, **kwargs):
tile = tileframe[y0:y1:step, x0:x1:step].copy()
return self._outputTile(tile, TILE_FORMAT_NUMPY, x, y, z,
pilImageAllowed, numpyAllowed, **kwargs)


def open(*args, **kwargs):
"""
Create an instance of the module class.
"""
return ND2FileTileSource(*args, **kwargs)


def canRead(*args, **kwargs):
"""
Check if an input can be read by the module class.
"""
return ND2FileTileSource.canRead(*args, **kwargs)
14 changes: 14 additions & 0 deletions sources/ometiff/large_image_source_ometiff/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,17 @@ def getPreferredLevel(self, level):
while level - baselevel > self._maxSkippedLevels:
level -= self._maxSkippedLevels
return level


def open(*args, **kwargs):
"""
Create an instance of the module class.
"""
return OMETiffFileTileSource(*args, **kwargs)


def canRead(*args, **kwargs):
"""
Check if an input can be read by the module class.
"""
return OMETiffFileTileSource.canRead(*args, **kwargs)
14 changes: 14 additions & 0 deletions sources/openjpeg/large_image_source_openjpeg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,17 @@ def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False, **kwargs):
tile = tile[::scale, ::scale]
return self._outputTile(tile, TILE_FORMAT_NUMPY, x, y, z,
pilImageAllowed, numpyAllowed, **kwargs)


def open(*args, **kwargs):
"""
Create an instance of the module class.
"""
return OpenjpegFileTileSource(*args, **kwargs)


def canRead(*args, **kwargs):
"""
Check if an input can be read by the module class.
"""
return OpenjpegFileTileSource.canRead(*args, **kwargs)
14 changes: 14 additions & 0 deletions sources/openslide/large_image_source_openslide/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,3 +370,17 @@ def _getAssociatedImage(self, imageKey):
_tiffFile.SetDirectory(images[imageKey])
img = _tiffFile.read_image()
return PIL.Image.fromarray(img)


def open(*args, **kwargs):
"""
Create an instance of the module class.
"""
return OpenslideFileTileSource(*args, **kwargs)


def canRead(*args, **kwargs):
"""
Check if an input can be read by the module class.
"""
return OpenslideFileTileSource.canRead(*args, **kwargs)
14 changes: 14 additions & 0 deletions sources/pil/large_image_source_pil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,17 @@ def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False,
raise TileSourceException('y is outside layer')
return self._outputTile(self._pilImage, TILE_FORMAT_PIL, x, y, z,
pilImageAllowed, numpyAllowed, **kwargs)


def open(*args, **kwargs):
"""
Create an instance of the module class.
"""
return PILFileTileSource(*args, **kwargs)


def canRead(*args, **kwargs):
"""
Check if an input can be read by the module class.
"""
return PILFileTileSource.canRead(*args, **kwargs)
14 changes: 14 additions & 0 deletions sources/test/large_image_source_test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,17 @@ def getState(self):
return 'test %r %r %r %r %r %r' % (
super().getState(), self.minLevel,
self.maxLevel, self.tileWidth, self.tileHeight, self.fractal)


def open(*args, **kwargs):
"""
Create an instance of the module class.
"""
return TestTileSource(*args, **kwargs)


def canRead(*args, **kwargs):
"""
Check if an input can be read by the module class.
"""
return TestTileSource.canRead(*args, **kwargs)
14 changes: 14 additions & 0 deletions sources/tiff/large_image_source_tiff/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,3 +647,17 @@ def _getAssociatedImage(self, imageKey):
if imageKey in self._associatedImages:
return PIL.Image.fromarray(self._associatedImages[imageKey])
return None


def open(*args, **kwargs):
"""
Create an instance of the module class.
"""
return TiffFileTileSource(*args, **kwargs)


def canRead(*args, **kwargs):
"""
Check if an input can be read by the module class.
"""
return TiffFileTileSource.canRead(*args, **kwargs)
8 changes: 4 additions & 4 deletions test/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def testConvertJp2kCompression(tmpdir):
info = tifftools.read_tiff(outputPath)
assert (info['ifds'][0]['tags'][tifftools.Tag.Compression.value]['data'][0] ==
tifftools.constants.Compression.JP2000.value)
source = large_image_source_tiff.TiffFileTileSource(outputPath)
source = large_image_source_tiff.open(outputPath)
image, _ = source.getRegion(
output={'maxWidth': 200, 'maxHeight': 200}, format=constants.TILE_FORMAT_NUMPY)
assert (image[12][167] == [215, 135, 172]).all()
Expand All @@ -160,7 +160,7 @@ def testConvertFromLargeImage(tmpdir):
imagePath = utilities.externaldata('data/sample_image.jp2.sha512')
outputPath = os.path.join(tmpdir, 'out.tiff')
large_image_converter.convert(imagePath, outputPath)
source = large_image_source_tiff.TiffFileTileSource(outputPath)
source = large_image_source_tiff.open(outputPath)
metadata = source.getMetadata()
assert metadata['levels'] == 6

Expand All @@ -169,7 +169,7 @@ def testConvertFromMultiframeImage(tmpdir):
imagePath = utilities.externaldata('data/sample.ome.tif.sha512')
outputPath = os.path.join(tmpdir, 'out.tiff')
large_image_converter.convert(imagePath, outputPath)
source = large_image_source_tiff.TiffFileTileSource(outputPath)
source = large_image_source_tiff.open(outputPath)
metadata = source.getMetadata()
assert metadata['levels'] == 5
assert len(metadata['frames']) == 3
Expand All @@ -181,7 +181,7 @@ def testConvertFromMultiframeImageNoSubIFDS(tmpdir):
imagePath = utilities.externaldata('data/sample.ome.tif.sha512')
outputPath = os.path.join(tmpdir, 'out.tiff')
large_image_converter.convert(imagePath, outputPath, subifds=False)
source = large_image_source_tiff.TiffFileTileSource(outputPath)
source = large_image_source_tiff.open(outputPath)
metadata = source.getMetadata()
assert metadata['levels'] == 5
assert len(metadata['frames']) == 3
Expand Down
4 changes: 2 additions & 2 deletions test/test_source_bioformats.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def testTilesFromBioformats():
import large_image_source_bioformats

imagePath = utilities.externaldata('data/HENormalN801.czi.sha512')
source = large_image_source_bioformats.BioformatsFileTileSource(imagePath)
source = large_image_source_bioformats.open(imagePath)
tileMetadata = source.getMetadata()

assert tileMetadata['tileWidth'] == 1024
Expand All @@ -28,7 +28,7 @@ def testInternalMetadata():
import large_image_source_bioformats

imagePath = utilities.externaldata('data/HENormalN801.czi.sha512')
source = large_image_source_bioformats.BioformatsFileTileSource(imagePath)
source = large_image_source_bioformats.open(imagePath)
metadata = source.getInternalMetadata()
assert 'sizeColorPlanes' in metadata

Expand Down
6 changes: 3 additions & 3 deletions test/test_source_dummy.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from large_image import getTileSource
import large_image
import large_image_source_dummy


def testDummyTileSource():
source = large_image_source_dummy.DummyTileSource()
source = large_image_source_dummy.open()
tileMetadata = source.getMetadata()
assert tileMetadata['tileWidth'] == 0
assert tileMetadata['tileHeight'] == 0
Expand All @@ -17,5 +17,5 @@ def testDummyTileSource():


def testGetDummyTileSource():
source = getTileSource('large_image://dummy')
source = large_image.open('large_image://dummy')
assert isinstance(source, large_image_source_dummy.DummyTileSource)
Loading

0 comments on commit f4451c5

Please sign in to comment.