large-image-source-vips 1.23.7.dev4__tar.gz → 1.33.4.dev37__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (17) hide show
  1. large_image_source_vips-1.33.4.dev37/PKG-INFO +42 -0
  2. {large-image-source-vips-1.23.7.dev4 → large_image_source_vips-1.33.4.dev37}/README.rst +48 -60
  3. {large-image-source-vips-1.23.7.dev4 → large_image_source_vips-1.33.4.dev37}/large_image_source_vips/__init__.py +151 -34
  4. large_image_source_vips-1.33.4.dev37/large_image_source_vips.egg-info/PKG-INFO +42 -0
  5. {large-image-source-vips-1.23.7.dev4 → large_image_source_vips-1.33.4.dev37}/large_image_source_vips.egg-info/SOURCES.txt +1 -0
  6. large_image_source_vips-1.33.4.dev37/large_image_source_vips.egg-info/requires.txt +7 -0
  7. large_image_source_vips-1.33.4.dev37/pyproject.toml +7 -0
  8. {large-image-source-vips-1.23.7.dev4 → large_image_source_vips-1.33.4.dev37}/setup.py +7 -29
  9. large-image-source-vips-1.23.7.dev4/PKG-INFO +0 -25
  10. large-image-source-vips-1.23.7.dev4/large_image_source_vips.egg-info/PKG-INFO +0 -25
  11. large-image-source-vips-1.23.7.dev4/large_image_source_vips.egg-info/requires.txt +0 -10
  12. {large-image-source-vips-1.23.7.dev4 → large_image_source_vips-1.33.4.dev37}/LICENSE +0 -0
  13. {large-image-source-vips-1.23.7.dev4 → large_image_source_vips-1.33.4.dev37}/large_image_source_vips/girder_source.py +0 -0
  14. {large-image-source-vips-1.23.7.dev4 → large_image_source_vips-1.33.4.dev37}/large_image_source_vips.egg-info/dependency_links.txt +0 -0
  15. {large-image-source-vips-1.23.7.dev4 → large_image_source_vips-1.33.4.dev37}/large_image_source_vips.egg-info/entry_points.txt +0 -0
  16. {large-image-source-vips-1.23.7.dev4 → large_image_source_vips-1.33.4.dev37}/large_image_source_vips.egg-info/top_level.txt +0 -0
  17. {large-image-source-vips-1.23.7.dev4 → large_image_source_vips-1.33.4.dev37}/setup.cfg +0 -0
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: large-image-source-vips
3
+ Version: 1.33.4.dev37
4
+ Summary: A libvips tilesource for large_image.
5
+ Home-page: https://github.com/girder/large_image
6
+ Author: Kitware, Inc.
7
+ Author-email: kitware@kitware.com
8
+ License: Apache-2.0
9
+ Keywords: large_image,tile source
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/x-rst
19
+ License-File: LICENSE
20
+ Requires-Dist: large-image>=1.33.4.dev37
21
+ Requires-Dist: numpy
22
+ Requires-Dist: packaging
23
+ Requires-Dist: pyvips
24
+ Provides-Extra: girder
25
+ Requires-Dist: girder-large-image; extra == "girder"
26
+ Dynamic: author
27
+ Dynamic: author-email
28
+ Dynamic: classifier
29
+ Dynamic: description
30
+ Dynamic: description-content-type
31
+ Dynamic: home-page
32
+ Dynamic: keywords
33
+ Dynamic: license
34
+ Dynamic: license-file
35
+ Dynamic: provides-extra
36
+ Dynamic: requires-dist
37
+ Dynamic: requires-python
38
+ Dynamic: summary
39
+
40
+ A libvips tilesource for large_image.
41
+
42
+ See the large-image package for more details.
@@ -1,25 +1,23 @@
1
1
  Large Image
2
2
  ===========
3
3
 
4
- |build-status| |codecov-io| |license-badge| |doi-badge| |pypi-badge|
4
+ .. image:: https://img.shields.io/circleci/build/github/girder/large_image.svg
5
+ :target: https://circleci.com/gh/girder/large_image
6
+ :alt: Build Status
5
7
 
6
- .. |build-status| image:: https://img.shields.io/circleci/build/github/girder/large_image.svg
7
- :target: https://circleci.com/gh/girder/large_image
8
- :alt: Build Status
8
+ .. image:: https://img.shields.io/badge/license-Apache%202-blue.svg
9
+ :target: https://raw.githubusercontent.com/girder/large_image/master/LICENSE
10
+ :alt: License
9
11
 
10
- .. |license-badge| image:: https://img.shields.io/badge/license-Apache%202-blue.svg
11
- :target: https://raw.githubusercontent.com/girder/large_image/master/LICENSE
12
- :alt: License
12
+ .. image:: https://img.shields.io/codecov/c/github/girder/large_image.svg
13
+ :target: https://codecov.io/github/girder/large_image?branch=master
14
+ :alt: codecov.io
13
15
 
