diff --git a/CHANGELOG.md b/CHANGELOG.md index 14e750411..43115842b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Change Log +## Version 1.11.2 + +### Improvements +- Emit events when an overlay annotation layer is created ([#787](../../pull/787)) +- Minor improvements to setFrameQuad to make it more flexible ([#790](../../pull/790)) +- Support drawing polygon selection regions ([#791](../../pull/791)) +- Add some automating options to getTileFramesQuadInfo ([#792](../../pull/792)) + +### Changes +- Change how we do a version check ([#785](../../pull/785)) + ## Version 1.11.1 ### Improvements diff --git a/girder/girder_large_image/rest/tiles.py b/girder/girder_large_image/rest/tiles.py index a9390f75f..604457240 100644 --- a/girder/girder_large_image/rest/tiles.py +++ b/girder/girder_large_image/rest/tiles.py @@ -1257,17 +1257,24 @@ def stream(): 'replace the defaults.', required=False) .param('query', 'Addition query parameters that would be passed to ' 'tile endpoints, such as style.', required=False) - .param('frameBase', 'Starting frame number (default 0)', + .param('frameBase', 'Starting frame number (default 0). If c/z/t/xy ' + 'then step through values from 0 to number of that axis - 1. ' + 'The axis specification in only useful for cache reporting or ' + 'scheduling', required=False, dataType='int') .param('frameStride', 'Only use every frameStride frame of the image ' - '(default 1)', required=False, dataType='int') + '(default 1). c/z/t/xy to use the length of that axis', + required=False, dataType='int') .param('frameGroup', 'Group frames when using multiple textures to ' - 'keep boundaries at a multiple of the group size number.', + 'keep boundaries at a multiple of the group size number. ' + 'c/z/t/xy to use the length of that axis.', required=False, dataType='int') .param('frameGroupFactor', 'Ignore grouping if the resultant images ' 'would be more than this factor smaller than without grouping ' '(default 4)', required=False, dataType='int') - .param('frameGroupStride', 'Reorder frames based on the to stride.', + .param('frameGroupStride', 'Reorder frames based on the to stride ' + '(default 1). "auto" to use frameGroup / frameStride if that ' + 'value is an integer.', required=False, dataType='int') .param('maxTextureSize', 'Maximum texture size in either dimension. ' 'This should be the smaller of a desired value and of the ' @@ -1299,11 +1306,11 @@ def tileFramesQuadInfo(self, item, params): options = self._parseParams(params, False, [ ('format', str), ('query', str), - ('frameBase', int), - ('frameStride', int), - ('frameGroup', int), + ('frameBase', str), + ('frameStride', str), + ('frameGroup', str), ('frameGroupFactor', int), - ('frameGroupStride', int), + ('frameGroupStride', str), ('maxTextureSize', int), ('maxTextures', int), ('maxTotalTexturePixels', int), diff --git a/large_image/tilesource/utilities.py b/large_image/tilesource/utilities.py index ead34e8d3..ea9cce8e8 100644 --- a/large_image/tilesource/utilities.py +++ b/large_image/tilesource/utilities.py @@ -681,18 +681,21 @@ def getTileFramesQuadInfo(metadata, options=None): {'encoding': 'JPEG', 'jpegQuality': 85, 'jpegSubsampling': 1}. query: Additional query options to add to the tile source, such as style. - frameBase: (default 0) Starting frame number used. + frameBase: (default 0) Starting frame number used. c/z/xy/z to step + through that index length (0 to 1 less than the value), which is + probably only useful for cache reporting or scheduling. frameStride: (default 1) Only use every ``frameStride`` frame of the - image. + image. c/z/xy/z to use that axis length. frameGroup: (default 1) If above 1 and multiple textures are used, each texture will have an even multiple of the group size number of frames. This helps control where texture loading transitions - occur. + occur. c/z/xy/z to use that axis length. frameGroupFactor: (default 4) If ``frameGroup`` would reduce the size of the tile images beyond this factor, don't use it. frameGroupStride: (default 1) If ``frameGroup`` is above 1 and multiple textures are used, then the frames are reordered based on this - stride value. + stride value. "auto" to use frameGroup / frameStride if that + value is an integer. maxTextureSize: Limit the maximum texture size to a square of this size. maxTextures: (default 1) If more than one, allow multiple textures to @@ -730,6 +733,30 @@ def getTileFramesQuadInfo(metadata, options=None): } opts = defaultOptions.copy() opts.update(options or {}) + + opts['frameStride'] = ( + int(opts['frameStride']) if str(opts['frameStride']).isdigit() else + metadata.get('IndexRange', {}).get('Index' + opts['frameStride'].upper(), 1)) + opts['frameGroup'] = ( + int(opts['frameGroup']) if str(opts['frameGroup']).isdigit() else + metadata.get('IndexRange', {}).get('Index' + opts['frameGroup'].upper(), 1)) + opts['frameGroupStride'] = ( + int(opts['frameGroupStride']) if opts['frameGroupStride'] != 'auto' else + max(1, opts['frameGroup'] // opts['frameStride'])) + if str(opts['frameBase']).isdigit(): + opts['frameBase'] = int(opts['frameBase']) + else: + status = { + 'metadata': metadata, + 'options': opts, + 'src': [], + } + for val in range(metadata.get( + 'IndexRange', {}).get('Index' + opts['frameBase'].upper(), 1)): + opts['frameBase'] = val + result = getTileFramesQuadInfo(metadata, opts) + status['src'].extend(result['src']) + return status sizeX, sizeY = metadata['sizeX'], metadata['sizeY'] numFrames = len(metadata.get('frames', [])) or 1 frames = [] diff --git a/test/test_source_base.py b/test/test_source_base.py index 7c0029b39..e4f91cb1b 100644 --- a/test/test_source_base.py +++ b/test/test_source_base.py @@ -427,10 +427,24 @@ def testTileOverlapWithRegionOffset(): 'left': 2880, 'top': 3816, }), + + ({ + 'maxTextures': 8, + 'maxTextureSize': 4096, + 'frameBase': 'c', + 'frameStride': 'c', + 'frameGroup': 'z', + 'frameGroupStride': 'auto', + }, 10, None, None, { + 'framesAcross': 5, + 'height': 624, + 'width': 816, + }, None, None), ]) def testGetTileFramesQuadInfo(options, lensrc, lenquads, frame10, src0, srclast, quads10): metadata = { 'frames': [0] * 250, + 'IndexRange': {'IndexC': 5, 'IndexZ': 25, 'IndexT': 2}, 'levels': 10, 'sizeX': 100000, 'sizeY': 75000, @@ -439,8 +453,9 @@ def testGetTileFramesQuadInfo(options, lensrc, lenquads, frame10, src0, srclast, } results = large_image.tilesource.utilities.getTileFramesQuadInfo(metadata, options) assert len(results['src']) == lensrc - assert len(results['quads']) == lenquads - if len(results['frames']) > 10: + if lenquads: + assert len(results['quads']) == lenquads + if frame10 is not None and len(results['frames']) > 10: assert results['frames'][10] == frame10 for key, value in src0.items(): if value is not None: @@ -450,7 +465,7 @@ def testGetTileFramesQuadInfo(options, lensrc, lenquads, frame10, src0, srclast, if srclast is not None: for key, value in srclast.items(): assert results['src'][-1][key] == value - if len(results['quads']) > 10 and quads10 is not None: + if quads10 is not None and len(results['quads']) > 10: crop10 = results['quads'][10]['crop'] for key, value in quads10.items(): assert crop10[key] == value