large-image-converter 1.29.5.dev10__tar.gz → 1.33.4.dev39__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 (15) hide show
  1. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/PKG-INFO +20 -9
  2. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter/__init__.py +151 -28
  3. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter/__main__.py +1 -1
  4. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter.egg-info/PKG-INFO +20 -9
  5. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter.egg-info/requires.txt +3 -3
  6. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/pyproject.toml +4 -0
  7. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/setup.py +5 -24
  8. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/LICENSE +0 -0
  9. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/README.rst +0 -0
  10. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter/format_aperio.py +0 -0
  11. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter.egg-info/SOURCES.txt +0 -0
  12. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter.egg-info/dependency_links.txt +0 -0
  13. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter.egg-info/entry_points.txt +0 -0
  14. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter.egg-info/top_level.txt +0 -0
  15. {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/setup.cfg +0 -0
@@ -1,23 +1,23 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: large-image-converter
3
- Version: 1.29.5.dev10
3
+ Version: 1.33.4.dev39
4
4
  Summary: Converter for Large Image.
5
5
  Author: Kitware Inc
6
6
  Author-email: kitware@kitware.com
7
- License: Apache Software License 2.0
7
+ License: Apache-2.0
8
8
  Classifier: Development Status :: 5 - Production/Stable
9
- Classifier: License :: OSI Approved :: Apache Software License
10
9
  Classifier: Topic :: Scientific/Engineering
11
10
  Classifier: Intended Audience :: Science/Research
12
11
  Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.8
14
12
  Classifier: Programming Language :: Python :: 3.9
15
13
  Classifier: Programming Language :: Python :: 3.10
16
14
  Classifier: Programming Language :: Python :: 3.11
17
15
  Classifier: Programming Language :: Python :: 3.12
18
- Requires-Python: >=3.8
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/x-rst
19
19
  License-File: LICENSE
20
- Requires-Dist: large-image-source-tiff>=1.29.5.dev10
20
+ Requires-Dist: large-image-source-tiff>=1.33.4.dev39
21
21
  Requires-Dist: numpy
22
22
  Requires-Dist: psutil
23
23
  Requires-Dist: pyvips
@@ -27,16 +27,27 @@ Requires-Dist: glymur; extra == "jp2k"
27
27
  Provides-Extra: geospatial
28
28
  Requires-Dist: gdal; extra == "geospatial"
29
29
  Provides-Extra: sources
30
- Requires-Dist: large-image[sources]>=1.29.5.dev10; extra == "sources"
30
+ Requires-Dist: large-image[sources]>=1.33.4.dev39; extra == "sources"
31
31
  Provides-Extra: stats
32
32
  Requires-Dist: packaging; extra == "stats"
33
33
  Requires-Dist: scikit-image; extra == "stats"
34
34
  Provides-Extra: all
35
35
  Requires-Dist: glymur; extra == "all"
36
36
  Requires-Dist: gdal; extra == "all"
37
- Requires-Dist: large-image[sources]>=1.29.5.dev10; extra == "all"
37
+ Requires-Dist: large-image[sources]>=1.33.4.dev39; extra == "all"
38
38
  Requires-Dist: packaging; extra == "all"
39
39
  Requires-Dist: scikit-image; extra == "all"
40
+ Dynamic: author
41
+ Dynamic: author-email
42
+ Dynamic: classifier
43
+ Dynamic: description
44
+ Dynamic: description-content-type
45
+ Dynamic: license
46
+ Dynamic: license-file
47
+ Dynamic: provides-extra
48
+ Dynamic: requires-dist
49
+ Dynamic: requires-python
50
+ Dynamic: summary
40
51
 
41
52
  *********************
42
53
  Large Image Converter
@@ -80,7 +80,7 @@ def _data_from_large_image(path, outputPath, **kwargs):
80
80
  try:
81
81
  ts = large_image.open(path, noCache=True)
82
82
  except Exception:
83
- return
83
+ return None
84
84
  else:
85
85
  import urllib.parse
86
86
 
@@ -109,7 +109,7 @@ def _data_from_large_image(path, outputPath, **kwargs):
109
109
  _pool_add(tasks, (pool.submit(
110
110
  _convert_via_vips, img, savePath, outputPath, mime=mime, forTiled=False), ))
111
111
  results['images'][key] = savePath
112
- _drain_pool(pool, tasks)
112
+ _drain_pool(pool, tasks, 'associated images')
113
113
  return results
114
114
 
115
115
 
@@ -218,7 +218,7 @@ def _generate_multiframe_tiff(inputPath, outputPath, tempPath, lidata, **kwargs)
218
218
  _pool_add(tasks, (pool.submit(
219
219
  _convert_via_vips, subInputPath, savePath, tempPath, False), ))
220
220
  extraImages[key] = savePath
221
- _drain_pool(pool, tasks)
221
+ _drain_pool(pool, tasks, 'subpage')
222
222
  _output_tiff(outputList, outputPath, tempPath, lidata, extraImages, **kwargs)
223
223
 
224
224
 
@@ -249,7 +249,7 @@ def _generate_tiff(inputPath, outputPath, tempPath, lidata, **kwargs):
249
249
 
250
250
 
251
251
  def _convert_via_vips(inputPathOrBuffer, outputPath, tempPath, forTiled=True,
252
- status=None, **kwargs):
252
+ status=None, preferredVipsCast=None, **kwargs):
253
253
  """
254
254
  Convert a file, buffer, or vips image to a tiff file. This is equivalent
255
255
  to a vips command line of
@@ -263,6 +263,7 @@ def _convert_via_vips(inputPathOrBuffer, outputPath, tempPath, forTiled=True,
263
263
  also stores files in TMPDIR
264
264
  :param forTiled: True if the output should be tiled, false if not.
265
265
  :param status: an optional additional string to add to log messages.
266
+ :param preferredVipsCast: vips scaling parameters to use in a cast.
266
267
  :param kwargs: addition arguments that get passed to _vipsParameters
267
268
  and _convert_to_jp2k.
268
269
  """
