large-image-source-multi 1.27.5.dev6__py3-none-any.whl → 1.30.7.dev10__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.
@@ -10,7 +10,6 @@ from importlib.metadata import PackageNotFoundError
10
10
  from importlib.metadata import version as _importlib_version
11
11
  from pathlib import Path
12
12
 
13
- import jsonschema
14
13
  import numpy as np
15
14
  import yaml
16
15
 
@@ -27,6 +26,26 @@ except PackageNotFoundError:
27
26
  # package is not installed
28
27
  pass
29
28
 
29
+ jsonschema = None
30
+ _validator = None
31
+
32
+
33
+ def _lazyImport():
34
+ """
35
+ Import the jsonschema module. This is done when needed rather than in the
36
+ module initialization because it is slow.
37
+ """
38
+ global jsonschema, _validator
39
+
40
+ if jsonschema is None:
41
+ try:
42
+ import jsonschema
43
+
44
+ _validator = jsonschema.Draft6Validator(MultiSourceSchema)
45
+ except ImportError:
46
+ msg = 'jsonschema module not found.'
47
+ raise TileSourceError(msg)
48
+
30
49
 
31
50
  SourceEntrySchema = {
32
51
  'type': 'object',
@@ -269,10 +288,24 @@ SourceEntrySchema = {
269
288
  'type': 'array',
270
289
  'items': {'type': 'integer'},
271
290
  },
272
- 'style': {'type': 'object'},
273
- 'params': {
291
+ 'sampleScale': {
274
292
  'description':
275
- 'Additional parameters to pass to the base tile source',
293
+ 'Each pixel sample values is divided by this scale after any '
294
+ 'sampleOffset has been applied',
295
+ 'type': 'number',
296
+ },
297
+ 'sampleOffset': {
298
+ 'description':
299
+ 'This is added to each pixel sample value before any '
300
+ 'sampleScale is applied',
301
+ 'type': 'number',
302
+ },
303
+ 'style': {
304
+ 'description': 'A style specification to pass to the base tile source',
305
+ 'type': 'object',
306
+ },
307
+ 'params': {
308
+ 'description': 'Additional parameters to pass to the base tile source',
276
309
  'type': 'object',
277
310
  },
278
311
  },
@@ -333,6 +366,10 @@ MultiSourceSchema = {
333
366
  'layout and size, assume all sources are so similar',
334
367
  'type': 'boolean',
335
368
  },
369
+ 'dtype': {
370
+ 'description': 'If present, a numpy dtype name to use for the data.',
371
+ 'type': 'string',
372
+ },
336
373
  'singleBand': {
337
374
  'description':
338
375
  'If true, output only the first band of compositied results',
@@ -376,6 +413,7 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
376
413
  None: SourcePriority.FALLBACK,
377
414
  'application/json': SourcePriority.PREFERRED,
378
415
  'application/yaml': SourcePriority.PREFERRED,
416
+ 'text/yaml': SourcePriority.PREFERRED,
379
417
  }
380
418
 
381
419
  _minTileSize = 64
@@ -383,9 +421,7 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
383
421
  _defaultTileSize = 256
384
422
  _maxOpenHandles = 6
385
423
 
386
- _validator = jsonschema.Draft6Validator(MultiSourceSchema)
387
-
388
- def __init__(self, path, **kwargs):
424
+ def __init__(self, path, **kwargs): # noqa
389
425
  """
390
426
  Initialize the tile class. See the base class for other available
391
427
  parameters.
@@ -394,11 +430,22 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
394
430
  """
395
431
  super().__init__(path, **kwargs)
396
432
 
433
+ _lazyImport()
434
+ self._validator = _validator
397
435
  self._largeImagePath = self._getLargeImagePath()
398
436
  self._lastOpenSourceLock = threading.RLock()
399
437
  # 'c' must be first as channels are special because they can have names
400
438
  self._axesList = ['c', 'z', 't', 'xy']
401
- if not os.path.isfile(self._largeImagePath):
439
+ if isinstance(path, dict) and 'sources' in path:
440
+ self._info = path.copy()
441
+ self._basePath = '.'
442
+ self._largeImagePath = '.'
443
+ try:
444
+ self._validator.validate(self._info)
445
+ except jsonschema.ValidationError:
446
+ msg = 'File cannot be validated via multi-source reader.'
447
+ raise TileSourceError(msg)
448
+ elif not os.path.isfile(self._largeImagePath):
402
449
  try:
403
450
  possibleYaml = self._largeImagePath.split('multi://', 1)[-1]
404
451
  self._info = yaml.safe_load(possibleYaml)
@@ -410,7 +457,8 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
410
457
  try:
411
458
  with builtins.open(self._largeImagePath) as fptr:
412
459
  start = fptr.read(1024).strip()
413
- if start[:1] not in ('{', '#', '-') and (start[:1] < 'a' or start[:1] > 'z'):
460
+ if (start[:1] not in ('{', '#', '-') and
461
+ (start[:1] < 'a' or start[:1] > 'z')) or 'FeatureCollection' in start:
414
462
  msg = 'File cannot be opened via multi-source reader.'
415
463
  raise TileSourceError(msg)
416
464
  fptr.seek(0)
@@ -661,9 +709,15 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
661
709
  aKey = tuple(self._axisKey(source, frame.get(f'Index{axis.upper()}') or 0, axis)
662
710
  for axis in self._axesList)
663
711
  channel = channels[cIdx] if cIdx < len(channels) else None
712
+ # We add the channel name to our channel list if the individual
713
+ # source lists the channel name or a set of channels OR the source
714
+ # appends channels to an existing set.
664
715
  if channel and channel not in self._channels and (
665
- 'channel' in source or 'channels' in source):
716
+ 'channel' in source or 'channels' in source or
717
+ len(self._channels) == aKey[0]):
666
718
  self._channels.append(channel)
