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.
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/PKG-INFO +20 -9
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter/__init__.py +151 -28
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter/__main__.py +1 -1
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter.egg-info/PKG-INFO +20 -9
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter.egg-info/requires.txt +3 -3
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/pyproject.toml +4 -0
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/setup.py +5 -24
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/LICENSE +0 -0
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/README.rst +0 -0
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter/format_aperio.py +0 -0
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter.egg-info/SOURCES.txt +0 -0
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter.egg-info/dependency_links.txt +0 -0
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter.egg-info/entry_points.txt +0 -0
- {large-image-converter-1.29.5.dev10 → large_image_converter-1.33.4.dev39}/large_image_converter.egg-info/top_level.txt +0 -0
- {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
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: large-image-converter
|
|
3
|
-
Version: 1.
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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),
|
|
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
|
-
|
|
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'
|
|
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 (
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
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
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: large-image-converter
|
|
3
|
-
Version: 1.
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
21
|
+
large-image[sources]>=1.33.4.dev39
|
|
22
22
|
|
|
23
23
|
[stats]
|
|
24
24
|
packaging
|
|
@@ -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='../..'
|
|
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
|
-
|
|
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
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|