@@ -285,7 +286,7 @@ def _convert_via_vips(inputPathOrBuffer, outputPath, tempPath, forTiled=True,
285
286
  adjusted = format_hook('modify_vips_image_before_output', image, convertParams, **kwargs)
286
287
  if adjusted is False:
287
288
  return
288
- elif adjusted:
289
+ if adjusted:
289
290
  image = adjusted
290
291
  if (convertParams['compression'] not in {'jpeg'} or
291
292
  image.interpretation != pyvips.Interpretation.SCRGB):
@@ -294,7 +295,8 @@ def _convert_via_vips(inputPathOrBuffer, outputPath, tempPath, forTiled=True,
294
295
  image = _vipsCast(
295
296
  image,
296
297
  convertParams['compression'] in {'webp', 'jpeg'} or
297
- kwargs.get('compression') in {'jp2k'})
298
+ kwargs.get('compression') in {'jp2k'},
299
+ preferredVipsCast)
298
300
  # TODO: revisit the TMPDIR override; this is not thread safe
299
301
  # oldtmpdir = os.environ.get('TMPDIR')
300
302
  # os.environ['TMPDIR'] = os.path.dirname(tempPath)
@@ -349,7 +351,7 @@ def _concurrency_to_value(_concurrency=None, **kwargs):
349
351
  return max(1, large_image.config.cpu_count(logical=True) + _concurrency)
350
352
 
351
353
 
352
- def _get_thread_pool(memoryLimit=None, **kwargs):
354
+ def _get_thread_pool(memoryLimit=None, parentConcurrency=None, numItems=None, **kwargs):
353
355
  """
354
356
  Allocate a thread pool based on the specific concurrency.
355
357
 
@@ -357,12 +359,37 @@ def _get_thread_pool(memoryLimit=None, **kwargs):
357
359
  process per memoryLimit bytes of total memory.
358
360
  """
359
361
  concurrency = _concurrency_to_value(**kwargs)
362
+ if parentConcurrency and parentConcurrency > 1 and concurrency > 1:
363
+ concurrency = max(1, int(math.ceil(concurrency / parentConcurrency)))
360
364
  if memoryLimit:
365
+ if parentConcurrency:
366
+ memoryLimit *= parentConcurrency
361
367
  concurrency = min(concurrency, large_image.config.total_memory() // memoryLimit)
368
+ if numItems and numItems >= 1 and concurrency > numItems:
369
+ concurrency = numItems
362
370
  concurrency = max(1, concurrency)
363
371
  return concurrent.futures.ThreadPoolExecutor(max_workers=concurrency)
364
372
 
365
373
 
374
+ def _pool_log(left, total, label):
375
+ """
376
+ Log processing within a pool.
377
+
378
+ :param left: units left to process.
379
+ :param total: total units left to process.
380
+ :param label: label to log describing what is being processed.
381
+ """
382
+ if not hasattr(logger, '_pool_log_starttime'):
383
+ logger._pool_log_starttime = time.time()
384
+ if not hasattr(logger, '_pool_log_lastlog'):
385
+ logger._pool_log_lastlog = time.time()
386
+ if time.time() - logger._pool_log_lastlog < 10:
387
+ return
388
+ elapsed = time.time() - logger._pool_log_starttime
389
+ logger.debug('%d/%d %s left %4.2fs', left, total, label, elapsed)
390
+ logger._pool_log_lastlog = time.time()
391
+
392
+
366
393
  def _pool_add(tasks, newtask):
367
394
  """
368
395
  Add a new task to a pool, then drain any finished tasks at the start of the
@@ -381,7 +408,7 @@ def _pool_add(tasks, newtask):
381
408
  tasks.pop(0)
382
409
 
383
410
 
384
- def _drain_pool(pool, tasks):
411
+ def _drain_pool(pool, tasks, label=''):
385
412
  """
386
413
  Wait for all tasks in a pool to complete, then shutdown the pool.
387
414
 
@@ -389,6 +416,8 @@ def _drain_pool(pool, tasks):
389
416
  :param tasks: a list containing either lists or tuples, the last element
390
417
  of which is a task submitted to the pool. Altered.
391
418
  """
419
+ numtasks = len(tasks)
420
+ _pool_log(len(tasks), numtasks, label)
392
421
  while len(tasks):
393
422
  # This allows better stopping on a SIGTERM
394
423
  try:
@@ -396,6 +425,7 @@ def _drain_pool(pool, tasks):
396
425
  except concurrent.futures.TimeoutError:
397
426
  continue
398
427
  tasks.pop(0)
428
+ _pool_log(len(tasks), numtasks, label)
399
429
  pool.shutdown(False)
400
430
 
401
431
 
@@ -507,7 +537,9 @@ def _convert_large_image_tile(tilelock, strips, tile):
507
537
  strips[ty] = strips[ty].insert(vimg, x, 0, expand=True)
508
538
 
509
539
 
510
- def _convert_large_image_frame(frame, numFrames, ts, frameOutputPath, tempPath, **kwargs):
540
+ def _convert_large_image_frame(
541
+ frame, numFrames, ts, frameOutputPath, tempPath, preferredVipsCast=None,
542
+ parentConcurrency=None, **kwargs):
511
543
  """
512
544
  Convert a single frame from a large_image source. This parallelizes tile
513
545
  reads. Once all tiles are converted to a composited vips image, a tiff
@@ -518,23 +550,103 @@ def _convert_large_image_frame(frame, numFrames, ts, frameOutputPath, tempPath,
518
550
  :param ts: the open tile source.
519
551
  :param frameOutputPath: the destination name for the tiff file.
520
552
  :param tempPath: a temporary file in a temporary directory.
553
+ :param preferredVipsCast: vips scaling parameters to use in a cast.
554
+ :param parentConcurrency: amount of concurrency used by parent task.
521
555
  """
522
556
  # The iterator tile size is a balance between memory use and fewer calls
523
557
  # and file handles.
524
558
  _iterTileSize = 4096
525
559
  logger.info('Processing frame %d/%d', frame + 1, numFrames)
526
560
  strips = []
527
- pool = _get_thread_pool(**kwargs)
561
+ pool = _get_thread_pool(
562
+ memoryLimit=FrameMemoryEstimate,
563
+ # allow multiple tiles even if we are using all the cores, as it
564
+ # balances I/O and computation
565
+ parentConcurrency=(parentConcurrency // 2),
566
+ **kwargs)
528
567
  tasks = []
529
568
  tilelock = threading.Lock()
530
569
  for tile in ts.tileIterator(tile_size=dict(width=_iterTileSize), frame=frame):
531
570
  _pool_add(tasks, (pool.submit(_convert_large_image_tile, tilelock, strips, tile), ))
532
- _drain_pool(pool, tasks)
571
+ _drain_pool(pool, tasks, f'tiles from frame {frame + 1}/{numFrames}')
572
+ minbands = min(strip.bands for strip in strips)
573
+ maxbands = max(strip.bands for strip in strips)
574
+ if minbands != maxbands:
575
+ strips = [strip[:minbands] for strip in strips]
576
+ # Persist the strips to temp files to build them into single objects;
577
+ # otherwise vips will use arbitrarily large amounts of memory
578
+ for sidx in range(len(strips)):
579
+ _pool_log(len(strips) - sidx, len(strips), 'resolving strips')
580
+ strip = strips[sidx]
581
+ vimgTemp = pyvips.Image.new_temp_file('%s.v')
582
+ strip.write(vimgTemp)
583
+ strips[sidx] = vimgTemp
533
584
  img = strips[0]
534
585
  for stripidx in range(1, len(strips)):
535
586
  img = img.insert(strips[stripidx], 0, stripidx * _iterTileSize, expand=True)
536
587
  _convert_via_vips(
537
- img, frameOutputPath, tempPath, status='%d/%d' % (frame + 1, numFrames), **kwargs)
588
+ img, frameOutputPath, tempPath, status='%d/%d' % (frame + 1, numFrames),
589
+ preferredVipsCast=preferredVipsCast, **kwargs)
590
+
591
+
592
+ def _output_type(lidata, keepFloat=False): # noqa
593
+ """
594
+ Determine how to cast and scale vips data based on actual image contents.
595
+ """
596
+ try:
597
+ intype = np.dtype(lidata['tilesource'].dtype)
598
+ except Exception:
599
+ return None
600
+ if intype == np.uint8 or intype == np.uint16:
601
+ return None
602
+ if keepFloat and np.issubdtype(intype, np.floating):
603
+ if intype == np.float16 or intype == np.float32:
604
+ return (pyvips.BandFormat.FLOAT, 0, 1)
605
+ return (pyvips.BandFormat.DOUBLE, 0, 1)
606
+ logger.debug('Checking data range')
607
+ minval = maxval = None
608
+ for frame in range(len(lidata['metadata'].get('frames', [0]))):
609
+ h = lidata['tilesource'].histogram(
610
+ onlyMinMax=True, output=dict(maxWidth=2048, maxHeight=2048),
611
+ resample=0, frame=frame)
612
+ if 'max' not in h:
613
+ continue
614
+ if maxval is None:
615
+ maxval = max(h['max'].tolist())
616
+ minval = min(h['min'].tolist())
617
+ else:
618
+ maxval = max(maxval, max(h['max'].tolist()))
619
+ minval = min(minval, min(h['min'].tolist()))
620
+ lidata['range'] = (minval, maxval)
621
+ logger.debug('Data range is [%r, %r]', minval, maxval)
622
+ if minval >= 0 and intype == np.int8:
623
+ return (pyvips.BandFormat.UCHAR, 0, 1)
624
+ if minval >= 0 and intype == np.int16:
625
+ return (pyvips.BandFormat.USHORT, 0, 1)
626
+ if minval >= 0 and maxval == 0:
627
+ return (pyvips.BandFormat.UCHAR, 0, 1)
628
+ if minval >= 0 and maxval <= 2 ** -8:
629
+ return (pyvips.BandFormat.USHORT, 0,
630
+ 2 ** -(math.ceil(math.log2(maxval)) - 16) - 2 ** -math.ceil(math.log2(maxval)))
631
+ if minval >= 0 and maxval <= 1:
632
+ return (pyvips.BandFormat.USHORT, 0, 65535)
633
+ if minval >= 0 and maxval < 256:
634
+ return (pyvips.BandFormat.UCHAR, 0, 1)
635
+ if minval >= 0 and maxval < 65536:
636
+ return (pyvips.BandFormat.USHORT, 0, 1)
637
+ if minval >= 0:
638
+ return (pyvips.BandFormat.USHORT, 0,
639
+ 2 ** -(math.ceil(math.log2(maxval)) - 16) - 2 ** -math.ceil(math.log2(maxval)))
640
+ if minval >= -2 ** -8 and maxval <= 2 ** -8:
641
+ return (pyvips.BandFormat.USHORT, 1,
642
+ 2 ** -(math.ceil(math.log2(maxval)) - 15) - 2 ** -math.ceil(math.log2(maxval)))
643
+ if minval >= -1 and maxval <= 1:
644
+ return (pyvips.BandFormat.USHORT, 1, 32767)
645
+ if minval >= -32768 and maxval < 32768:
646
+ return (pyvips.BandFormat.USHORT, 32768, 1)
647
+ return (pyvips.BandFormat.USHORT, 0,
648
+ 2 ** -(math.ceil(math.log2(max(-minval, maxval))) - 16) -
649
+ 2 ** -math.ceil(math.log2(max(-minval, maxval))))
538
650
 
539
651
 
540
652
  def _convert_large_image(inputPath, outputPath, tempPath, lidata, **kwargs):
@@ -549,23 +661,25 @@ def _convert_large_image(inputPath, outputPath, tempPath, lidata, **kwargs):
549
661
  images.
550
662
  """
551
663
  ts = lidata['tilesource']
664
+ lidata['_vips_cast'] = _output_type(lidata, kwargs.get('keepFloat', False))
552
665
  numFrames = len(lidata['metadata'].get('frames', [0]))
553
666
  outputList = []
554
667
  tasks = []
555
- pool = _get_thread_pool(memoryLimit=FrameMemoryEstimate, **kwargs)
556
668
  startFrame = 0
557
669
  endFrame = numFrames
558
670
  if kwargs.get('onlyFrame') is not None and str(kwargs.get('onlyFrame')):
559
671
  startFrame = int(kwargs.get('onlyFrame'))
560
672
  endFrame = startFrame + 1
673
+ pool = _get_thread_pool(memoryLimit=FrameMemoryEstimate,
674
+ numItems=endFrame - startFrame, **kwargs)
561
675
  for frame in range(startFrame, endFrame):
562
676
  frameOutputPath = tempPath + '-%d-%s.tiff' % (
563
677
  frame + 1, time.strftime('%Y%m%d-%H%M%S'))
564
678
  _pool_add(tasks, (pool.submit(
565
679
  _convert_large_image_frame, frame, numFrames, ts, frameOutputPath,
566
- tempPath, **kwargs), ))
680
+ tempPath, lidata['_vips_cast'], pool._max_workers, **kwargs), ))
567
681
  outputList.append(frameOutputPath)