719
+ # Adjust the channel number if the source named the channel; do not
720
+ # do so in other cases
667
721
  if (channel and channel in self._channels and
668
722
  'c' not in source and 'cValues' not in source):
669
723
  aKey = tuple([self._channels.index(channel)] + list(aKey[1:]))
@@ -759,7 +813,11 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
759
813
  self.tileWidth = self.tileWidth or ts.tileWidth
760
814
  self.tileHeight = self.tileHeight or ts.tileHeight
761
815
  if not hasattr(self, '_firstdtype'):
762
- self._firstdtype = ts.dtype
816
+ self._firstdtype = (
817
+ ts.dtype if not self._info.get('dtype') else
818
+ np.dtype(self._info['dtype']))
819
+ if self._info.get('dtype'):
820
+ self._dtype = np.dtype(self._info['dtype'])
763
821
  if not numChecked:
764
822
  tsMag = ts.getNativeMagnification()
765
823
  for key in self._nativeMagnification:
@@ -773,7 +831,7 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
773
831
  self._bands = {}
774
832
  self._bands.update(tsMeta['bands'])
775
833
  lastSource = source
776
- self._bandcount = 1 if self._info.get('singleBand') else (
834
+ self._bandCount = 1 if self._info.get('singleBand') else (
777
835
  len(self._bands) if hasattr(self, '_bands') else (bandCount or None))
778
836
  bbox = self._sourceBoundingBox(source, tsMeta['sizeX'], tsMeta['sizeY'])
779
837
  computedWidth = max(computedWidth, int(math.ceil(bbox['right'])))
@@ -828,7 +886,8 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
828
886
  with self._lastOpenSourceLock:
829
887
  if (hasattr(self, '_lastOpenSource') and
830
888
  self._lastOpenSource['source'] == source and
831
- self._lastOpenSource['params'] == params):
889
+ (self._lastOpenSource['params'] == params or (
890
+ params == {} and self._lastOpenSource['params'] is None))):
832
891
  return self._lastOpenSource['ts']
833
892
  if not len(large_image.tilesource.AvailableTileSources):
834
893
  large_image.tilesource.loadTileSources()
@@ -841,6 +900,22 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
841
900
  if params is None:
842
901
  params = source.get('params', {})
843
902
  ts = openFunc(source['path'], **params)
