Skip to content

Commit

Permalink
Merge pull request #408 from girder/style-option-2.x
Browse files Browse the repository at this point in the history
Style option 2.x
  • Loading branch information
manthey authored Jan 10, 2020
2 parents 70274a2 + 8a69e2d commit 3f57735
Show file tree
Hide file tree
Showing 14 changed files with 857 additions and 269 deletions.
2 changes: 1 addition & 1 deletion plugin_tests/sources_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ def testGetRegion(self):
# We should be able to get a PIL image
image, imageFormat = source.getRegion(
scale={'magnification': 2.5},
format=(tilesource.TILE_FORMAT_PIL, tilesource.TILE_FORMAT_NUMPY))
format=(tilesource.TILE_FORMAT_PIL, ))
self.assertEqual(imageFormat, tilesource.TILE_FORMAT_PIL)
self.assertEqual(image.width, 1438)
self.assertEqual(image.height, 1447)
Expand Down
5 changes: 5 additions & 0 deletions server/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,8 @@ class SourcePriority(object):
LOW = 4
FALLBACK = 5
MANUAL = 6 # Will never be selected automatically


TILE_FORMAT_IMAGE = 'image'
TILE_FORMAT_PIL = 'PIL'
TILE_FORMAT_NUMPY = 'numpy'
12 changes: 12 additions & 0 deletions server/models/image_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,18 @@ def getPixel(self, item, **kwargs):
tileSource = self._loadTileSource(item, **kwargs)
return tileSource.getPixel(**kwargs)

def histogram(self, item, **kwargs):
"""
Using a tile source, get a histogram of the image.
:param item: the item with the tile source.
:param **kwargs: optional arguments. See the tilesource histogram
method.
:returns: histogram object.
"""
tileSource = self._loadTileSource(item, **kwargs)
return tileSource.histogram(**kwargs)

def tileSource(self, item, **kwargs):
"""
Get a tile source for an item.
Expand Down
87 changes: 87 additions & 0 deletions server/rest/tiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ def __init__(self, apiRoot):
self.getTilesRegion)
apiRoot.item.route('GET', (':itemId', 'tiles', 'pixel'),
self.getTilesPixel)
apiRoot.item.route('GET', (':itemId', 'tiles', 'histogram'),
self.getHistogram)
apiRoot.item.route('GET', (':itemId', 'tiles', 'zxy', ':z', ':x', ':y'),
self.getTile)
apiRoot.item.route('GET', (':itemId', 'tiles', 'fzxy', ':frame', ':z', ':x', ':y'),
Expand Down Expand Up @@ -552,6 +554,7 @@ def getTilesThumbnail(self, item, params):
('jpegSubsampling', int),
('tiffCompression', str),
('encoding', str),
('style', str),
('contentDisposition', str),
])
try:
Expand Down Expand Up @@ -637,6 +640,7 @@ def getTilesThumbnail(self, item, params):
.param('tiffCompression', 'Compression method when storing a TIFF '
'image', required=False,
enum=['raw', 'tiff_lzw', 'jpeg', 'tiff_adobe_deflate'])
.param('style', 'JSON-encoded style string', required=False)
.param('contentDisposition', 'Specify the Content-Disposition response '
'header disposition-type value.', required=False,
enum=['inline', 'attachment'])
Expand Down Expand Up @@ -671,6 +675,7 @@ def getTilesRegion(self, item, params):
('jpegQuality', int),
('jpegSubsampling', int),
('tiffCompression', str),
('style', str),
('contentDisposition', str),
])
try:
Expand Down Expand Up @@ -724,6 +729,87 @@ def getTilesPixel(self, item, params):
raise RestException('Value Error: %s' % e.args[0])
return pixel

@describeRoute(
Description('Get a histogram for any region of a large image item.')
.notes('This can take all of the parameters as the region endpoint, '
'plus some histogram-specific parameters. Only typically used '
'parameters are listed. The returned result is a list with '
'one entry per channel (always one of L, LA, RGB, or RGBA '
'colorspace). Each entry has the histogram values, bin edges, '
'minimum and maximum values for the channel, and number of '
'samples (pixels) used in the computation.')
.param('itemId', 'The ID of the item.', paramType='path')
.param('width', 'The maximum width of the analyzed region in pixels.',
default=2048, required=False, dataType='int')
.param('height', 'The maximum height of the analyzed region in pixels.',
default=2048, required=False, dataType='int')
.param('resample', 'If false, an existing level of the image is used '
'for the histogram. If true, the internal values are '
'interpolated to match the specified size as needed.',
required=False, dataType='boolean', default=False)
.param('frame', 'For multiframe images, the 0-based frame number. '
'This is ignored on non-multiframe images.', required=False,
dataType='int')
.param('bins', 'The number of bins in the histogram.',
default=256, required=False, dataType='int')
.param('rangeMin', 'The minimum value in the histogram. Defaults to '
'the minimum value in the image.',
required=False, dataType='float')
.param('rangeMax', 'The maximum value in the histogram. Defaults to '
'the maximum value in the image.',
required=False, dataType='float')
.param('density', 'If true, scale the results by the number of '
'samples.', required=False, dataType='boolean', default=False)
.errorResponse('ID was invalid.')
.errorResponse('Read access was denied for the item.', 403)
)
@access.public
@loadmodel(model='item', map={'itemId': 'item'}, level=AccessType.READ)
def getHistogram(self, item, params):
_adjustParams(params)
params = self._parseParams(params, True, [
('left', float, 'region', 'left'),
('top', float, 'region', 'top'),
('right', float, 'region', 'right'),
('bottom', float, 'region', 'bottom'),
('regionWidth', float, 'region', 'width'),
('regionHeight', float, 'region', 'height'),
('units', str, 'region', 'units'),
('unitsWH', str, 'region', 'unitsWH'),
('width', int, 'output', 'maxWidth'),
('height', int, 'output', 'maxHeight'),
('fill', str),
('magnification', float, 'scale', 'magnification'),
('mm_x', float, 'scale', 'mm_x'),
('mm_y', float, 'scale', 'mm_y'),
('exact', bool, 'scale', 'exact'),
('frame', int),
('encoding', str),
('jpegQuality', int),
('jpegSubsampling', int),
('tiffCompression', str),
('style', str),
('resample', bool),
('bins', int),
('rangeMin', int),
('rangeMax', int),
('density', bool),
])
histRange = None
if 'rangeMin' in params or 'rangeMax' in params:
histRange = [params.pop('rangeMin', 0), params.pop('rangeMax', 256)]
result = self.imageItemModel.histogram(item, range=histRange, **params)
result = result['histogram']
# Cast everything to lists and floats so json with encode properly
for entry in result:
for key in {'bin_edges', 'hist', 'range'}:
if key in entry:
entry[key] = [float(val) for val in list(entry[key])]
for key in {'min', 'max', 'samples'}:
if key in entry:
entry[key] = float(entry[key])
return result

@describeRoute(
Description('Get a list of additional images associated with a large image.')
.param('itemId', 'The ID of the item.', paramType='path')
Expand Down Expand Up @@ -771,6 +857,7 @@ def getAssociatedImage(self, itemId, image, params):
('jpegSubsampling', int),
('tiffCompression', str),
('encoding', str),
('style', str),
('contentDisposition', str),
])
try:
Expand Down
Loading

0 comments on commit 3f57735

Please sign in to comment.