568
- _drain_pool(pool, tasks)
682
+ _drain_pool(pool, tasks, 'frames')
569
683
  _output_tiff(outputList, outputPath, tempPath, lidata, **kwargs)
570
684
 
571
685
 
@@ -733,7 +847,7 @@ def _is_lossy(path, tiffinfo=None):
733
847
 
734
848
  def _is_multiframe(path):
735
849
  """
736
- Check if a path is a multiframe file.
850
+ Check if a path is a multiframe file according to vips.
737
851
 
738
852
  :param path: The path to the file
739
853
  :returns: True if multiframe.
@@ -743,18 +857,17 @@ def _is_multiframe(path):
743
857
  with _newFromFileLock:
744
858
  image = pyvips.Image.new_from_file(path)
745
859
  except Exception:
746
- try:
747
- open(path, 'rb').read(1)
748
- raise
749
- except Exception:
750
- logger.warning('Is the file reachable and readable? (%r)', path)
751
- raise OSError(path) from None
860
+ return None
752
861
  pages = 1
753
862
  if 'n-pages' in image.get_fields():
754
863
  pages = image.get_value('n-pages')
755
864
  return pages > 1
756
865
 
757
866
 
867
+ def _is_new(path):
868
+ return os.path.basename(path).startswith(large_image.constants.NEW_IMAGE_PATH_FLAG)
869
+
870
+
758
871
  def _list_possible_sizes(width, height):
759
872
  """