903
+ if (self._dtype and np.dtype(ts.dtype).kind == 'f' and
904
+ (self._dtype == 'check' or np.dtype(self._dtype).kind != 'f') and
905
+ 'sampleScale' not in source and 'sampleOffset' not in source):
906
+ minval = maxval = 0
907
+ for f in range(ts.frames):
908
+ ftile = ts.getTile(x=0, y=0, z=0, frame=f, numpyAllowed='always')
909
+ minval = min(minval, np.amin(ftile))
910
+ maxval = max(maxval, np.amax(ftile))
911
+ if minval >= 0 and maxval <= 1:
912
+ source['sampleScale'] = None
913
+ elif minval >= 0:
914
+ source['sampleScale'] = 2 ** math.ceil(math.log2(maxval))
915
+ else:
916
+ source['sampleScale'] = 2 ** math.ceil(math.log2(max(-minval, maxval)) + 1)
917
+ source['sampleOffset'] = source['sampleScale'] / 2
918
+ source['sourceName'] = ts.name
844
919
  with self._lastOpenSourceLock:
845
920
  self._lastOpenSource = {
846
921
  'source': source,
@@ -860,7 +935,7 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
860
935
  None if the associated image doesn't exist.
861
936
  """
862
937
  if imageKey not in self._associatedImages:
863
- return
938
+ return None
864
939
  source = self._sources[self._associatedImages[imageKey]['sourcenum']]
865
940
  ts = self._openSource(source)
866
941
  return ts.getAssociatedImage(self._associatedImages[imageKey]['key'], *args, **kwargs)
@@ -894,7 +969,7 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
894
969
  """
895
970
  Return additional known metadata about the tile source. Data returned
896
971
  from this method is not guaranteed to be in any particular format or
897
- have specific values.
972
+ have specific values. Also, only the first 100 sources are used.
898
973
 
899
974
  :returns: a dictionary of data or None.
900
975
  """
@@ -903,7 +978,7 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
903
978
  'sources': copy.deepcopy(self._sources),
904
979
  'sourceFiles': [],
905
980
  }
906
- for path in self._sourcePaths.values():
981
+ for path in list(self._sourcePaths.values())[:100]:
907
982
  source = self._sources[min(path['sourcenum'])]
908
983
  ts = self._openSource(source)
909
984
  result['sourceFiles'].append({
@@ -937,38 +1012,39 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
937
1012
  (tile.shape[0] + y - base.shape[0], base.shape[1], base.shape[2]),
938
1013
  dtype=base.dtype)
939
1014
  if base.shape[2] in {2, 4}:
940
- vfill[:, :, -1] = fullAlphaValue(self.dtype)
1015
+ vfill[:, :, -1] = fullAlphaValue(base.dtype)
941
1016
  base = np.vstack((base, vfill))
942
1017
  if base.shape[1] < tile.shape[1] + x:
943
1018
  hfill = np.zeros(
944
1019
  (base.shape[0], tile.shape[1] + x - base.shape[1], base.shape[2]),
945
1020
  dtype=base.dtype)
946
1021
  if base.shape[2] in {2, 4}:
947
- hfill[:, :, -1] = fullAlphaValue(self.dtype)
1022
+ hfill[:, :, -1] = fullAlphaValue(base.dtype)
948
1023
  base = np.hstack((base, hfill))
949
1024
  if base.flags.writeable is False:
950
1025
  base = base.copy()
951
1026
  if base.shape[2] in {2, 4}:
952
1027
  baseA = base[y:y + tile.shape[0], x:x + tile.shape[1], -1].astype(
953
- float) / fullAlphaValue(self.dtype)
954
- tileA = tile[:, :, -1].astype(float) / fullAlphaValue(self.dtype)
1028
+ float) / fullAlphaValue(base.dtype)
1029
+ tileA = tile[:, :, -1].astype(float) / fullAlphaValue(tile.dtype)
955
1030
  outA = tileA + baseA * (1 - tileA)
956
1031
  base[y:y + tile.shape[0], x:x + tile.shape[1], :-1] = (
957
- np.where(tileA[..., np.newaxis], tile[:, :, :-1], 0) +
1032
+ np.where(tileA[..., np.newaxis], tile[:, :, :-1] * tileA[..., np.newaxis], 0) +
958
1033
  base[y:y + tile.shape[0], x:x + tile.shape[1], :-1] * baseA[..., np.newaxis] *
959
1034
  (1 - tileA[..., np.newaxis])
960
1035
  ) / np.where(outA[..., np.newaxis], outA[..., np.newaxis], 1)
961
- base[y:y + tile.shape[0], x:x + tile.shape[1], -1] = outA * fullAlphaValue(self.dtype)
1036
+ base[y:y + tile.shape[0], x:x + tile.shape[1], -1] = outA * fullAlphaValue(base.dtype)
962
1037
  else:
963
1038
  base[y:y + tile.shape[0], x:x + tile.shape[1], :] = tile
964
1039
  return base
965
1040
 
966
- def _getTransformedTile(self, ts, transform, corners, scale, frame, crop=None):
1041
+ def _getTransformedTile(self, ts, transform, corners, scale, frame,
1042
+ crop=None, firstMerge=False):
967
1043
  """
968
1044
  Determine where the target tile's corners are located on the source.
969
1045
  Fetch that so that we have at least sqrt(2) more resolution, then use
970
- scikit-image warp to transform it. scikit-image does a better job than
971
- scipy.ndimage.affine_transform.
1046
+ scikit-image warp to transform it. scikit-image does a better and
1047
+ faster job than scipy.ndimage.affine_transform.
972
1048
 
973
1049
  :param ts: the source of the image to transform.
974
1050
  :param transform: a 3x3 affine 2d matrix for transforming the source
@@ -980,6 +1056,9 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
980
1056
  :param crop: an optional dictionary to crop the source image in full
981
1057
  resolution, untransformed coordinates. This may contain left, top,
982
1058
  right, and bottom values in pixels.
1059
+ :param firstMerge: if False and using an alpha channel, transform
1060
+ with nearest neighbor rather than a higher order function to
1061
+ avoid transparency effects.
983
1062
  :returns: a numpy array tile or None, x, y coordinates within the
984
1063
  target tile for the placement of the numpy tile array.
985
1064
  """
@@ -1010,7 +1089,7 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
1010
1089
  # we only need every 1/srcscale pixel.
1011
1090
  srcscale = int(2 ** math.log2(max(1, srcscale)))
1012
1091
  # Pad to reduce edge effects at tile boundaries
1013
- border = int(math.ceil(2 * srcscale))
1092
+ border = int(math.ceil(4 * srcscale))
1014
1093
  region = {
1015
1094
  'left': int(max(0, minx - border) // srcscale) * srcscale,
1016
1095
  'top': int(max(0, miny - border) // srcscale) * srcscale,
@@ -1057,28 +1136,38 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
1057
1136
  x, y = int(math.floor(x)), int(math.floor(y))
1058
1137
  # Recompute where the source corners will land
1059
1138
  destcorners = (np.dot(transform, regioncorners.T).T).tolist()
1060
- destsize = (max(math.ceil(c[0]) for c in destcorners),
1061
- max(math.ceil(c[1]) for c in destcorners))
1062
- # Add an alpha band if needed
1139
+ destShape = [
1140
+ max(max(math.ceil(c[1]) for c in destcorners), srcImage.shape[0]),
1141
+ max(max(math.ceil(c[0]) for c in destcorners), srcImage.shape[1]),
1142
+ ]
1143
+ if max(0, -x) or max(0, -y):
1144
+ transform[0][2] -= max(0, -x)
1145
+ transform[1][2] -= max(0, -y)
1146
+ destShape[0] -= max(0, -y)
1147
+ destShape[1] -= max(0, -x)
1148
+ x += max(0, -x)
1149
+ y += max(0, -y)
1150
+ destShape = [min(destShape[0], outh - y), min(destShape[1], outw - x)]
1151
+ if destShape[0] <= 0 or destShape[1] <= 0:
1152
+ return None, None, None
1153
+ # Add an alpha band if needed. This has to be done before the
1154
+ # transform if it isn't the first tile, since the unused transformed
1155
+ # areas need to have a zero alpha value
1063
1156
  if srcImage.shape[2] in {1, 3}:
1064
1157
  _, srcImage = _makeSameChannelDepth(np.zeros((1, 1, srcImage.shape[2] + 1)), srcImage)
1065
- # Add enough space to warp the source image in place
1066
- srcImage = np.pad(srcImage, (
1067
- (0, max(0, destsize[1] - srcImage.shape[0])),
1068
- (0, max(0, destsize[0] - srcImage.shape[1])),
1069
- (0, 0)), mode='constant')
1158
+ useNearest = srcImage.shape[2] in {2, 4} and not firstMerge
1159
+ # skimage.transform.warp is faster and has less artifacts than
1160
+ # scipy.ndimage.affine_transform. It is faster than using cupy's
1161
+ # version of scipy's affine_transform when the source and destination
1162
+ # images are converted from numpy to cupy and back in this method.
1070
1163
  destImage = skimage.transform.warp(
1164
+ # Although using np.float32 could reduce memory use, it doesn't
1165
+ # provide any speed improvement
1071
1166
  srcImage.astype(float),
1072
- skimage.transform.AffineTransform(np.linalg.inv(
1073
- transform))).astype(srcImage.dtype)
1074
- # Trim to target size and location
1075
- destImage = destImage[max(0, -y):, max(0, -x):, :]
1076
- x += max(0, -x)
1077
- y += max(0, -y)
1078
- if x >= outw or y >= outh:
1079
- return None, None, None
1080
- destImage = destImage[:min(destImage.shape[0], outh - y),
1081
- :min(destImage.shape[1], outw - x), :]
1167
+ skimage.transform.AffineTransform(np.linalg.inv(transform)),
1168
+ order=0 if useNearest else 3,
1169
+ output_shape=(destShape[0], destShape[1], srcImage.shape[2]),
1170
+ ).astype(srcImage.dtype)
1082
1171
  return destImage, x, y
1083
1172
 
1084
1173
  def _addSourceToTile(self, tile, sourceEntry, corners, scale):
@@ -1098,12 +1187,12 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
1098
1187
  :returns: a numpy array of the tile.
1099
1188
  """
1100
1189
  source = self._sources[sourceEntry['sourcenum']]
1101
- ts = self._openSource(source, sourceEntry['kwargs'])
1102
1190
  # If tile is outside of bounding box, skip it
1103
1191
  bbox = source['bbox']
1104
1192
  if (corners[2][0] <= bbox['left'] or corners[0][0] >= bbox['right'] or
1105
1193
  corners[2][1] <= bbox['top'] or corners[0][1] >= bbox['bottom']):
1106
1194
  return tile
1195
+ ts = self._openSource(source, sourceEntry['kwargs'])
1107
1196
  transform = bbox.get('transform')
1108
1197
  x = y = 0
1109
1198
  # If there is no transform or the diagonals are positive and there is
@@ -1153,8 +1242,23 @@ class MultiFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
1153
1242
  else:
1154
1243
  sourceTile, x, y = self._getTransformedTile(
1155
1244
  ts, transform, corners, scale, sourceEntry.get('frame', 0),
1156
- source.get('position', {}).get('crop'))
1245
+ source.get('position', {}).get('crop'),
1246
+ firstMerge=tile is None)
1157
1247
  if sourceTile is not None and all(dim > 0 for dim in sourceTile.shape):
