large-image-source-tiff 1.30.5.dev8__py3-none-any.whl → 1.30.6__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.
@@ -141,7 +141,7 @@ class TiffFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
141
141
  continue
142
142
  # If a layer is a multiple of the tile size, the number of tiles
143
143
  # should be a power of two rounded up from the primary.
144
- if (not (td.imageWidth % td.tileWidth) and not (td.imageHeight % td.tileHeight)):
144
+ if not (td.imageWidth % td.tileWidth) and not (td.imageHeight % td.tileHeight):
145
145
  htw = highest.imageWidth // td.tileWidth
146
146
  hth = highest.imageHeight // td.tileHeight
147
147
  ttw = td.imageWidth // td.tileWidth
@@ -327,12 +327,19 @@ class TiffFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
327
327
  self._info = info
328
328
  frames = []
329
329
  associated = [] # for now, a list of directories
330
+ used_subifd = False
330
331
  for idx, ifd in enumerate(info['ifds']):
331
332
  # if not tiles, add to associated images
332
333
  if tifftools.Tag.tileWidth.value not in ifd['tags']:
333
- associated.append(idx)
334
+ associated.append((idx, False))
334
335
  continue
335
- level = self._levelFromIfd(ifd, info['ifds'][0])
336
+ try:
337
+ level = self._levelFromIfd(ifd, info['ifds'][0])
338
+ except TileSourceError:
339
+ if idx and used_subifd:
340
+ associated.append((idx, True))
341
+ continue
342
+ raise
336
343
  # if the same resolution as the main image, add a frame
337
344
  if level == self.levels - 1:
338
345
  frames.append({'dirs': [None] * self.levels})
@@ -371,9 +378,13 @@ class TiffFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
371
378
  tifftools.Tag.TileOffsets.value not in subifds[0]['tags']):
372
379
  msg = 'Subifd has no strip or tile offsets.'
373
380
  raise TileSourceMalformedError(msg)
374
- level = self._levelFromIfd(subifds[0], info['ifds'][0])
381
+ try:
382
+ level = self._levelFromIfd(subifds[0], info['ifds'][0])
383
+ except Exception:
384
+ break
375
385
  if level < self.levels - 1 and frames[-1]['dirs'][level] is None:
376
386
  frames[-1]['dirs'][level] = (idx, subidx + 1)
387
+ used_subifd = True
377
388
  else:
378
389
  msg = 'Tile layers are in a surprising order'
379
390
  raise TileSourceError(msg)
@@ -407,8 +418,8 @@ class TiffFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
407
418
  self._iccprofiles.append(ifd['tags'][
408
419
  tifftools.Tag.ICCProfile.value]['data'])
409
420
  self._associatedImages = {}
410
- for dirNum in associated:
411
- self._addAssociatedImage(dirNum)
421
+ for dirNum, isTiled in associated:
422
+ self._addAssociatedImage(dirNum, isTiled)
412
423
  self._frames = frames