14
- .. |codecov-io| image:: https://img.shields.io/codecov/c/github/girder/large_image.svg
15
- :target: https://codecov.io/github/girder/large_image?branch=master
16
- :alt: codecov.io
16
+ .. image:: https://img.shields.io/badge/DOI-10.5281%2Fzenodo.4562625-blue.svg
17
+ :target: https://doi.org/10.5281/zenodo.4562625
17
18
 
18
- .. |doi-badge| image:: https://img.shields.io/badge/DOI-10.5281%2Fzenodo.4723355-blue
19
- :target: https://zenodo.org/badge/latestdoi/45569214
20
-
21
- .. |pypi-badge| image:: https://img.shields.io/pypi/v/large-image.svg?logo=python&logoColor=white
22
- :target: https://pypi.org/project/large-image/
19
+ .. image:: https://img.shields.io/pypi/v/large-image.svg?logo=python&logoColor=white
20
+ :target: https://pypi.org/project/large-image/
23
21
 
24
22
  *Python modules to work with large, multiresolution images.*
25
23
 
@@ -39,16 +37,20 @@ Highlights
39
37
  Installation
40
38
  ------------
41
39
 
42
- In addition to installing the ``large-image`` package, you'll need at least one tile source (a ``large-image-source-xxx`` package). You can install everything from the main project with one of these commands:
40
+ In addition to installing the base ``large-image`` package, you'll need at least one tile source which corresponds to your target file format(s) (a ``large-image-source-xxx`` package). You can install everything from the main project with one of these commands:
43
41
 
44
42
  Pip
45
43
  ~~~
46
44
 
45
+ Install common tile sources on linux, OSX, or Windows::
46
+
47
+ pip install large-image[common]
48
+
47
49
  Install all tile sources on linux::
48
50
 
49
51
  pip install large-image[all] --find-links https://girder.github.io/large_image_wheels
50
52
 
51
- Install all tile sources and all Girder plugins on linux::
53
+ When using large-image with an instance of `Girder`_, install all tile sources and all Girder plugins on linux::
52
54
 
53
55
  pip install large-image[all] girder-large-image-annotation[tasks] --find-links https://girder.github.io/large_image_wheels
54
56
 
@@ -56,8 +58,9 @@ Install all tile sources and all Girder plugins on linux::
56
58
  Conda
57
59
  ~~~~~
58
60
 
59
- Conda makes dependency management a bit easier if not on Linux. Some of the source modules are available on conda-forge. You can install the following::
61
+ Conda makes dependency management a bit easier if not on Linux. The base module, converter module, and two of the source modules are available on conda-forge. You can install the following::
60
62
 
63
+ conda install -c conda-forge large-image
61
64
  conda install -c conda-forge large-image-source-gdal
62
65
  conda install -c conda-forge large-image-source-tiff
63
66
  conda install -c conda-forge large-image-converter
@@ -101,81 +104,66 @@ Large Image consists of several Python modules designed to work together. These
101
104
 
102
105
  - ``performance``: include optional modules that can improve performance
103
106
 
107
+ - ``common``: all of the tile sources and above packages that will install directly from pypi without other external libraries on linux, OSX, and Windows.
108
+
104
109
  - ``all``: for all of the above
105
110
 
106
111
  - ``large-image-converter``: A utility for using pyvips and other libraries to convert images into pyramidal tiff files that can be read efficiently by large_image.
107
- You can specify extras_require of ``jp2k`` to include modules to allow output to JPEG2000 compression, ``sources`` to include all sources, and ``stats`` to include modules to allow computing compression noise statistics.
112
+ You can specify extras_require of ``jp2k`` to include modules to allow output to JPEG2000 compression, ``sources`` to include all sources, ``stats`` to include modules to allow computing compression noise statistics, ``geospatial`` to include support for converting geospatial sources, or ``all`` for all of the optional extras_require.
108
113
 
109
114
  - Tile sources:
110
115
 
111
- - ``large-image-source-tiff``: A tile source for reading pyramidal tiff files in common compression formats.
112
-
113
- - ``large-image-source-openslide``: A tile source using the OpenSlide library. This works with svs, ndpi, Mirax, tiff, vms, and other file formats.
116
+ - ``large-image-source-bioformats``: A tile source for reading any file handled by the Java Bioformats library.
114
117
 
115
- - ``large-image-source-ometiff``: A tile source using the tiff library that can handle some multi-frame OMETiff files.
118
+ - ``large-image-source-deepzoom``: A tile source for reading Deepzoom tiles.
116
119
 
117
- - ``large-image-source-pil``: A tile source for small images via the Python Imaging Library (Pillow).
120
+ - ``large-image-source-dicom``: A tile source for reading DICOM Whole Slide Images (WSI).
118
121
 
119
122
  - ``large-image-source-gdal``: A tile source for reading geotiff files via GDAL. This handles source data with more complex transforms than the mapnik tile source.
120
123
 
