From e65f43ab923d66d1ed14e080c3a57ea094b86170 Mon Sep 17 00:00:00 2001 From: David Manthey Date: Thu, 3 Feb 2022 13:15:09 -0500 Subject: [PATCH] Adjust scaling when generating lossless pyramidal files. Before, sub-resolution images could be interpolated. Now, they use nearest neighbor rather than mean when scaling, so all pixel values in sub-resolution images also exist in full-resolution images. --- CHANGELOG.md | 1 + large_image/tilesource/utilities.py | 7 +++++++ utilities/converter/large_image_converter/__main__.py | 7 +++++++ 3 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ce137514..6ab3a5057 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Make annotation schema more uniform ([#763](../../pull/763)) - Improve TileSource class repr ([#765](../../pull/765)) - Improve frame slider response with base quads ([#771](../../pull/771)) +- Default to nearest-neighbor scaling in lossless image conversion ([#772](../../pull/772)) ## Version 1.10.0 diff --git a/large_image/tilesource/utilities.py b/large_image/tilesource/utilities.py index ca820de0a..e98acd1ce 100644 --- a/large_image/tilesource/utilities.py +++ b/large_image/tilesource/utilities.py @@ -271,6 +271,7 @@ def _vipsParameters(forTiled=True, defaultCompression=None, **kwargs): :param level: compression level for zstd, 1-22 (default is 10). :param predictor: one of 'none', 'horizontal', or 'float' used for lzw and deflate. + :param shrinkMode: one of vips's VipsRegionShrink strings. :returns: a dictionary of parameters. """ if not forTiled: @@ -293,6 +294,12 @@ def _vipsParameters(forTiled=True, defaultCompression=None, **kwargs): 'Q': 90, 'predictor': 'horizontal', } + # For lossless modes, make sure pixel values in lower resolutions are + # values that exist in the upper resolutions. + if convertParams['compression'] in {'none', 'lzw'}: + convertParams['region_shrink'] = 'nearest' + if kwargs.get('shrinkMode') and kwargs['shrinkMode'] != 'default': + convertParams['region_shrink'] = kwargs['shrinkMode'] for vkey, kwkeys in { 'tile_width': {'tileSize'}, 'tile_height': {'tileSize'}, diff --git a/utilities/converter/large_image_converter/__main__.py b/utilities/converter/large_image_converter/__main__.py index 8a391d381..e6de001f2 100644 --- a/utilities/converter/large_image_converter/__main__.py +++ b/utilities/converter/large_image_converter/__main__.py @@ -84,6 +84,13 @@ def get_parser(): help='JP2K peak signal to noise ratio. 0 for lossless.') parser.add_argument( '--cr', type=int, help='JP2K compression ratio. 1 for lossless.') + parser.add_argument( + '--shrink-mode', '--shrink', '--reduce', dest='shrinkMode', + default=None, + choices=['mean', 'median', 'mode', 'max', 'min', 'nearest', 'default'], + help='When producing lower resolution images, use this method for ' + 'computing pixels. This defaults to median for lossy images and ' + 'nearest for lossless images.') parser.add_argument( '--only-associated', dest='_keep_associated', action='append', help='Only keep associated images with the specified keys. The value '