1248
+ targetDtype = np.dtype(self._info.get('dtype', ts.dtype))
1249
+ changeDtype = sourceTile.dtype != targetDtype
1250
+ if source.get('sampleScale') or source.get('sampleOffset'):
1251
+ sourceTile = sourceTile.astype(float)
1252
+ if source.get('sampleOffset'):
1253
+ sourceTile[:, :, :-1] += source['sampleOffset']
1254
+ if source.get('sampleScale') and source.get('sampleScale') != 1:
1255
+ sourceTile[:, :, :-1] /= source['sampleScale']
1256
+ if sourceTile.dtype != targetDtype:
1257
+ if changeDtype:
1258
+ sourceTile = (
1259
+ sourceTile.astype(float) * fullAlphaValue(targetDtype) /
1260
+ fullAlphaValue(sourceTile))
1261
+ sourceTile = sourceTile.astype(targetDtype)
1158
1262
  tile = self._mergeTiles(tile, sourceTile, x, y)
1159
1263
  return tile
1160
1264
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: large-image-source-multi
3
- Version: 1.27.5.dev6
3
+ Version: 1.30.7.dev10
4
4
  Summary: A tilesource for large_image to composite other tile sources
5
5
  Home-page: https://github.com/girder/large_image
6
6
  Author: Kitware, Inc.
