large-image-source-zarr 1.31.0__py3-none-any.whl → 1.33.6a188__py3-none-any.whl
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.
- large_image_source_zarr/__init__.py +250 -57
- {large_image_source_zarr-1.31.0.dist-info → large_image_source_zarr-1.33.6a188.dist-info}/METADATA +10 -10
- large_image_source_zarr-1.33.6a188.dist-info/RECORD +8 -0
- {large_image_source_zarr-1.31.0.dist-info → large_image_source_zarr-1.33.6a188.dist-info}/WHEEL +1 -1
- large_image_source_zarr-1.31.0.dist-info/RECORD +0 -8
- {large_image_source_zarr-1.31.0.dist-info → large_image_source_zarr-1.33.6a188.dist-info}/entry_points.txt +0 -0
- {large_image_source_zarr-1.31.0.dist-info → large_image_source_zarr-1.33.6a188.dist-info/licenses}/LICENSE +0 -0
- {large_image_source_zarr-1.31.0.dist-info → large_image_source_zarr-1.33.6a188.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import importlib.metadata
|
|
3
|
+
import json
|
|
1
4
|
import math
|
|
2
5
|
import multiprocessing
|
|
3
6
|
import os
|
|
@@ -6,8 +9,6 @@ import tempfile
|
|
|
6
9
|
import threading
|
|
7
10
|
import uuid
|
|
8
11
|
import warnings
|
|
9
|
-
from importlib.metadata import PackageNotFoundError
|
|
10
|
-
from importlib.metadata import version as _importlib_version
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
|
|
13
14
|
import numpy as np
|
|
@@ -21,11 +22,8 @@ from large_image.tilesource import FileTileSource
|
|
|
21
22
|
from large_image.tilesource.resample import ResampleMethod, downsampleTileHalfRes
|
|
22
23
|
from large_image.tilesource.utilities import _imageToNumpy, nearPowerOfTwo
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
__version__ =
|
|
26
|
-
except PackageNotFoundError:
|
|
27
|
-
# package is not installed
|
|
28
|
-
pass
|
|
25
|
+
with contextlib.suppress(importlib.metadata.PackageNotFoundError):
|
|
26
|
+
__version__ = importlib.metadata.version(__name__)
|
|
29
27
|
|
|
30
28
|
|
|
31
29
|
zarr = None
|
|
@@ -92,6 +90,10 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
92
90
|
self._initNew(path, **kwargs)
|
|
93
91
|
else:
|
|
94
92
|
self._initOpen(**kwargs)
|
|
93
|
+
internal = self.getInternalMetadata().get('zarr', {}).get('base', {})
|
|
94
|
+
multiscale = internal.get('multiscales', [None])[0]
|
|
95
|
+
if multiscale is not None:
|
|
96
|
+
self._imageDescription = multiscale.get('metadata', {}).get('description')
|
|
95
97
|
self._tileLock = threading.RLock()
|
|
96
98
|
|
|
97
99
|
def _initOpen(self, **kwargs):
|
|
@@ -110,10 +112,8 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
110
112
|
self._zarr = zarr.open(self._largeImagePath, mode='r')
|
|
111
113
|
except Exception:
|
|
112
114
|
if os.path.basename(self._largeImagePath) in {'.zgroup', '.zattrs', '.zarray'}:
|
|
113
|
-
|
|
115
|
+
with contextlib.suppress(Exception):
|
|
114
116
|
self._zarr = zarr.open(os.path.dirname(self._largeImagePath), mode='r')
|
|
115
|
-
except Exception:
|
|
116
|
-
pass
|
|
117
117
|
if self._zarr is None:
|
|
118
118
|
if not os.path.isfile(self._largeImagePath):
|
|
119
119
|
raise TileSourceFileNotFoundError(self._largeImagePath) from None
|
|
@@ -148,6 +148,8 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
148
148
|
self._threadLock = threading.RLock()
|
|
149
149
|
self._processLock = multiprocessing.Lock()
|
|
150
150
|
self._framecount = 0
|
|
151
|
+
self._minWidth = None
|
|
152
|
+
self._minHeight = None
|
|
151
153
|
self._mm_x = 0
|
|
152
154
|
self._mm_y = 0
|
|
153
155
|
self._channelNames = []
|
|
@@ -158,18 +160,16 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
158
160
|
self._frameValues = None
|
|
159
161
|
self._frameAxes = None
|
|
160
162
|
self._frameUnits = None
|
|
163
|
+
self._projection = None
|
|
164
|
+
self._gcps = None
|
|
161
165
|
if not self._created:
|
|
162
|
-
|
|
166
|
+
with contextlib.suppress(Exception):
|
|
163
167
|
self._validateZarr()
|
|
164
|
-
except Exception:
|
|
165
|
-
pass
|
|
166
168
|
|
|
167
169
|
def __del__(self):
|
|
168
170
|
if not hasattr(self, '_derivedSource'):
|
|
169
|
-
|
|
171
|
+
with contextlib.suppress(Exception):
|
|
170
172
|
self._zarr.close()
|
|
171
|
-
except Exception:
|
|
172
|
-
pass
|
|
173
173
|
try:
|
|
174
174
|
if self._created:
|
|
175
175
|
shutil.rmtree(self._tempdir)
|
|
@@ -281,7 +281,7 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
281
281
|
elif check == results['best']:
|
|
282
282
|
results['series'].append((group, arr))
|
|
283
283
|
if not any(group is g for g, _ in results['associated']):
|
|
284
|
-
axes = {k: v for k, v in axes.items() if arr.shape[axes[k]] > 1}
|
|
284
|
+
axes = {k: v for k, v in axes.items() if arr.shape[axes[k]] > 1 or k in {'x', 'y'}}
|
|
285
285
|
if (len(axes) <= 3 and
|
|
286
286
|
self._minAssociatedImageSize <= arr.shape[axes['x']] <=
|
|
287
287
|
self._maxAssociatedImageSize and
|
|
@@ -371,7 +371,7 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
371
371
|
self._frameValues = None
|
|
372
372
|
frame_values_shape = [baseArray.shape[self._axes[a]] for a in self.frameAxes]
|
|
373
373
|
frame_values_shape.append(len(frame_values_shape))
|
|
374
|
-
frame_values = np.
|
|
374
|
+
frame_values = np.zeros(frame_values_shape, dtype=object)
|
|
375
375
|
all_frame_specs = self.getMetadata().get('frames')
|
|
376
376
|
for axis, values in axes_values.items():
|
|
377
377
|
if axis in self.frameAxes:
|
|
@@ -453,10 +453,16 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
453
453
|
stride = 1
|
|
454
454
|
self._strides = {}
|
|
455
455
|
self._axisCounts = {}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
)
|
|
456
|
+
# If we aren't in editable mode, prefer the channel axis to have a
|
|
457
|
+
# stride of 1, then the z axis, then the t axis, then sorted by the
|
|
458
|
+
# axis name.
|
|
459
|
+
axisOrder = ((-'tzc'.index(k) if k in 'tzc' else 1, k)
|
|
460
|
+
for k in self._axes if k not in {'x', 'y', 's'})
|
|
461
|
+
# In editable mode, prefer the order that the axes are being written.
|
|
462
|
+
if self._editable:
|
|
463
|
+
axisOrder = ((-self._axes.get(k, 'tzc'.index(k) if k in 'tzc' else -1), k)
|
|
464
|
+
for k in self._axes if k not in {'x', 'y', 's'})
|
|
465
|
+
for _, k in sorted(axisOrder):
|
|
460
466
|
self._strides[k] = stride
|
|
461
467
|
self._axisCounts[k] = baseArray.shape[self._axes[k]]
|
|
462
468
|
stride *= baseArray.shape[self._axes[k]]
|
|
@@ -545,10 +551,8 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
545
551
|
result['zarr'] = {
|
|
546
552
|
'base': self._zarr.attrs.asdict(),
|
|
547
553
|
}
|
|
548
|
-
|
|
554
|
+
with contextlib.suppress(Exception):
|
|
549
555
|
result['zarr']['main'] = self._series[0][0].attrs.asdict()
|
|
550
|
-
except Exception:
|
|
551
|
-
pass
|
|
552
556
|
return result
|
|
553
557
|
|
|
554
558
|
def getAssociatedImagesList(self):
|
|
@@ -629,7 +633,7 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
629
633
|
|
|
630
634
|
def _validateNewTile(self, tile, mask, placement, axes):
|
|
631
635
|
if not isinstance(tile, np.ndarray) or axes is None:
|
|
632
|
-
axes = 'yxs'
|
|
636
|
+
axes = self._axes if hasattr(self, '_axes') else 'yxs'
|
|
633
637
|
tile, mode = _imageToNumpy(tile)
|
|
634
638
|
elif not isinstance(axes, str) and not isinstance(axes, list):
|
|
635
639
|
err = 'Invalid type for axes. Must be str or list[str].'
|
|
@@ -637,6 +641,7 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
637
641
|
axes = [x.lower() for x in axes]
|
|
638
642
|
if axes[-1] != 's':
|
|
639
643
|
axes.append('s')
|
|
644
|
+
tile = tile[..., np.newaxis]
|
|
640
645
|
if mask is not None and len(axes) - 1 == len(mask.shape):
|
|
641
646
|
mask = mask[:, :, np.newaxis]
|
|
642
647
|
if 'x' not in axes or 'y' not in axes:
|
|
@@ -689,7 +694,9 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
689
694
|
arr,
|
|
690
695
|
[(0, s - arr.shape[i]) for i, s in enumerate(new_shape)],
|
|
691
696
|
)
|
|
692
|
-
new_arr = zarr.
|
|
697
|
+
new_arr = zarr.zeros(
|
|
698
|
+
new_shape, chunks=chunking, dtype=arr.dtype,
|
|
699
|
+
write_empty_chunks=False)
|
|
693
700
|
new_arr[:] = arr[:]
|
|
694
701
|
arr = new_arr
|
|
695
702
|
else:
|
|
@@ -716,11 +723,9 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
716
723
|
``level`` is a reserved word and not permitted for an axis name.
|
|
717
724
|
"""
|
|
718
725
|
self._checkEditable()
|
|
719
|
-
|
|
726
|
+
with contextlib.suppress(TileSourceError):
|
|
720
727
|
# read any info written by other processes
|
|
721
728
|
self._validateZarr()
|
|
722
|
-
except TileSourceError:
|
|
723
|
-
pass
|
|
724
729
|
updateMetadata = False
|
|
725
730
|
store_path = str(kwargs.pop('level', 0))
|
|
726
731
|
placement = {
|
|
@@ -765,12 +770,22 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
765
770
|
self._zarr_store.rmdir(path)
|
|
766
771
|
chunking = None
|
|
767
772
|
if store_path not in current_arrays:
|
|
768
|
-
arr = np.empty(tuple(new_dims.values()), dtype=tile.dtype)
|
|
769
773
|
chunking = tuple([
|
|
770
774
|
self._tileSize if a in ['x', 'y'] else
|
|
771
775
|
new_dims.get('s') if a == 's' else 1
|
|
772
776
|
for a in axes
|
|
773
777
|
])
|
|
778
|
+
# If we have to create the array, do so with the desired store
|
|
779
|
+
# and chunking so we don't have to immediately rechunk it
|
|
780
|
+
arr = zarr.zeros(
|
|
781
|
+
tuple(new_dims.values()),
|
|
782
|
+
dtype=tile.dtype,
|
|
783
|
+
chunks=chunking,
|
|
784
|
+
store=self._zarr_store,
|
|
785
|
+
path=store_path,
|
|
786
|
+
write_empty_chunks=False,
|
|
787
|
+
)
|
|
788
|
+
chunking = None
|
|
774
789
|
else:
|
|
775
790
|
arr = current_arrays[store_path]
|
|
776
791
|
new_shape = tuple(
|
|
@@ -885,31 +900,33 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
885
900
|
with self._threadLock and self._processLock:
|
|
886
901
|
name = str(self._tempdir.name).split('/')[-1]
|
|
887
902
|
arrays = dict(self._zarr.arrays())
|
|
888
|
-
channel_axis = self._axes.get('c', self._axes.get('s'))
|
|
889
903
|
datasets = []
|
|
890
904
|
axes = []
|
|
891
905
|
channels = []
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
'
|
|
901
|
-
'
|
|
902
|
-
|
|
903
|
-
'
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
906
|
+
channel_axis = rdefs = None
|
|
907
|
+
if hasattr(self, '_axes'):
|
|
908
|
+
channel_axis = self._axes.get('c', self._axes.get('s'))
|
|
909
|
+
rdefs = {'model': 'color' if len(self._channelColors) else 'greyscale'}
|
|
910
|
+
sorted_axes = [a[0] for a in sorted(self._axes.items(), key=lambda item: item[1])]
|
|
911
|
+
for arr_name in arrays:
|
|
912
|
+
level = int(arr_name)
|
|
913
|
+
scale = [1.0 for a in sorted_axes]
|
|
914
|
+
scale[self._axes.get('x')] = (self._mm_x or 0) * (2 ** level)
|
|
915
|
+
scale[self._axes.get('y')] = (self._mm_y or 0) * (2 ** level)
|
|
916
|
+
dataset_metadata = {
|
|
917
|
+
'path': arr_name,
|
|
918
|
+
'coordinateTransformations': [{
|
|
919
|
+
'type': 'scale',
|
|
920
|
+
'scale': scale,
|
|
921
|
+
}],
|
|
922
|
+
}
|
|
923
|
+
datasets.append(dataset_metadata)
|
|
924
|
+
for a in sorted_axes:
|
|
925
|
+
if a == 't':
|
|
926
|
+
rdefs['defaultT'] = 0
|
|
927
|
+
elif a == 'z':
|
|
928
|
+
rdefs['defaultZ'] = 0
|
|
929
|
+
axes.append(self._getAxisInternalMetadata(a))
|
|
913
930
|
if channel_axis is not None and len(arrays) > 0:
|
|
914
931
|
base_array = list(arrays.values())[0]
|
|
915
932
|
base_shape = base_array.shape
|
|
@@ -991,6 +1008,32 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
991
1008
|
raise TileSourceError(msg)
|
|
992
1009
|
self._crop = (x, y, w, h)
|
|
993
1010
|
|
|
1011
|
+
@property
|
|
1012
|
+
def minWidth(self):
|
|
1013
|
+
return self._minWidth
|
|
1014
|
+
|
|
1015
|
+
@minWidth.setter
|
|
1016
|
+
def minWidth(self, value):
|
|
1017
|
+
self._checkEditable()
|
|
1018
|
+
value = int(value) if value is not None else None
|
|
1019
|
+
if value is not None and value <= 0:
|
|
1020
|
+
msg = 'minWidth must be positive or None'
|
|
1021
|
+
raise TileSourceError(msg)
|
|
1022
|
+
self._minWidth = value
|
|
1023
|
+
|
|
1024
|
+
@property
|
|
1025
|
+
def minHeight(self):
|
|
1026
|
+
return self._minHeight
|
|
1027
|
+
|
|
1028
|
+
@minHeight.setter
|
|
1029
|
+
def minHeight(self, value):
|
|
1030
|
+
self._checkEditable()
|
|
1031
|
+
value = int(value) if value is not None else None
|
|
1032
|
+
if value is not None and value <= 0:
|
|
1033
|
+
msg = 'minHeight must be positive or None'
|
|
1034
|
+
raise TileSourceError(msg)
|
|
1035
|
+
self._minHeight = value
|
|
1036
|
+
|
|
994
1037
|
@property
|
|
995
1038
|
def mm_x(self):
|
|
996
1039
|
return self._mm_x
|
|
@@ -1019,12 +1062,54 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
1019
1062
|
|
|
1020
1063
|
@property
|
|
1021
1064
|
def imageDescription(self):
|
|
1065
|
+
if not hasattr(self, '_imageDescription'):
|
|
1066
|
+
return None
|
|
1067
|
+
if isinstance(self._imageDescription, dict):
|
|
1068
|
+
return self._imageDescription.get('description')
|
|
1022
1069
|
return self._imageDescription
|
|
1023
1070
|
|
|
1024
1071
|
@imageDescription.setter
|
|
1025
1072
|
def imageDescription(self, description):
|
|
1026
1073
|
self._checkEditable()
|
|
1027
|
-
|
|
1074
|
+
try:
|
|
1075
|
+
json.dumps(description)
|
|
1076
|
+
except TypeError:
|
|
1077
|
+
msg = 'Description must be JSON serializable'
|
|
1078
|
+
raise TileSourceError(msg)
|
|
1079
|
+
if (
|
|
1080
|
+
hasattr(self, '_imageDescription') and
|
|
1081
|
+
isinstance(self._imageDescription, dict)
|
|
1082
|
+
):
|
|
1083
|
+
self._imageDescription['description'] = description
|
|
1084
|
+
else:
|
|
1085
|
+
self._imageDescription = description
|
|
1086
|
+
|
|
1087
|
+
@property
|
|
1088
|
+
def additionalMetadata(self):
|
|
1089
|
+
if not hasattr(self, '_imageDescription'):
|
|
1090
|
+
return None
|
|
1091
|
+
if isinstance(self._imageDescription, dict):
|
|
1092
|
+
return self._imageDescription.get('additionalMetadata')
|
|
1093
|
+
return None
|
|
1094
|
+
|
|
1095
|
+
@additionalMetadata.setter
|
|
1096
|
+
def additionalMetadata(self, data):
|
|
1097
|
+
self._checkEditable()
|
|
1098
|
+
try:
|
|
1099
|
+
json.dumps(data)
|
|
1100
|
+
except TypeError:
|
|
1101
|
+
msg = 'Metadata must be JSON serializable'
|
|
1102
|
+
raise TileSourceError(msg)
|
|
1103
|
+
if (
|
|
1104
|
+
hasattr(self, '_imageDescription') and
|
|
1105
|
+
isinstance(self._imageDescription, dict)
|
|
1106
|
+
):
|
|
1107
|
+
self._imageDescription['additionalMetadata'] = data
|
|
1108
|
+
else:
|
|
1109
|
+
self.imageDescription = dict(
|
|
1110
|
+
description=self._imageDescription,
|
|
1111
|
+
additionalMetadata=data,
|
|
1112
|
+
)
|
|
1028
1113
|
|
|
1029
1114
|
@property
|
|
1030
1115
|
def channelNames(self):
|
|
@@ -1067,7 +1152,7 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
1067
1152
|
err = 'frameAxes must be set first with a list of frame axis names.'
|
|
1068
1153
|
raise ValueError(err)
|
|
1069
1154
|
if not isinstance(units, dict) or not all(
|
|
1070
|
-
k in self.frameAxes for k in units
|
|
1155
|
+
k in self.frameAxes for k in units
|
|
1071
1156
|
):
|
|
1072
1157
|
err = 'frameUnits must be a dictionary with keys that exist in frameAxes.'
|
|
1073
1158
|
raise ValueError(err)
|
|
@@ -1089,6 +1174,76 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
1089
1174
|
self._frameValues = a
|
|
1090
1175
|
self._writeInternalMetadata()
|
|
1091
1176
|
|
|
1177
|
+
@property
|
|
1178
|
+
def projection(self):
|
|
1179
|
+
return self._projection
|
|
1180
|
+
|
|
1181
|
+
@projection.setter
|
|
1182
|
+
def projection(self, proj):
|
|
1183
|
+
import pyproj
|
|
1184
|
+
|
|
1185
|
+
self._checkEditable()
|
|
1186
|
+
try:
|
|
1187
|
+
pyproj.CRS.from_string(str(proj))
|
|
1188
|
+
except Exception as e:
|
|
1189
|
+
msg = f'Invalid projection value. Cannot be interpreted by pyproj: {str(e)}'
|
|
1190
|
+
raise TileSourceError(msg)
|
|
1191
|
+
self._projection = proj
|
|
1192
|
+
self._writeInternalMetadata()
|
|
1193
|
+
|
|
1194
|
+
@property
|
|
1195
|
+
def gcps(self):
|
|
1196
|
+
return self._gcps
|
|
1197
|
+
|
|
1198
|
+
@gcps.setter
|
|
1199
|
+
def gcps(self, gcps):
|
|
1200
|
+
self._checkEditable()
|
|
1201
|
+
if isinstance(gcps, str):
|
|
1202
|
+
gcps = list(zip(*[iter(gcps.split())] * 4, strict=False))
|
|
1203
|
+
if (isinstance(gcps, (list, tuple))):
|
|
1204
|
+
gcps = [
|
|
1205
|
+
[float(v) for v in gcp.split()]
|
|
1206
|
+
if isinstance(gcp, str) else gcp
|
|
1207
|
+
for gcp in gcps
|
|
1208
|
+
]
|
|
1209
|
+
if any(
|
|
1210
|
+
not isinstance(gcp, list) and not isinstance(gcp, tuple)
|
|
1211
|
+
for gcp in gcps
|
|
1212
|
+
):
|
|
1213
|
+
msg = 'Each GCP must be specified as a list, tuple, or space-separated string.'
|
|
1214
|
+
raise TileSourceError(msg)
|
|
1215
|
+
if any(len(gcp) != 4 for gcp in gcps):
|
|
1216
|
+
msg = (
|
|
1217
|
+
'Each GCP must contain four values: '
|
|
1218
|
+
'[projected_x, projected_y, pixel_x, pixel_y].'
|
|
1219
|
+
)
|
|
1220
|
+
raise TileSourceError(msg)
|
|
1221
|
+
unique_gcps = []
|
|
1222
|
+
dup_gcps = []
|
|
1223
|
+
for gcp in gcps:
|
|
1224
|
+
if not any(
|
|
1225
|
+
u[0:2] == gcp[0:2] or u[2:4] == gcp[2:4]
|
|
1226
|
+
for u in unique_gcps
|
|
1227
|
+
):
|
|
1228
|
+
unique_gcps.append(gcp)
|
|
1229
|
+
else:
|
|
1230
|
+
dup_gcps.append(gcp)
|
|
1231
|
+
gcps = unique_gcps
|
|
1232
|
+
if len(dup_gcps):
|
|
1233
|
+
msg = (
|
|
1234
|
+
f'Removed duplicate GCPs {dup_gcps} from list; shared projected '
|
|
1235
|
+
'coordinate or pixel coordinate with another GCP.'
|
|
1236
|
+
)
|
|
1237
|
+
warnings.warn(msg, stacklevel=2)
|
|
1238
|
+
if len(gcps) < 2:
|
|
1239
|
+
msg = 'Must specify at least 2 unique GCPs.'
|
|
1240
|
+
raise TileSourceError(msg)
|
|
1241
|
+
else:
|
|
1242
|
+
msg = 'GCPs must be specified as a list, tuple, or space-separated string.'
|
|
1243
|
+
raise TileSourceError(msg)
|
|
1244
|
+
self._gcps = gcps
|
|
1245
|
+
self._writeInternalMetadata()
|
|
1246
|
+
|
|
1092
1247
|
def _generateDownsampledLevels(self, resample_method):
|
|
1093
1248
|
self._checkEditable()
|
|
1094
1249
|
current_arrays = dict(self._zarr.arrays())
|
|
@@ -1132,7 +1287,10 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
1132
1287
|
output=iterator_output,
|
|
1133
1288
|
resample=False, # TODO: incorporate resampling in core
|
|
1134
1289
|
):
|
|
1135
|
-
|
|
1290
|
+
tile_data = tile['tile']
|
|
1291
|
+
if tile_data.shape[-1] == 1:
|
|
1292
|
+
tile_data = np.squeeze(tile['tile'], axis=(-1,))
|
|
1293
|
+
new_tile = downsampleTileHalfRes(tile_data, resample_method)
|
|
1136
1294
|
overlap = {k: int(v / 2) for k, v in tile['tile_overlap'].items()}
|
|
1137
1295
|
new_tile = new_tile[
|
|
1138
1296
|
slice(overlap['top'], new_tile.shape[0] - overlap['bottom']),
|
|
@@ -1152,6 +1310,23 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
1152
1310
|
)
|
|
1153
1311
|
self._validateZarr() # refresh self._levels before continuing
|
|
1154
1312
|
|
|
1313
|
+
def _applyGeoReferencing(self, path):
|
|
1314
|
+
if self.projection and not self.gcps:
|
|
1315
|
+
msg = (
|
|
1316
|
+
'Projection was specified but GCPs were not specified. '
|
|
1317
|
+
'Cannot write georeferenced file.'
|
|
1318
|
+
)
|
|
1319
|
+
raise TileSourceError(msg)
|
|
1320
|
+
import tifftools
|
|
1321
|
+
|
|
1322
|
+
setlist = []
|
|
1323
|
+
if self.projection is not None:
|
|
1324
|
+
setlist.append(('projection', self.projection))
|
|
1325
|
+
if self.gcps is not None and len(self.gcps):
|
|
1326
|
+
setlist.append(('gcps', self.gcps))
|
|
1327
|
+
|
|
1328
|
+
tifftools.tiff_set(path, setlist=setlist, overwrite=True)
|
|
1329
|
+
|
|
1155
1330
|
def write(
|
|
1156
1331
|
self,
|
|
1157
1332
|
path,
|
|
@@ -1210,6 +1385,21 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
1210
1385
|
**frame_position,
|
|
1211
1386
|
)
|
|
1212
1387
|
|
|
1388
|
+
if self.minWidth or self.minHeight:
|
|
1389
|
+
old_axes = self._axes if hasattr(self, '_axes') else {}
|
|
1390
|
+
current_arrays = dict(self._zarr.arrays())
|
|
1391
|
+
arr = current_arrays['0']
|
|
1392
|
+
new_shape = tuple(
|
|
1393
|
+
max(
|
|
1394
|
+
v,
|
|
1395
|
+
self.minWidth if self.minWidth is not None and k == 'x' else
|
|
1396
|
+
self.minHeight if self.minHeight is not None and k == 'y' else
|
|
1397
|
+
arr.shape[old_axes[k]],
|
|
1398
|
+
)
|
|
1399
|
+
for k, v in old_axes.items()
|
|
1400
|
+
)
|
|
1401
|
+
self._resizeImage(arr, new_shape, {}, None)
|
|
1402
|
+
|
|
1213
1403
|
source._validateZarr()
|
|
1214
1404
|
|
|
1215
1405
|
if suffix in ['.zarr', '.db', '.sqlite', '.zip']:
|
|
@@ -1240,8 +1430,11 @@ class ZarrFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
|
|
|
1240
1430
|
params = {}
|
|
1241
1431
|
if lossy and self.dtype == np.uint8:
|
|
1242
1432
|
params['compression'] = 'jpeg'
|
|
1433
|
+
params['overwrite'] = overwriteAllowed
|
|
1243
1434
|
params.update(converterParams)
|
|
1244
|
-
convert(str(attrs_path), path,
|
|
1435
|
+
convert(str(attrs_path), path, **params)
|
|
1436
|
+
|
|
1437
|
+
self._applyGeoReferencing(path)
|
|
1245
1438
|
|
|
1246
1439
|
|
|
1247
1440
|
def open(*args, **kwargs):
|
{large_image_source_zarr-1.31.0.dist-info → large_image_source_zarr-1.33.6a188.dist-info}/METADATA
RENAMED
|
@@ -1,30 +1,29 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: large-image-source-zarr
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.33.6a188
|
|
4
4
|
Summary: A OME Zarr tilesource for large_image.
|
|
5
5
|
Home-page: https://github.com/girder/large_image
|
|
6
6
|
Author: Kitware, Inc.
|
|
7
7
|
Author-email: kitware@kitware.com
|
|
8
|
-
License: Apache
|
|
8
|
+
License: Apache-2.0
|
|
9
9
|
Keywords: large_image,tile source
|
|
10
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
12
11
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
15
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
16
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
17
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
-
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Requires-Python: >=3.10
|
|
20
18
|
Description-Content-Type: text/x-rst
|
|
21
19
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: large-image>=1.
|
|
20
|
+
Requires-Dist: large-image>=1.33.6.a188
|
|
23
21
|
Requires-Dist: zarr<3
|
|
24
|
-
Requires-Dist:
|
|
22
|
+
Requires-Dist: imagecodecs
|
|
23
|
+
Requires-Dist: numcodecs<0.16
|
|
25
24
|
Requires-Dist: imagecodecs-numcodecs!=2024.9.22
|
|
26
25
|
Provides-Extra: girder
|
|
27
|
-
Requires-Dist: girder-large-image>=1.
|
|
26
|
+
Requires-Dist: girder-large-image>=1.33.6.a188; extra == "girder"
|
|
28
27
|
Provides-Extra: all
|
|
29
28
|
Requires-Dist: large-image-converter; extra == "all"
|
|
30
29
|
Dynamic: author
|
|
@@ -35,6 +34,7 @@ Dynamic: description-content-type
|
|
|
35
34
|
Dynamic: home-page
|
|
36
35
|
Dynamic: keywords
|
|
37
36
|
Dynamic: license
|
|
37
|
+
Dynamic: license-file
|
|
38
38
|
Dynamic: provides-extra
|
|
39
39
|
Dynamic: requires-dist
|
|
40
40
|
Dynamic: requires-python
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
large_image_source_zarr/__init__.py,sha256=BJq47bX_ZR0M0laIJWlI_7RbmUIZgyHwq-A7zDzitH0,58921
|
|
2
|
+
large_image_source_zarr/girder_source.py,sha256=RljxQ49-2PyTPZ4xxTipyyaXiwLAC7npdwWVx7XEhLE,342
|
|
3
|
+
large_image_source_zarr-1.33.6a188.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
|
4
|
+
large_image_source_zarr-1.33.6a188.dist-info/METADATA,sha256=TKAqpuCo2XwIikJU-vyJWl7TcQGJ9npiLMhSMBCuXcA,1415
|
|
5
|
+
large_image_source_zarr-1.33.6a188.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
+
large_image_source_zarr-1.33.6a188.dist-info/entry_points.txt,sha256=VakBZBTOHxWMsYsOO8Egale1mPhLTlLruMfhSBUOwkk,166
|
|
7
|
+
large_image_source_zarr-1.33.6a188.dist-info/top_level.txt,sha256=cMYOehfHJ55_mkRAFS3h2lsO0Pe4jYvMrNKbEznImZI,24
|
|
8
|
+
large_image_source_zarr-1.33.6a188.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
large_image_source_zarr/__init__.py,sha256=GiPnjdIVWz7yK-JWtkUmaKFanXeiakI8DvGmF78pmaY,51369
|
|
2
|
-
large_image_source_zarr/girder_source.py,sha256=RljxQ49-2PyTPZ4xxTipyyaXiwLAC7npdwWVx7XEhLE,342
|
|
3
|
-
large_image_source_zarr-1.31.0.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
|
4
|
-
large_image_source_zarr-1.31.0.dist-info/METADATA,sha256=2v5TQfjswYs4uCpxV8gfXtybyjGGjKy3eKLH1s0N_LY,1475
|
|
5
|
-
large_image_source_zarr-1.31.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
6
|
-
large_image_source_zarr-1.31.0.dist-info/entry_points.txt,sha256=VakBZBTOHxWMsYsOO8Egale1mPhLTlLruMfhSBUOwkk,166
|
|
7
|
-
large_image_source_zarr-1.31.0.dist-info/top_level.txt,sha256=cMYOehfHJ55_mkRAFS3h2lsO0Pe4jYvMrNKbEznImZI,24
|
|
8
|
-
large_image_source_zarr-1.31.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|