760
873
  Given a width and height, return a list of possible sizes that could be
@@ -877,6 +990,8 @@ def convert(inputPath, outputPath=None, **kwargs): # noqa: C901
877
990
  primary ifds.
878
991
  :param overwrite: if not True, throw an exception if the output path
879
992
  already exists.
993
+ :param keepFloat: if True, keep float or double data types as they are, if
994
+ possible.
880
995
 
881
996
  Additional optional parameters:
882
997
 
@@ -887,6 +1002,7 @@ def convert(inputPath, outputPath=None, **kwargs): # noqa: C901
887
1002
 
888
1003
  :returns: outputPath if successful
889
1004
  """
1005
+ logger._pool_log_starttime = time.time()
890
1006
  if kwargs.get('_concurrency'):
891
1007
  os.environ['VIPS_CONCURRENCY'] = str(_concurrency_to_value(**kwargs))
892
1008
  geospatial = kwargs.get('geospatial')
@@ -895,7 +1011,7 @@ def convert(inputPath, outputPath=None, **kwargs): # noqa: C901
895
1011
  logger.debug('Is file geospatial: %r', geospatial)
896
1012
  suffix = format_hook('adjust_params', geospatial, kwargs, **kwargs)
897
1013
  if suffix is False:
898
- return
1014
+ return None
899
1015
  suffix = suffix or ('.tiff' if not geospatial else '.geo.tiff')
900
1016
  if not outputPath:
901
1017
  outputPath = os.path.splitext(inputPath)[0] + suffix
@@ -912,7 +1028,7 @@ def convert(inputPath, outputPath=None, **kwargs): # noqa: C901
912
1028
  except Exception:
913
1029
  tiffinfo = None
914
1030
  eightbit = _is_eightbit(inputPath, tiffinfo)
915
- if not kwargs.get('compression', None):
1031
+ if not kwargs.get('compression'):
916
1032
  kwargs = kwargs.copy()
917
1033
  lossy = _is_lossy(inputPath, tiffinfo)
918
1034
  logger.debug('Is file lossy: %r', lossy)
@@ -927,10 +1043,15 @@ def convert(inputPath, outputPath=None, **kwargs): # noqa: C901
927
1043
  lidata = _data_from_large_image(str(inputPath), tempPath, **kwargs)
928
1044
  logger.log(logging.DEBUG - 1, 'large_image information for %s: %r',
929
1045
  inputPath, lidata)
930
- if lidata and (not is_vips(
931
- inputPath, (lidata['metadata']['sizeX'], lidata['metadata']['sizeY'])) or (
932
- len(lidata['metadata'].get('frames', [])) >= 2 and
933
- not _is_multiframe(inputPath))):
1046
+ if lidata and (_is_new(inputPath) or _is_multiframe(inputPath)):
1047
+ _convert_large_image(inputPath, outputPath, tempPath, lidata, **kwargs)
1048
+ elif lidata and (
1049
+ (len(lidata['metadata'].get('frames', [])) >= 2 and
1050
+ not _is_multiframe(inputPath)) or
1051
+ (np.dtype(lidata['tilesource'].dtype) != np.uint8 and
1052
+ np.dtype(lidata['tilesource'].dtype) != np.uint16) or
1053
+ not is_vips(inputPath, (lidata['metadata']['sizeX'], lidata['metadata']['sizeY']))
1054
+ ):
934
1055
  _convert_large_image(inputPath, outputPath, tempPath, lidata, **kwargs)
935
1056
  elif _is_multiframe(inputPath):
936
1057
  _generate_multiframe_tiff(inputPath, outputPath, tempPath, lidata, **kwargs)
@@ -940,6 +1061,8 @@ def convert(inputPath, outputPath=None, **kwargs): # noqa: C901
940
1061
  except Exception:
941
1062
  if lidata:
942
1063
  _convert_large_image(inputPath, outputPath, tempPath, lidata, **kwargs)
1064
+ else:
1065
+ raise
943
1066
  return outputPath
944
1067
 
945
1068
 
@@ -260,7 +260,7 @@ def main(args=sys.argv[1:]):
260
260
  desc = json.loads(info['ifds'][0]['tags'][tifftools.Tag.ImageDescription.value]['data'])
261
261
  except Exception:
262
262
  logger.debug('Cannot generate statistics.')
263
- return
263
+ return None
264
264
  desc['large_image_converter']['conversion_stats'] = {
265
265
  'time': end_time - start_time,
266
266
  'filesize': os.path.getsize(dest),
@@ -1,23 +1,23 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: large-image-converter
3
- Version: 1.29.5.dev10
3
+ Version: 1.33.4.dev39
4
4
  Summary: Converter for Large Image.
5
5
  Author: Kitware Inc
6
6
  Author-email: kitware@kitware.com
7
- License: Apache Software License 2.0
7
+ License: Apache-2.0
8
8
  Classifier: Development Status :: 5 - Production/Stable
9
- Classifier: License :: OSI Approved :: Apache Software License
10
9
  Classifier: Topic :: Scientific/Engineering
11
10
  Classifier: Intended Audience :: Science/Research
12
11
  Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.8
14
12
  Classifier: Programming Language :: Python :: 3.9
15
13
  Classifier: Programming Language :: Python :: 3.10
16
14
  Classifier: Programming Language :: Python :: 3.11
17
15
  Classifier: Programming Language :: Python :: 3.12
18
- Requires-Python: >=3.8
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/x-rst
19
19
  License-File: LICENSE
20
- Requires-Dist: large-image-source-tiff>=1.29.5.dev10
20
+ Requires-Dist: large-image-source-tiff>=1.33.4.dev39
21
21
  Requires-Dist: numpy
22
22
  Requires-Dist: psutil
23
23
  Requires-Dist: pyvips
@@ -27,16 +27,27 @@ Requires-Dist: glymur; extra == "jp2k"
27
27
  Provides-Extra: geospatial
28
28
  Requires-Dist: gdal; extra == "geospatial"
29
29
  Provides-Extra: sources
30
- Requires-Dist: large-image[sources]>=1.29.5.dev10; extra == "sources"
30
+ Requires-Dist: large-image[sources]>=1.33.4.dev39; extra == "sources"
31
31
  Provides-Extra: stats
32
32
  Requires-Dist: packaging; extra == "stats"
33
33
  Requires-Dist: scikit-image; extra == "stats"
34
34
  Provides-Extra: all
35
35
  Requires-Dist: glymur; extra == "all"
36
36
  Requires-Dist: gdal; extra == "all"
37
- Requires-Dist: large-image[sources]>=1.29.5.dev10; extra == "all"
37
+ Requires-Dist: large-image[sources]>=1.33.4.dev39; extra == "all"
38
38
  Requires-Dist: packaging; extra == "all"
39
39
  Requires-Dist: scikit-image; extra == "all"
40
+ Dynamic: author
41
+ Dynamic: author-email
42
+ Dynamic: classifier
43
+ Dynamic: description
44
+ Dynamic: description-content-type
45
+ Dynamic: license
46
+ Dynamic: license-file
47
+ Dynamic: provides-extra
48
+ Dynamic: requires-dist
49
+ Dynamic: requires-python
50
+ Dynamic: summary
40
51
 
41
52
  *********************
42
53
  Large Image Converter
@@ -1,4 +1,4 @@
1
- large-image-source-tiff>=1.29.5.dev10
1
+ large-image-source-tiff>=1.33.4.dev39
2
2
  numpy
3
3
  psutil
4
4
  pyvips
@@ -7,7 +7,7 @@ tifftools
7
7
  [all]
8
8
  glymur
9
9
  gdal
10
- large-image[sources]>=1.29.5.dev10
10
+ large-image[sources]>=1.33.4.dev39
11
11
  packaging
12
12
  scikit-image
13
13
 
@@ -18,7 +18,7 @@ gdal
18
18
  glymur
19
19
 
20
20
  [sources]
21
- large-image[sources]>=1.29.5.dev10
21
+ large-image[sources]>=1.33.4.dev39
22
22
 
23
23
  [stats]
24
24
  packaging
@@ -1,3 +1,7 @@
1
1
  [build-system]
2
2
  requires = ["setuptools", "setuptools-scm"]
3
3
  build-backend = "setuptools.build_meta"
4
+
5
+ [tool.setuptools_scm]
6
+ fallback_version = "0.0.0"
7
+ root = "../.."
@@ -9,52 +9,34 @@ description = 'Converter for Large Image.'
9
9
  long_description = readme
10
10
 
11
11
 
12
- def prerelease_local_scheme(version):
13
- """
14
- Return local scheme version unless building on master in CircleCI.
15
-
16
- This function returns the local scheme version number
17
- (e.g. 0.0.0.dev<N>+g<HASH>) unless building on CircleCI for a
18
- pre-release in which case it ignores the hash and produces a
19
- PEP440 compliant pre-release version number (e.g. 0.0.0.dev<N>).
20
- """
21
- from setuptools_scm.version import get_local_node_and_date
22
-
23
- if os.getenv('CIRCLE_BRANCH') in ('master', ):
24
- return ''
25
- else:
26
- return get_local_node_and_date(version)
27
-
28
-
29
12
  try:
30
13
  from setuptools_scm import get_version
31
14
 
32
- version = get_version(root='../..', local_scheme=prerelease_local_scheme)
15
+ version = get_version(root='../..')
33
16
  limit_version = f'>={version}' if '+' not in version and not os.getenv('TOX_ENV_NAME') else ''
34
17
  except (ImportError, LookupError):
35
18
  limit_version = ''
36
19
 
37
20
  setup(
38
21
  name='large-image-converter',
39
- use_scm_version={'root': '../..', 'local_scheme': prerelease_local_scheme,
40
- 'fallback_version': '0.0.0'},
41
22
  description=description,
42
23
  long_description=long_description,
43
- license='Apache Software License 2.0',
24
+ long_description_content_type='text/x-rst',
25
+ license='Apache-2.0',
44
26
  author='Kitware Inc',
45
27
  author_email='kitware@kitware.com',
46
28
  classifiers=[
47
29
  'Development Status :: 5 - Production/Stable',
48
- 'License :: OSI Approved :: Apache Software License',
49
30
  'Topic :: Scientific/Engineering',
50
31
  'Intended Audience :: Science/Research',
51
32
  'Programming Language :: Python :: 3',
52
- 'Programming Language :: Python :: 3.8',
53
33
  'Programming Language :: Python :: 3.9',
54
34
  'Programming Language :: Python :: 3.10',
55
35
  'Programming Language :: Python :: 3.11',
56
36
  'Programming Language :: Python :: 3.12',
37
+ 'Programming Language :: Python :: 3.13',
57
38
  ],
39
+ python_requires='>=3.9',
58
40
  install_requires=[
59
41
  f'large-image-source-tiff{limit_version}',
60
42
  'numpy',
@@ -88,5 +70,4 @@ setup(
88
70
  entry_points={
89
71
  'console_scripts': ['large_image_converter = large_image_converter.__main__:main'],
90
72
  },
91
- python_requires='>=3.8',
92
73
  )