@@ -15,15 +15,29 @@ Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
- Requires-Python: >=3.6
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Requires-Python: >=3.8
20
+ Description-Content-Type: text/x-rst
19
21
  License-File: LICENSE
20
22
  Requires-Dist: jsonschema
21
- Requires-Dist: large-image >=1.27.5.dev6
23
+ Requires-Dist: large-image>=1.30.7.dev10
22
24
  Requires-Dist: pyyaml
23
25
  Provides-Extra: all
24
- Requires-Dist: scikit-image ; extra == 'all'
26
+ Requires-Dist: scikit-image; extra == "all"
25
27
  Provides-Extra: girder
26
- Requires-Dist: girder-large-image >=1.27.5.dev6 ; extra == 'girder'
28
+ Requires-Dist: girder-large-image>=1.30.7.dev10; extra == "girder"
29
+ Dynamic: author
30
+ Dynamic: author-email
31
+ Dynamic: classifier
32
+ Dynamic: description
33
+ Dynamic: description-content-type
34
+ Dynamic: home-page
35
+ Dynamic: keywords
36
+ Dynamic: license
37
+ Dynamic: provides-extra
38
+ Dynamic: requires-dist
39
+ Dynamic: requires-python
40
+ Dynamic: summary
27
41
 
28
42
  A tilesource for large_image to composite other tile sources