413
424
  self._tiffDirectories = [
414
425
  self.getTiffDir(
@@ -490,7 +501,7 @@ class TiffFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
490
501
  frame.setdefault('frame', {})
491
502
  frame['frame']['IndexC'] = idx
492
503
 
493
- def _addAssociatedImage(self, directoryNum, mustBeTiled=False, topImage=None):
504
+ def _addAssociatedImage(self, directoryNum, mustBeTiled=False, topImage=None, imageId=None):
494
505
  """
495
506
  Check if the specified TIFF directory contains an image with a sensible
496
507
  image description that can be used as an ID. If so, and if the image
@@ -501,6 +512,7 @@ class TiffFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
501
512
  untiled images.
502
513
  :param topImage: if specified, add image-embedded metadata to this
503
514
  image.
515
+ :param imageId: if specified, use this as the image name.
504
516
  """
505
517
  try:
506
518
  associated = self.getTiffDir(directoryNum, mustBeTiled)
@@ -514,6 +526,8 @@ class TiffFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
514
526
  id = 'dir%d' % directoryNum
515
527
  if not len(self._associatedImages):
516
528
  id = 'macro'
529
+ if imageId:
530
+ id = imageId
517
531
  if not id and not mustBeTiled:
518
532
  id = {1: 'label', 9: 'macro'}.get(associated._tiffInfo.get('subfiletype'))
519
533
  if not isinstance(id, str):
@@ -525,7 +539,7 @@ class TiffFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
525
539
  associated._pixelInfo['width'] <= self._maxAssociatedImageSize and
526
540
  associated._pixelInfo['height'] <= self._maxAssociatedImageSize and
527
541
  id not in self._associatedImages):
528
- image = associated._tiffFile.read_image()
542
+ image = associated.read_image()
529
543
  # Optrascan scanners store xml image descriptions in a "tiled
530
544
  # image". Check if this is the case, and, if so, parse such
531
545
  # data
@@ -681,8 +695,7 @@ class TiffFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
681
695
  except Exception:
682
696
  if sparseFallback:
683
697
  raise IOTiffError('Missing z level %d' % z)
684
- else:
685
- raise
698
+ raise
686
699
  else:
687
700
  tile = dir.getTile(x, y, asarray=numpyAllowed == 'always')
688
701
  format = 'JPEG'
@@ -765,7 +778,7 @@ class TiffFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
765
778
  """
766
779
  imageList = set(self._associatedImages)
767
780
  for td in self._tiffDirectories:
768
- if td is not None:
781
+ if td is not None and td is not False:
769
782
  imageList |= set(td._embeddedImages)
770
783
  return sorted(imageList)
771
784
 
@@ -780,11 +793,11 @@ class TiffFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
780
793
  # _associatedImages. There are some sample files where libtiff's
781
794
  # read_image fails to read the _associatedImage properly because of
782
795
  # separated jpeg information. For the samples we currently have,
783
- # preferring the _embeddedImages is sufficient, but if find other files
784
- # with seemingly bad associated images, we may need to read them with a
785
- # more complex process than read_image.
796
+ # preferring the _embeddedImages is sufficient, but if we find other
797
+ # files with seemingly bad associated images, we may need to read them
798
+ # with a more complex process than read_image.
786
799
  for td in self._tiffDirectories:
787
- if td is not None and imageKey in td._embeddedImages:
800
+ if td is not None and td is not False and imageKey in td._embeddedImages:
788
801
  return PIL.Image.open(io.BytesIO(base64.b64decode(td._embeddedImages[imageKey])))
789
802
  if imageKey in self._associatedImages:
790
803
  return PIL.Image.fromarray(self._associatedImages[imageKey])
@@ -447,12 +447,11 @@ class TiledTiffDirectory:
447
447
 
448
448
  if tileByteCountsLibtiffType == libtiff_ctypes.TIFFDataType.TIFF_LONG8:
449
449
  return ctypes.c_uint64
450
- elif tileByteCountsLibtiffType == \
450
+ if tileByteCountsLibtiffType == \
451
451
  libtiff_ctypes.TIFFDataType.TIFF_SHORT:
452
452
  return ctypes.c_uint16
453
- else:
454
- raise IOTiffError(
455
- 'Invalid type for TIFFTAG_TILEBYTECOUNTS: %s' % tileByteCountsLibtiffType)
453
+ raise IOTiffError(
454
+ 'Invalid type for TIFFTAG_TILEBYTECOUNTS: %s' % tileByteCountsLibtiffType)
456
455
 
457
456
  def _getJpegFrameSize(self, tileNum):
458
457
  """
@@ -529,10 +528,10 @@ class TiledTiffDirectory:
529
528
  if bytesRead == -1:
530
529
  msg = 'Failed to read raw tile'
531
530
  raise IOTiffError(msg)
532
- elif bytesRead < rawTileSize:
531
+ if bytesRead < rawTileSize:
533
532
  msg = 'Buffer underflow when reading tile'
534
533
  raise IOTiffError(msg)
535
- elif bytesRead > rawTileSize:
534
+ if bytesRead > rawTileSize:
536
535
  # It's unlikely that this will ever occur, but incomplete reads will
537
536
  # be checked for by looking for the JPEG end marker
538
537
  msg = 'Buffer overflow when reading tile'
@@ -788,11 +787,13 @@ class TiledTiffDirectory:
788
787
 
789
788
  if (not self._tiffInfo.get('istiled') or
790
789
  self._tiffInfo.get('compression') not in {
791
- libtiff_ctypes.COMPRESSION_JPEG, 33003, 33005, 34712} or
790
+ libtiff_ctypes.COMPRESSION_JPEG, 33003, 33004, 33005, 34712} or
792
791
  self._tiffInfo.get('bitspersample') != 8 or
793
792
  self._tiffInfo.get('sampleformat') not in {
794
793
  None, libtiff_ctypes.SAMPLEFORMAT_UINT} or
795
- (asarray and self._tiffInfo.get('compression') not in {33003, 33005, 34712} and (
794
+ (asarray and self._tiffInfo.get('compression') not in {
795
+ 33003, 33004, 33005, 34712,
796
+ } and (
796
797
  self._tiffInfo.get('compression') != libtiff_ctypes.COMPRESSION_JPEG or
797
798
  self._tiffInfo.get('photometric') != libtiff_ctypes.PHOTOMETRIC_YCBCR))):
798
799
  return self._getUncompressedTile(tileNum)
@@ -811,7 +812,7 @@ class TiledTiffDirectory:
811
812
  # Get the whole frame, which is in a JPEG or JPEG 2000 format
812
813
  frame = self._getJpegFrame(tileNum, True)
813
814
  # For JP2K, see if we can convert it faster than PIL
814
- if self._tiffInfo.get('compression') in {33003, 33005}:
815
+ if self._tiffInfo.get('compression') in {33003, 33004, 33005, 34712}:
815
816
  try:
816
817
  import openjpeg
817
818
 
@@ -836,7 +837,7 @@ class TiledTiffDirectory:
836
837
  self._embeddedImages = {}
837
838
 
838
839
  if not meta:
839
- return
840
+ return None
840
841
  if not isinstance(meta, str):
841
842
  meta = meta.decode(errors='ignore')
842
843
  try:
@@ -858,7 +859,7 @@ class TiledTiffDirectory:
858
859
  meta.split('|MPP = ', 1)[1].split('|')[0].strip()) * 0.001
859
860
  except Exception:
860
861
  pass
861
- return
862
+ return None
862
863
  try:
863
864
  image = xml.find(
864
865
  ".//DataObject[@ObjectType='DPScannedImage']")
@@ -894,3 +895,26 @@ class TiledTiffDirectory:
894
895
  except Exception:
895
896
  pass
896
897
  return True
898
+
899
+ def read_image(self):
900
+ """
901
+ Use the underlying _tiffFile to read an image. But, if it is in a jp2k
902
+ encoding, read the raw data and convert it.
903
+ """
904
+ if self._tiffInfo.get('compression') not in {33003, 33004, 33005, 34712}:
905
+ return self._tiffFile.read_image()
906
+ output = None
907
+ for yidx, y in enumerate(range(0, self.imageHeight, self.tileHeight)):
908
+ for xidx, x in enumerate(range(0, self.imageWidth, self.tileWidth)):
909
+ tile = self.getTile(xidx, yidx, asarray=True)
910
+ if len(tile.shape) == 2:
911
+ tile = tile[:, :, np.newaxis]
912
+ if output is None:
913
+ output = np.zeros(
914
+ (self.imageHeight, self.imageWidth, tile.shape[2]), dtype=tile.dtype)
915
+ if tile.shape[0] > self.imageHeight - y:
916
+ tile = tile[:self.imageHeight - y, :, :]
917
+ if tile.shape[1] > self.imageWidth - x:
918
+ tile = tile[:, :self.imageWidth - x, :]
919
+ output[y:y + tile.shape[0], x:x + tile.shape[1], :] = tile
920
+ return output
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: large-image-source-tiff
3
- Version: 1.30.5.dev8
3
+ Version: 1.30.6
4
4
  Summary: A TIFF tilesource for large_image.
5
5
  Home-page: https://github.com/girder/large_image
6
6
  Author: Kitware, Inc.
@@ -19,13 +19,25 @@ Classifier: Programming Language :: Python :: 3.13
19
19
  Requires-Python: >=3.8
20
20
  Description-Content-Type: text/x-rst
21
21
  License-File: LICENSE
22
- Requires-Dist: large-image>=1.30.5.dev8
22
+ Requires-Dist: large-image>=1.30.6
23
23
  Requires-Dist: pylibtiff
24
24
  Requires-Dist: tifftools>=1.2.0
25
25
  Provides-Extra: all
26
26
  Requires-Dist: pylibjpeg-openjpeg; extra == "all"
27
27
  Provides-Extra: girder
28
- Requires-Dist: girder-large-image>=1.30.5.dev8; extra == "girder"
28
+ Requires-Dist: girder-large-image>=1.30.6; 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
29
41
 
30
42
  A TIFF tilesource for large_image.
31
43
 
@@ -0,0 +1,10 @@
1
+ large_image_source_tiff/__init__.py,sha256=o-NkH3SI7XLmEdejSREFMw39kfTMu2LiV3GGHsmNrPQ,36588
2
+ large_image_source_tiff/exceptions.py,sha256=NgdwloaDCtbtUMe2BU2lXEU8IwQSYtaokIwGIFypCps,617
3
+ large_image_source_tiff/girder_source.py,sha256=Dp2e3O4VTANYXZI_eybgzs5BcyuMcw2-MAzCUJ8zzPg,1031
4
+ large_image_source_tiff/tiff_reader.py,sha256=gkAhuB4-IRzNLlNr958HKE9JZpRJwVo4zRGMd4rsD2o,40338
5
+ large_image_source_tiff-1.30.6.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
6
+ large_image_source_tiff-1.30.6.dist-info/METADATA,sha256=vj4QlrQcVdZVAR1VueoBLVs-lYUFrD6G2lOpdDsDa2c,1426
7
+ large_image_source_tiff-1.30.6.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
8
+ large_image_source_tiff-1.30.6.dist-info/entry_points.txt,sha256=iZ43sIcj98SND7nDUC-_4qroBL6apyXN4iSbPXZ8LE4,166
9
+ large_image_source_tiff-1.30.6.dist-info/top_level.txt,sha256=QRx_D2oeiOOz_5FlBOAoDPF-E4Q-aFmerUWlaeP14B8,24
10
+ large_image_source_tiff-1.30.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.6.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,10 +0,0 @@
1
- large_image_source_tiff/__init__.py,sha256=lUgwD58vyfM3QOGY3913jfneHUzSYIkui0YlB2-bhzY,36060
2
- large_image_source_tiff/exceptions.py,sha256=NgdwloaDCtbtUMe2BU2lXEU8IwQSYtaokIwGIFypCps,617
3
- large_image_source_tiff/girder_source.py,sha256=Dp2e3O4VTANYXZI_eybgzs5BcyuMcw2-MAzCUJ8zzPg,1031
4
- large_image_source_tiff/tiff_reader.py,sha256=Gqzv3ct5YDicho9l23a0Gjoyln7TJNEbqAov4RnHRGI,39143
5
- large_image_source_tiff-1.30.5.dev8.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
6
- large_image_source_tiff-1.30.5.dev8.dist-info/METADATA,sha256=bPJRHpGN7PinasyFxNOyMYdyvKfLD1CNNLCo7ZWOwZI,1185
7
- large_image_source_tiff-1.30.5.dev8.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
8
- large_image_source_tiff-1.30.5.dev8.dist-info/entry_points.txt,sha256=iZ43sIcj98SND7nDUC-_4qroBL6apyXN4iSbPXZ8LE4,166
9
- large_image_source_tiff-1.30.5.dev8.dist-info/top_level.txt,sha256=QRx_D2oeiOOz_5FlBOAoDPF-E4Q-aFmerUWlaeP14B8,24
10
- large_image_source_tiff-1.30.5.dev8.dist-info/RECORD,,