121
124
  - ``large-image-source-mapnik``: A tile source for reading geotiff and netcdf files via Mapnik and GDAL. This handles more vector issues than the gdal tile source.
122
125
 
123
- - ``large-image-source-openjpeg``: A tile source using the Glymur library to read jp2 (JPEG 2000) files.
124
-
125
- - ``large-image-source-nd2``: A tile source for reading nd2 (NIS Element) images.
126
-
127
- - ``large-image-source-bioformats``: A tile source for reading any file handled by the Java Bioformats library.
128
-
129
- - ``large-image-source-deepzoom``: A tile source for reading Deepzoom tiles.
130
-
131
126
  - ``large-image-source-multi``: A tile source for compositing other tile sources into a single multi-frame source.
132
127
 
133
- - ``large-image-source-vips``: A tile source for reading any files handled by libvips. This also can be used for writing tiled images from numpy arrays.
134
-
135
- - ``large-image-source-tifffile``: A tile source using the tifffile library that can handle a wide variety of tiff-like files.
128
+ - ``large-image-source-nd2``: A tile source for reading nd2 (NIS Element) images.
136
129
 
137
- - ``large-image-source-dicom``: A tile source for reading DICOM WSI images.
130
+ - ``large-image-source-ometiff``: A tile source using the tiff library that can handle most multi-frame OMETiff files that are compliant with the specification.
138
131
 
139
- - ``large-image-source-test``: A tile source that generates test tiles, including a simple fractal pattern. Useful for testing extreme zoom levels.
140
-
141
- - ``large-image-source-dummy``: A tile source that does nothing.
132
+ - ``large-image-source-openjpeg``: A tile source using the Glymur library to read jp2 (JPEG 2000) files.
142
133
 
143
- Most tile sources can be used with girder-large-image. You can specific an extras_require of ``girder`` to include ``girder-large-image`` with the source.
134
+ - ``large-image-source-openslide``: A tile source using the OpenSlide library. This works with svs, ndpi, Mirax, tiff, vms, and other file formats.
144
135
 
145
- - As a Girder plugin:
136
+ - ``large-image-source-pil``: A tile source for small images via the Python Imaging Library (Pillow). By default, the maximum size is 4096, but the maximum size can be configured.
146
137
 
147
- - ``girder-large-image``: Large Image as a Girder_ 3.x plugin.
148
- You can specify extras_require of ``tasks`` to install a Girder Worker task that can convert otherwise unreadable images to pyramidal tiff files.
138
+ - ``large-image-source-tiff``: A tile source for reading pyramidal tiff files in common compression formats.
149
139
 
150
- - ``girder-large-image-annotation``: Annotations for large images as a Girder_ 3.x plugin.
140
+ - ``large-image-source-tifffile``: A tile source using the tifffile library that can handle a wide variety of tiff-like files.
151
141
 
152
- - ``large-image-tasks``: A utility for running the converter via Girder Worker.
153
- You can specify an extras_require of ``girder`` to include modules needed to work with the Girder remote worker or ``worker`` to include modules needed on the remote side of the Girder remote worker. If neither is specified, some conversion tasks can be run using Girder local jobs.
142
+ - ``large-image-source-vips``: A tile source for reading any files handled by libvips. This also can be used for writing tiled images from numpy arrays (up to 4 dimensions).
154
143
 
144
+ - ``large-image-source-zarr``: A tile source using the zarr library that can handle OME-Zarr (OME-NGFF) files as well as some other zarr files. This can also be used for writing N-dimensional tiled images from numpy arrays. Written images can be saved as any supported format.
155
145
 
156
- Developer Installation
157
- ----------------------
146
+ - ``large-image-source-test``: A tile source that generates test tiles, including a simple fractal pattern. Useful for testing extreme zoom levels.
158
147
 
159
- To install all packages from source, clone the repository::
148
+ - ``large-image-source-dummy``: A tile source that does nothing. This is an absolutely minimal implementation of a tile source used for testing. If you want to create a custom tile source, start with this implementation.
160
149
 
161
- git clone https://github.com/girder/large_image.git
162
- cd large_image
163
150
 
164
- Install all packages and dependencies::
151
+ As a `Girder`_ plugin, ``large-image`` adds end points to access all of the image formats it can read both to get metadata and to act as a tile server.
152
+ In the Girder UI, ``large-image`` shows images on item pages, and can show thumbnails in item lists when browsing folders.
153
+ There is also cache management to balance memory use and speed of response in Girder when ``large-image`` is used as a tile server.
165
154
 
166
- pip install -e . -r requirements-dev.txt
155
+ Most tile sources can be used with Girder Large Image. You can specify an extras_require of ``girder`` to install the following packages:
167
156
 
168
- If you aren't developing with Girder 3, you can skip installing those components. Use ``requirements-dev-core.txt`` instead of ``requirements-dev.txt``::
157
+ - ``girder-large-image``: Large Image as a Girder 3.x plugin.
169
158
 