29
43
 
@@ -0,0 +1,8 @@
1
+ large_image_source_multi/__init__.py,sha256=rUPaj9H32i9v89rCPsjmIfCIEi_N-ZOE9jTiRMrllDc,59131
2
+ large_image_source_multi/girder_source.py,sha256=YOp9hFCv-fzEhOIGAPdRewmyHM1jNu6WLr5Vt6cFtIA,1356
3
+ large_image_source_multi-1.30.7.dev10.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
4
+ large_image_source_multi-1.30.7.dev10.dist-info/METADATA,sha256=ovk964Jj7KL1Fyqg1kACs_qKr1W0DGP8sOgJ_lx-ZHM,1482
5
+ large_image_source_multi-1.30.7.dev10.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
6
+ large_image_source_multi-1.30.7.dev10.dist-info/entry_points.txt,sha256=1ufcPRbofaLVrGNESx1R9QhdUovr4jTzte185wHqpcU,172
7
+ large_image_source_multi-1.30.7.dev10.dist-info/top_level.txt,sha256=v6W6Hkx77lY-DGQ8XqRgbdxYSJRLFv90dCrIJe15d2A,25
8
+ large_image_source_multi-1.30.7.dev10.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,8 +0,0 @@
1
- large_image_source_multi/__init__.py,sha256=E4xscgnBb1l0EsUQtaj5LoWpiiBhMqjm5MStHneiaio,54069
2
- large_image_source_multi/girder_source.py,sha256=YOp9hFCv-fzEhOIGAPdRewmyHM1jNu6WLr5Vt6cFtIA,1356
3
- large_image_source_multi-1.27.5.dev6.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
4
- large_image_source_multi-1.27.5.dev6.dist-info/METADATA,sha256=cB51hajqTzKi4NdqeSwiOfJawQsmi3YjNpIp1TamqP8,1139
5
- large_image_source_multi-1.27.5.dev6.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
6
- large_image_source_multi-1.27.5.dev6.dist-info/entry_points.txt,sha256=1ufcPRbofaLVrGNESx1R9QhdUovr4jTzte185wHqpcU,172
7
- large_image_source_multi-1.27.5.dev6.dist-info/top_level.txt,sha256=v6W6Hkx77lY-DGQ8XqRgbdxYSJRLFv90dCrIJe15d2A,25
8
- large_image_source_multi-1.27.5.dev6.dist-info/RECORD,,