Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Register the tile output formats from PIL. #904

Merged
merged 1 commit into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -892,3 +892,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 @@ -74,6 +74,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