170
- pip install -e . -r requirements-dev-core.txt
159
+ You can install ``large-image[tasks]`` to install a Girder Worker task that can convert otherwise unreadable images to pyramidal tiff files.
171
160
 
161
+ - ``girder-large-image-annotation``: Adds models to the Girder database for supporting annotating large images. These annotations can be rendered on images. Annotations can include polygons, points, image overlays, and other types. Each annotation can have a label and metadata.
172
162
 
173
- Tile source prerequisites
174
- =========================
163
+ - ``large-image-tasks``: A utility for running the converter via Girder Worker.
175
164
 
176
- Many tile sources have complex prerequisites. These can be installed directly using your system's package manager or from some prebuilt Python wheels for Linux. The prebuilt wheels are not official packages, but they can be used by instructing pip to use them by preference::
165
+ You can specify an extras_require of ``girder`` to include modules needed to work with the Girder remote worker or ``worker`` to include modules needed on the remote side of the Girder remote worker. If neither is specified, some conversion tasks can be run using Girder local jobs.
177
166
 
178
- pip install -e . -r requirements-dev.txt --find-links https://girder.github.io/large_image_wheels
179
167
 
180
168
 
181
- .. _Girder: https://github.com/girder/girder
169
+ .. _Girder: https://girder.readthedocs.io/en/latest/
@@ -16,22 +16,28 @@ from large_image.constants import (NEW_IMAGE_PATH_FLAG, TILE_FORMAT_NUMPY,
16
16
  dtypeToGValue)
17
17
  from large_image.exceptions import TileSourceError, TileSourceFileNotFoundError
18
18
  from large_image.tilesource import FileTileSource
19
- from large_image.tilesource.utilities import _imageToNumpy
19
+ from large_image.tilesource.utilities import _imageToNumpy, _newFromFileLock, nearPowerOfTwo
20
20
 
21
21
  logging.getLogger('pyvips').setLevel(logging.ERROR)
22
22
 
23
23
  # Default to ignoring files with no extension and some specific extensions.
24
- config.ConfigValues['source_vips_ignored_names'] = \
25
- r'(^[^.]*|\.(yml|yaml|json|png|svs))$'
24
+ config.ConfigValues.setdefault(
25
+ 'source_vips_ignored_names', r'(^[^.]*|\.(yml|yaml|json|png|svs|mrxs))$')
26
26
 
27
27
 
28
28
  def _clearVipsCache():
29
- old = pyvips.voperation.cache_get_max_files()
30
- pyvips.voperation.cache_set_max_files(0)
31
- pyvips.voperation.cache_set_max_files(old)
32
- old = pyvips.voperation.cache_get_max()
33
- pyvips.voperation.cache_set_max(0)
34
- pyvips.voperation.cache_set_max(old)
29
+ oldfiles = pyvips.cache_get_max_files()
30
+ oldmax = pyvips.cache_get_max()
31
+ oldmem = pyvips.cache_get_max_mem()
32
+ # print('Clearing vips cache: size %r, max files %r, max %r, mem %r' % (
33
+ # pyvips.cache_get_size(), oldfiles, oldmax, oldmem))
34
+ pyvips.cache_set_max_files(0)
35
+ # This shouldn't ever go to zero
36
+ pyvips.cache_set_max(1)
37
+ pyvips.cache_set_max_mem(0)
38
+ pyvips.cache_set_max_files(oldfiles)
39
+ pyvips.cache_set_max(oldmax)
40
+ pyvips.cache_set_max_mem(oldmem)
35
41
 
36
42
 
37
43
  _cacheClearFuncs.append(_clearVipsCache)
@@ -50,10 +56,11 @@ class VipsFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
50
56
  mimeTypes = {
51
57
  None: SourcePriority.FALLBACK,
52
58
  }
59
+ newPriority = SourcePriority.MEDIUM
53
60
 
54
61
  _tileSize = 256
55
62
 
56
- def __init__(self, path, **kwargs):
63
+ def __init__(self, path, **kwargs): # noqa
57
64
  """
58
65
  Initialize the tile class. See the base class for other available
59
66
  parameters.
@@ -61,20 +68,29 @@ class VipsFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
61
68
  :param path: a filesystem path for the tile source.
62
69
  """
63
70
  super().__init__(path, **kwargs)
71
+ self.addKnownExtensions()
64
72
 
73
+ self._suffix = ''
65
74
  if str(path).startswith(NEW_IMAGE_PATH_FLAG):
66
- return self._initNew(**kwargs)
75
+ self._initNew(**kwargs)
76
+ return
67
77
  self._largeImagePath = str(self._getLargeImagePath())
68
78
  self._editable = False
69
79
 
70
- self._ignoreSourceNames('vips', self._largeImagePath)
80
+ config._ignoreSourceNames('vips', self._largeImagePath)
71
81
  try:
72
- self._image = pyvips.Image.new_from_file(self._largeImagePath)
82
+ with _newFromFileLock:
83
+ self._image = pyvips.Image.new_from_file(self._largeImagePath)
73
84
  except pyvips.error.Error:
74
85
  if not os.path.isfile(self._largeImagePath):
75
86
  raise TileSourceFileNotFoundError(self._largeImagePath) from None
76
87
  msg = 'File cannot be opened via pyvips'
77
88
  raise TileSourceError(msg)
89
+ # Ask pdfs to render at a higher resolution
90
+ if ('vips-loader' in self._image.get_fields() and
91
+ self._image.get('vips-loader') in {'pdfload'}):
92
+ self._suffix = ',dpi=144'
93
+ self._image = pyvips.Image.new_from_file(self._largeImagePath + '[dpi=300]')
78
94
  self.sizeX = self._image.width
79
95
  self.sizeY = self._image.height
80
96
  self.tileWidth = self.tileHeight = self._tileSize
@@ -82,15 +98,29 @@ class VipsFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
82
98
  if 'n-pages' in self._image.get_fields():
83
99
  pages = self._image.get('n-pages')
84
100
  self._frames = [0]
101
+ self._lowres = {}
85
102
  for page in range(1, pages):
86
- subInputPath = self._largeImagePath + '[page=%d]' % page
87
- subImage = pyvips.Image.new_from_file(subInputPath)
103
+ subInputPath = self._largeImagePath + f'[page={page}{self._suffix}]'
104
+ with _newFromFileLock:
105
+ try:
106
+ subImage = pyvips.Image.new_from_file(subInputPath)
107
+ except Exception:
108
+ continue
88
109
  if subImage.width == self.sizeX and subImage.height == self.sizeY:
89
110
  self._frames.append(page)
90
111
  continue
112
+ if (self.sizeX - 16 < subImage.width <= self.sizeX and
113
+ self.sizeY - 16 < subImage.height <= self.sizeY):
114
+ self._frames.append(page)
115
+ continue
91
116
  if subImage.width * subImage.height < self.sizeX * self.sizeY:
117
+ if (nearPowerOfTwo(self.sizeX, subImage.width) and
118
+ nearPowerOfTwo(self.sizeY, subImage.height)):
119
+ level = int(round(math.log(self.sizeX / subImage.width) / math.log(2)))
120
+ self._lowres.setdefault(len(self._frames) - 1, {})[level] = page
92
121
  continue
93
122
  self._frames = [page]
123
+ self._lowres = {}
94
124
  self.sizeX = subImage.width
95
125
  self.sizeY = subImage.height
96
126
  try:
@@ -98,12 +128,41 @@ class VipsFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
98
128
  except Exception:
99
129
  pass
100
130
  self._image = subImage
131
+ self._checkLowerLevels()
101
132
  self.levels = int(max(1, math.ceil(math.log(
102
133
  float(max(self.sizeX, self.sizeY)) / self.tileWidth) / math.log(2)) + 1))
103
- if len(self._frames) > 1:
134
+ if len(self._frames) > 1 or self._lowres is not None:
104
135
  self._recentFrames = cachetools.LRUCache(maxsize=6)
105
136
  self._frameLock = threading.RLock()
106
137
 
138
+ def _checkLowerLevels(self):
139
+ if (len(self._lowres) != len(self._frames) or
140
+ min(len(v) for v in self._lowres.values()) !=
141
+ max(len(v) for v in self._lowres.values()) or
142
+ min(len(v) for v in self._lowres.values()) == 0):
143
+ self._lowres = None
144
+ if len(self._frames) == 1 and 'openslide.level-count' in self._image.get_fields():
145
+ self._lowres = [{}]
146
+ for oslevel in range(1, int(self._image.get('openslide.level-count'))):
147
+ with _newFromFileLock:
148
+ try:
149
+ subImage = pyvips.Image.new_from_file(
150
+ self._largeImagePath, level=oslevel)
151
+ except Exception:
152
+ continue
153
+ if subImage.width * subImage.height < self.sizeX * self.sizeY:
154
+ if (nearPowerOfTwo(self.sizeX, subImage.width) and
155
+ nearPowerOfTwo(self.sizeY, subImage.height)):
156
+ level = int(round(math.log(
157
+ self.sizeX / subImage.width) / math.log(2)))
158
+ self._lowres[0][level] = (self._frames[0], oslevel)
159
+ if not len(self._lowres[0]):
160
+ self._lowres = None
161
+ else:
162
+ self._lowres = list(self._lowres.values())
163
+ if self._lowres is not None:
164
+ self._populatedLevels = len(self._lowres[0]) + 1
165
+
107
166
  def _initNew(self, **kwargs):
108
167
  """
109
168
  Initialize the tile class for creating a new image.
@@ -115,6 +174,7 @@ class VipsFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
115
174
  self.sizeX = self.sizeY = self.levels = 0
116
175
  self.tileWidth = self.tileHeight = self._tileSize
117
176
  self._frames = [0]
177
+ self._lowres = None
118
178
  self._cacheValue = str(uuid.uuid4())
119
179
  self._output = None
120
180
  self._editable = True
@@ -170,24 +230,38 @@ class VipsFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
170
230
  self._addMetadataFrameInformation(result)
171
231
  return result
172
232
 
173
- def _getFrameImage(self, frame=0):
233
+ def _getFrameImage(self, frame=0, lowres=None):
174
234
  """
