Skip to content

Commit

Permalink
Merge pull request #904 from girder/more-tile-encodings
Browse files Browse the repository at this point in the history
Register the tile output formats from PIL.
  • Loading branch information
manthey authored Aug 2, 2022
2 parents 28e31b5 + 8f8eaf9 commit 7eb1f18
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 13 deletions.
4 changes: 3 additions & 1 deletion docs/tilesource_options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ Python tile functions can return tile data as images, numpy arrays, or PIL Image
Encoding
--------

The ``encoding`` parameter can be one of ``JPEG``, ``PNG``, ``TIFF``, or ``JFIF``. When the tile is output as an image, this is the preferred format. Note that ``JFIF`` is a specific variant of ``JPEG`` that will always use either the Y or YCbCr color space as well as constraining other options.
The ``encoding`` parameter can be one of ``JPEG``, ``PNG``, ``TIFF``, ``JFIF``, or ``TILED``. When the tile is output as an image, this is the preferred format. Note that ``JFIF`` is a specific variant of ``JPEG`` that will always use either the Y or YCbCr color space as well as constraining other options. ``TILED`` will output a tiled tiff file; this is slower than ``TIFF`` but can support images of arbitrary size.

Additional options are available based on the PIL.Image registered encoders.

The ``encoding`` only affects output when ``format`` is ``TILE_FORMAT_IMAGE``.

Expand Down
16 changes: 9 additions & 7 deletions girder/girder_large_image/rest/tiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
from girder.models.item import Item
from girder.utility.progress import setResponseTimeLimit
from large_image.cache_util import strhash
from large_image.constants import TileInputUnits
from large_image.constants import TileInputUnits, TileOutputMimeTypes
from large_image.exceptions import TileGeneralError

from .. import loadmodelcache
Expand All @@ -46,7 +46,12 @@
'image/png': 'png',
'image/tiff': 'tiff',
}
for key, value in TileOutputMimeTypes.items():
if value not in MimeTypeExtensions:
MimeTypeExtensions[value] = key.lower()
ImageMimeTypes = list(MimeTypeExtensions)
EncodingTypes = list(TileOutputMimeTypes.keys()) + [
'pickle', 'pickle:3', 'pickle:4', 'pickle:5']


def _adjustParams(params):
Expand Down Expand Up @@ -697,8 +702,7 @@ def deleteTiles(self, item, params):
'have. For geospatial sources, TILED will also have '
'appropriate tagging. Pickle emits python pickle data with an '
'optional specific protocol', required=False,
enum=['JPEG', 'PNG', 'TIFF', 'TILED', 'pickle', 'pickle:3',
'pickle:4', 'pickle:5'], default='JPEG')
enum=EncodingTypes, default='JPEG')
.param('contentDisposition', 'Specify the Content-Disposition response '
'header disposition-type value.', required=False,
enum=['inline', 'attachment'])
Expand Down Expand Up @@ -807,8 +811,7 @@ def getTilesThumbnail(self, item, params):
'have. For geospatial sources, TILED will also have '
'appropriate tagging. Pickle emits python pickle data with an '
'optional specific protocol', required=False,
enum=['JPEG', 'PNG', 'TIFF', 'TILED', 'pickle', 'pickle:3',
'pickle:4', 'pickle:5'], default='JPEG')
enum=EncodingTypes, default='JPEG')
.param('jpegQuality', 'Quality used for generating JPEG images',
required=False, dataType='int', default=95)
.param('jpegSubsampling', 'Chroma subsampling used for generating '
Expand Down Expand Up @@ -1220,8 +1223,7 @@ def getAssociatedImageMetadata(self, item, image, params):
'have. For geospatial sources, TILED will also have '
'appropriate tagging. Pickle emits python pickle data with an '
'optional specific protocol', required=False,
enum=['JPEG', 'PNG', 'TIFF', 'TILED', 'pickle', 'pickle:3',
'pickle:4', 'pickle:5'], default='JPEG')
enum=EncodingTypes, default='JPEG')
.param('jpegQuality', 'Quality used for generating JPEG images',
required=False, dataType='int', default=95)
.param('jpegSubsampling', 'Chroma subsampling used for generating '
Expand Down
10 changes: 5 additions & 5 deletions large_image/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,16 @@ class SourcePriority:


TileOutputMimeTypes = {
# JFIF forces conversion to JPEG through PIL to ensure the image is in a
# common colorspace. JPEG colorspace is complex: see
# https://docs.oracle.com/javase/8/docs/api/javax/imageio/metadata/
# doc-files/jpeg_metadata.html
'JFIF': 'image/jpeg',
'JPEG': 'image/jpeg',
'PNG': 'image/png',
'TIFF': 'image/tiff',
# TILED indicates the region output should be generated as a tiled TIFF
'TILED': 'image/tiff',
# JFIF forces conversion to JPEG through PIL to ensure the image is in a
# common colorspace. JPEG colorspace is complex: see
# https://docs.oracle.com/javase/8/docs/api/javax/imageio/metadata/
# doc-files/jpeg_metadata.html
'JFIF': 'image/jpeg',
}
TileOutputPILFormat = {
'JFIF': 'JPEG'
Expand Down
20 changes: 20 additions & 0 deletions large_image/tilesource/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -921,3 +921,23 @@ def histogramThreshold(histogram, threshold, fromMax=False):
return edges[idx]
tally += hist[idx]
return edges[-1]


def addPILFormatsToOutputOptions():
"""
Check PIL for available formats that be saved and add them to the lists of
of available formats.
"""
# Call this to actual register the extensions
PIL.Image.registered_extensions()
for key, value in PIL.Image.MIME.items():
if key not in TileOutputMimeTypes and key in PIL.Image.SAVE:
TileOutputMimeTypes[key] = value
for key, value in PIL.Image.registered_extensions().items():
key = key.lstrip('.')
if (key not in TileOutputMimeTypes and value in TileOutputMimeTypes and
key not in TileOutputPILFormat):
TileOutputPILFormat[key] = value


addPILFormatsToOutputOptions()
9 changes: 9 additions & 0 deletions test/test_source_tiff.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ def testTileIterator():
tileCount += 1
assert tile['tile'][:len(utilities.TIFFHeader)] == utilities.TIFFHeader
assert tileCount == 45
# Ask for WEBPs
tileCount = 0
for tile in source.tileIterator(
scale={'magnification': 2.5},
format=constants.TILE_FORMAT_IMAGE,
encoding='WEBP'):
tileCount += 1
assert tile['tile'][8:12] == b'WEBP'
assert tileCount == 45


def testTileIteratorRetiling():
Expand Down

0 comments on commit 7eb1f18

Please sign in to comment.