From f35dad1831e9441ac84d98e1795c784bd33081d1 Mon Sep 17 00:00:00 2001 From: David Manthey Date: Tue, 24 Aug 2021 13:24:39 -0400 Subject: [PATCH] Harden tile source detection. Detecting geospatial files was brittler than intended. Bioformats throws poor errors. --- CHANGELOG.md | 3 +++ large_image/tilesource/__init__.py | 2 ++ large_image/tilesource/utilities.py | 10 +++++++--- .../large_image_source_bioformats/__init__.py | 2 +- sources/gdal/large_image_source_gdal/__init__.py | 1 + utilities/converter/large_image_converter/__init__.py | 10 +++++++--- 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4321d3241..298f212a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ ### Features - Deepzoom tile source (#641) +### Bug Fixes +- Harden tile source detection (#644) + ## Version 1.7.1 ### Improvements diff --git a/large_image/tilesource/__init__.py b/large_image/tilesource/__init__.py index 0bb22d84a..329d5f09e 100644 --- a/large_image/tilesource/__init__.py +++ b/large_image/tilesource/__init__.py @@ -28,6 +28,8 @@ def isGeospatial(path): ds = gdal.Open(path, gdalconst.GA_ReadOnly) except Exception: return False + if ds.GetGCPs() and ds.GetGCPProjection(): + return True if ds.GetProjection(): return True if ds.GetDriver().ShortName in {'NITF', 'netCDF'}: diff --git a/large_image/tilesource/utilities.py b/large_image/tilesource/utilities.py index 3210df139..29a7c1888 100644 --- a/large_image/tilesource/utilities.py +++ b/large_image/tilesource/utilities.py @@ -205,11 +205,13 @@ def _vipsCast(image, mustBe8Bit=False, originalScale=None): return image -def _gdalParameters(defaultCompression=None, **kwargs): +def _gdalParameters(defaultCompression=None, eightbit=None, **kwargs): """ Return an array of gdal translation parameters. :param defaultCompression: if not specified, use this value. + :param eightbit: True or False to indicate that the bit depth per sample is + known. None for unknown. Optional parameters that can be specified in kwargs: @@ -227,8 +229,9 @@ def _gdalParameters(defaultCompression=None, **kwargs): 'tileSize': 256, 'compression': 'lzw', 'quality': 90, - 'predictor': 'yes', } + if eightbit is not None: + options['predictor'] = 'yes' if eightbit else 'none' predictor = { 'none': 'NO', 'horizontal': 'STANDARD', @@ -240,7 +243,8 @@ def _gdalParameters(defaultCompression=None, **kwargs): cmdopt += ['-co', 'BLOCKSIZE=%d' % options['tileSize']] cmdopt += ['-co', 'COMPRESS=%s' % options['compression'].upper()] cmdopt += ['-co', 'QUALITY=%s' % options['quality']] - cmdopt += ['-co', 'PREDICTOR=%s' % predictor[options['predictor']]] + if 'predictor' in options: + cmdopt += ['-co', 'PREDICTOR=%s' % predictor[options['predictor']]] if 'level' in options: cmdopt += ['-co', 'LEVEL=%s' % options['level']] return cmdopt diff --git a/sources/bioformats/large_image_source_bioformats/__init__.py b/sources/bioformats/large_image_source_bioformats/__init__.py index f6d2eaac1..e2f9b641e 100644 --- a/sources/bioformats/large_image_source_bioformats/__init__.py +++ b/sources/bioformats/large_image_source_bioformats/__init__.py @@ -160,7 +160,7 @@ def __init__(self, path, **kwargs): # noqa javabridge.attach() try: self._bioimage = bioformats.ImageReader(largeImagePath) - except AttributeError as exc: + except (AttributeError, FileNotFoundError) as exc: self.logger.debug('File cannot be opened via Bioformats. (%r)' % exc) raise TileSourceException('File cannot be opened via Bioformats. (%r)' % exc) _openImages.append(self) diff --git a/sources/gdal/large_image_source_gdal/__init__.py b/sources/gdal/large_image_source_gdal/__init__.py index e3fbcdbdf..eeda447d7 100644 --- a/sources/gdal/large_image_source_gdal/__init__.py +++ b/sources/gdal/large_image_source_gdal/__init__.py @@ -71,6 +71,7 @@ class GDALFileTileSource(FileTileSource, metaclass=LruCacheMetaclass): name = 'gdalfile' extensions = { None: SourcePriority.MEDIUM, + 'geotiff': SourcePriority.PREFERRED, # National Imagery Transmission Format 'ntf': SourcePriority.PREFERRED, 'nitf': SourcePriority.PREFERRED, diff --git a/utilities/converter/large_image_converter/__init__.py b/utilities/converter/large_image_converter/__init__.py index ca34fb38e..0b80a1786 100644 --- a/utilities/converter/large_image_converter/__init__.py +++ b/utilities/converter/large_image_converter/__init__.py @@ -854,6 +854,7 @@ def convert(inputPath, outputPath=None, **kwargs): # noqa: C901 geospatial = kwargs.get('geospatial') if geospatial is None: geospatial = is_geospatial(inputPath) + logger.debug('Is file geospatial: %r', geospatial) suffix = format_hook('adjust_params', geospatial, kwargs, **kwargs) if suffix is False: return @@ -871,16 +872,16 @@ def convert(inputPath, outputPath=None, **kwargs): # noqa: C901 tiffinfo = tifftools.read_tiff(inputPath) except Exception: tiffinfo = None + eightbit = _is_eightbit(inputPath, tiffinfo) if not kwargs.get('compression', None): kwargs = kwargs.copy() lossy = _is_lossy(inputPath, tiffinfo) logger.debug('Is file lossy: %r', lossy) - eightbit = _is_eightbit(inputPath, tiffinfo) logger.debug('Is file 8 bits per samples: %r', eightbit) kwargs['_compression'] = None kwargs['compression'] = 'jpeg' if lossy and eightbit else 'lzw' if geospatial: - _generate_geotiff(inputPath, outputPath, **kwargs) + _generate_geotiff(inputPath, outputPath, eightbit=eightbit or None, **kwargs) else: with TemporaryDirectory() as tempDir: tempPath = os.path.join(tempDir, os.path.basename(outputPath)) @@ -917,7 +918,10 @@ def is_geospatial(path): ds = gdal.Open(path, gdalconst.GA_ReadOnly) except Exception: return False - if ds and (ds.GetProjection() or ds.GetDriver().ShortName in {'NITF', 'netCDF'}): + if ds and ( + (ds.GetGCPs() and ds.GetGCPProjection()) or + ds.GetProjection() or + ds.GetDriver().ShortName in {'NITF', 'netCDF'}): return True return False