175
235
  Get the vips image associated with a specific frame.
176
236
 
177
237
  :param frame: the 0-based frame to get.
238
+ :param lowres: the lower resolution part of the frame
178
239
  :returns: a vips image.
179
240
  """
180
241
  if self._image is None and self._output:
181
242
  self._outputToImage()
182
243
  img = self._image
183
- if frame > 0:
244
+ if frame > 0 or lowres:
184
245
  with self._frameLock:
185
- if frame not in self._recentFrames:
186
- subpath = self._largeImagePath + '[page=%d]' % self._frames[frame]
187
- img = pyvips.Image.new_from_file(subpath)
188
- self._recentFrames[frame] = img
246
+ key = (frame, lowres)
247
+ frameval = self._frames[frame]
248
+ params = {}
249
+ if lowres is not None:
250
+ if isinstance(self._lowres[frame][lowres], tuple):
251
+ frameval = self._lowres[frame][lowres][0]
252
+ params = {'level': self._lowres[frame][lowres][1]}
253
+ else:
254
+ frameval = self._lowres[frame][lowres]
255
+ if key not in self._recentFrames:
256
+ if frameval:
257
+ subpath = self._largeImagePath + f'[page={frameval}{self._suffix}]'
258
+ else:
259
+ subpath = self._largeImagePath
260
+ with _newFromFileLock:
261
+ img = pyvips.Image.new_from_file(subpath, **params)
262
+ self._recentFrames[key] = img
189
263
  else:
190
- img = self._recentFrames[frame]
264
+ img = self._recentFrames[key]
191
265
  return img
192
266
 
193
267
  def getNativeMagnification(self):
@@ -206,14 +280,35 @@ class VipsFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
206
280
  def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False, **kwargs):
207
281
  frame = self._getFrame(**kwargs)
208
282
  self._xyzInRange(x, y, z, frame, len(self._frames))
209
- img = self._getFrameImage(frame)
210
283
  x0, y0, x1, y1, step = self._xyzToCorners(x, y, z)
211
- tileimg = img.crop(x0, y0, x1 - x0, y1 - y0)
212
- tileimg = tileimg.reduce(step, step, kernel=pyvips.enums.Kernel.NEAREST)
213
- tile = np.ndarray(
214
- buffer=tileimg.write_to_memory(),
215
- dtype=GValueToDtype[tileimg.format],
216
- shape=[tileimg.height, tileimg.width, tileimg.bands])
284
+ lowres = None
285
+ if self._lowres and step > 1:
286
+ use = 0
287
+ for ll in self._lowres[frame].keys():
288
+ if 2 ** ll <= step:
289
+ use = max(use, ll)
290
+ if use:
291
+ lowres = use
292
+ div = 2 ** use
293
+ x0 //= div
294
+ y0 //= div
295
+ x1 //= div
296
+ y1 //= div
297
+ step //= div
298
+ img = self._getFrameImage(frame, lowres)
299
+ tileimg = img.crop(min(x0, img.width), min(y0, img.height),
300
+ min(x1, img.width) - min(x0, img.width),
301
+ min(y1, img.height) - min(y0, img.height))
302
+ try:
303
+ if step != 1:
304
+ tileimg = tileimg.resize(1.0 / step, kernel=pyvips.enums.Kernel.NEAREST, gap=0)
305
+ tile = np.ndarray(
306
+ buffer=tileimg.write_to_memory(),
307
+ dtype=GValueToDtype[tileimg.format],
308
+ shape=[tileimg.height, tileimg.width, tileimg.bands])
309
+ except Exception:
310
+ self.logger.exception('Failed to getTile')
311
+ tile = np.zeros((1, 1))
217
312
  return self._outputTile(tile, TILE_FORMAT_NUMPY, x, y, z,
218
313
  pilImageAllowed, numpyAllowed, **kwargs)
219
314
 
@@ -298,7 +393,8 @@ class VipsFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
298
393
  """
299
394
  if self._output is not None:
300
395
  self._image = None
301
- w, h = self._output['width'], self._output['height']
396
+ w = self._output['width'] - min(0, self._output['minx'])
397
+ h = self._output['height'] - min(0, self._output['miny'])
302
398
  w = max(self.minWidth or w, w)
303
399
  h = max(self.minHeight or h, h)
304
400
  self.sizeX = w
@@ -310,7 +406,10 @@ class VipsFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
310
406
  def addTile(self, tile, x=0, y=0, mask=None, interpretation=None):
311
407
  """
312
408
  Add a numpy or image tile to the image, expanding the image as needed
313
- to accommodate it.
409
+ to accommodate it. Note that x and y can be negative. If so, the
410
+ output image (and internal memory access of the image) will act as if
411
+ the 0, 0 point is the most negative position. Cropping is applied
412
+ after this offset.
314
413
 
315
414
  :param tile: a numpy array, PIL Image, vips image, or a binary string
316
415
  with an image. The numpy array can have 2 or 3 dimensions.
@@ -431,7 +530,9 @@ class VipsFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
431
530
  if img.format == 'float' and entry['image'].format == 'double':
432
531
  entryimage = entryimage.cast(img.format)
433
532
  branch = branch.composite(
434
- entryimage, pyvips.BlendMode.OVER, x=entry['x'], y=entry['y'])
533
+ entryimage, pyvips.BlendMode.OVER,
534
+ x=entry['x'] - min(0, self._output['minx']),
535
+ y=entry['y'] - min(0, self._output['miny']))
435
536
  if not ((idx + 1) % leaves) or idx + 1 == len(self._output['images']):
436
537
  trunk = trunk.composite(branch, pyvips.BlendMode.OVER, x=0, y=0)
437
538
  branch = baseimg.copy()
@@ -593,9 +694,25 @@ class VipsFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
593
694
  if not self._editable:
594
695
  return self._image.format
595
696
  return self._getVipsFormat()
596
-
597
697
  # TODO: specify bit depth / bandFormat explicitly
598
698
 
699
+ @property
700
+ def origin(self):
701
+ if not self._editable:
702
+ return {'x': 0, 'y': 0}
703
+ return {'x': min(0, self._output['minx'] or 0),
704
+ 'y': min(0, self._output['miny'] or 0)}
705
+
706
+ @classmethod
707
+ def addKnownExtensions(cls):
708
+ if not hasattr(cls, '_addedExtensions'):
709
+ cls._addedExtensions = True
710
+ cls.extensions = cls.extensions.copy()
711
+ for dotext in pyvips.base.get_suffixes():
712
+ ext = dotext.lstrip('.')
713
+ if ext not in cls.extensions:
714
+ cls.extensions[ext] = SourcePriority.IMPLICIT
715
+
599
716
 
600
717
  def open(*args, **kwargs):
601
718
  """Create an instance of the module class."""
@@ -0,0 +1,42 @@
1
+ Metadata-Version: 2.4
2
+ Name: large-image-source-vips
3
+ Version: 1.33.4.dev37
4
+ Summary: A libvips tilesource for large_image.
5
+ Home-page: https://github.com/girder/large_image
6
+ Author: Kitware, Inc.
7
+ Author-email: kitware@kitware.com
8
+ License: Apache-2.0
9
+ Keywords: large_image,tile source
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/x-rst
19
+ License-File: LICENSE
20
+ Requires-Dist: large-image>=1.33.4.dev37
21
+ Requires-Dist: numpy
22
+ Requires-Dist: packaging
23
+ Requires-Dist: pyvips
24
+ Provides-Extra: girder
25
+ Requires-Dist: girder-large-image; extra == "girder"
26
+ Dynamic: author
27
+ Dynamic: author-email
28
+ Dynamic: classifier
29
+ Dynamic: description
30
+ Dynamic: description-content-type
31
+ Dynamic: home-page
32
+ Dynamic: keywords
33
+ Dynamic: license
34
+ Dynamic: license-file
35
+ Dynamic: provides-extra
36
+ Dynamic: requires-dist
37
+ Dynamic: requires-python
38
+ Dynamic: summary
39
+
40
+ A libvips tilesource for large_image.
41
+
42
+ See the large-image package for more details.
@@ -1,5 +1,6 @@
1
1
  LICENSE
2
2
  README.rst
3
+ pyproject.toml
3
4
  setup.py
4
5
  large_image_source_vips/__init__.py
5
6
  large_image_source_vips/girder_source.py
@@ -0,0 +1,7 @@
1
+ large-image>=1.33.4.dev37
2
+ numpy
3
+ packaging
4
+ pyvips
5
+
6
+ [girder]
7
+ girder-large-image
@@ -0,0 +1,7 @@
1
+ [build-system]
2
+ requires = ["setuptools", "setuptools-scm"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [tool.setuptools_scm]
6
+ fallback_version = "0.0.0"
7
+ root = "../.."
@@ -6,58 +6,37 @@ description = 'A libvips tilesource for large_image.'
6
6
  long_description = description + '\n\nSee the large-image package for more details.'
7
7
 
8
8
 
9
- def prerelease_local_scheme(version):
10
- """
11
- Return local scheme version unless building on master in CircleCI.
12
-
13
- This function returns the local scheme version number
14
- (e.g. 0.0.0.dev<N>+g<HASH>) unless building on CircleCI for a
15
- pre-release in which case it ignores the hash and produces a
16
- PEP440 compliant pre-release version number (e.g. 0.0.0.dev<N>).
17
- """
18
- from setuptools_scm.version import get_local_node_and_date
19
-
20
- if os.getenv('CIRCLE_BRANCH') in ('master', ):
21
- return ''
22
- else:
23
- return get_local_node_and_date(version)
24
-
25
-
26
9
  try:
27
10
  from setuptools_scm import get_version
28
11
 
29
- version = get_version(root='../..', local_scheme=prerelease_local_scheme)
30
- limit_version = f'>={version}' if '+' not in version else ''
12
+ version = get_version(root='../..')
13
+ limit_version = f'>={version}' if '+' not in version and not os.getenv('TOX_ENV_NAME') else ''
31
14
  except (ImportError, LookupError):
32
15
  limit_version = ''
33
16
 
34
17
  setup(
35
18
  name='large-image-source-vips',
36
- use_scm_version={'root': '../..', 'local_scheme': prerelease_local_scheme,
37
- 'fallback_version': '0.0.0'},
38
- setup_requires=['setuptools-scm'],
39
19
  description=description,
40
20
  long_description=long_description,
41
- license='Apache Software License 2.0',
21
+ long_description_content_type='text/x-rst',
22
+ license='Apache-2.0',
42
23
  author='Kitware, Inc.',
43
24
  author_email='kitware@kitware.com',
44
25
  classifiers=[
45
26
  'Development Status :: 5 - Production/Stable',
46
- 'License :: OSI Approved :: Apache Software License',
47
27
  'Programming Language :: Python :: 3',
48
- 'Programming Language :: Python :: 3.6',
49
- 'Programming Language :: Python :: 3.7',
50
- 'Programming Language :: Python :: 3.8',
51
28
  'Programming Language :: Python :: 3.9',
52
29
  'Programming Language :: Python :: 3.10',
53
30
  'Programming Language :: Python :: 3.11',
31
+ 'Programming Language :: Python :: 3.12',
32
+ 'Programming Language :: Python :: 3.13',
54
33
  ],
34
+ python_requires='>=3.9',
55
35
  install_requires=[
56
36
  f'large-image{limit_version}',
57
37
  'numpy',
58
38
  'packaging',
59
39
  'pyvips',
60
- 'importlib-metadata<5 ; python_version < "3.8"',
61
40
  ],
62
41
  extras_require={
63
42
  'girder': 'girder-large-image',
@@ -65,7 +44,6 @@ setup(
65
44
  keywords='large_image, tile source',
66
45
  packages=find_packages(exclude=['test', 'test.*']),
67
46
  url='https://github.com/girder/large_image',
68
- python_requires='>=3.6',
69
47
  entry_points={
70
48
  'large_image.source': [
71
49
  'vips = large_image_source_vips:VipsFileTileSource',
@@ -1,25 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: large-image-source-vips
3
- Version: 1.23.7.dev4
4
- Summary: A libvips tilesource for large_image.
5
- Home-page: https://github.com/girder/large_image
6
- Author: Kitware, Inc.
7
- Author-email: kitware@kitware.com
8
- License: Apache Software License 2.0
9
- Keywords: large_image,tile source
10
- Classifier: Development Status :: 5 - Production/Stable
11
- Classifier: License :: OSI Approved :: Apache Software License
12
- Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.6
14
- Classifier: Programming Language :: Python :: 3.7
15
- Classifier: Programming Language :: Python :: 3.8
16
- Classifier: Programming Language :: Python :: 3.9
17
- Classifier: Programming Language :: Python :: 3.10
18
- Classifier: Programming Language :: Python :: 3.11
19
- Requires-Python: >=3.6
20
- Provides-Extra: girder
21
- License-File: LICENSE
22
-
23
- A libvips tilesource for large_image.
24
-
25
- See the large-image package for more details.
@@ -1,25 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: large-image-source-vips
3
- Version: 1.23.7.dev4
4
- Summary: A libvips tilesource for large_image.
5
- Home-page: https://github.com/girder/large_image
6
- Author: Kitware, Inc.
7
- Author-email: kitware@kitware.com
8
- License: Apache Software License 2.0
9
- Keywords: large_image,tile source
10
- Classifier: Development Status :: 5 - Production/Stable
11
- Classifier: License :: OSI Approved :: Apache Software License
12
- Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.6
14
- Classifier: Programming Language :: Python :: 3.7
15
- Classifier: Programming Language :: Python :: 3.8
16
- Classifier: Programming Language :: Python :: 3.9
17
- Classifier: Programming Language :: Python :: 3.10
18
- Classifier: Programming Language :: Python :: 3.11
19
- Requires-Python: >=3.6
20
- Provides-Extra: girder
21
- License-File: LICENSE
22
-
23
- A libvips tilesource for large_image.
24
-
25
- See the large-image package for more details.
@@ -1,10 +0,0 @@
1
- large-image>=1.23.7.dev4
2
- numpy
3
- packaging
4
- pyvips
5
-
6
- [:python_version < "3.8"]
7
- importlib-metadata<5
8
-
9
- [girder]
10
- girder-large-image