def test_split_sub_subifds(tmp_path): path = datastore.fetch('subsubifds.tif') tifftools.tiff_split(str(path), tmp_path / 'test', subifds=True) info = tifftools.read_tiff(tmp_path / 'testaaa.tif') assert len(info['ifds']) == 1 info = tifftools.read_tiff(tmp_path / 'testaai.tif') assert len(info['ifds']) == 1
def testRegionTiledOutputIsTiled(): imagePath = datastore.fetch('sample_image.ptif') source = large_image_source_tiff.open(imagePath) # TIFF isn't tiled and has only one layer params = {'output': {'maxWidth': 500, 'maxHeight': 500}, 'encoding': 'TIFF'} image, mimeType = source.getRegion(**params) info = tifftools.read_tiff(io.BytesIO(image)) assert len(info['ifds']) == 1 assert tifftools.Tag.StripOffsets.value in info['ifds'][0]['tags'] assert tifftools.Tag.TileOffsets.value not in info['ifds'][0]['tags'] # TILED is tiled and has multiple layers params = {'output': {'maxWidth': 500, 'maxHeight': 500}, 'encoding': 'TILED'} image, mimeType = source.getRegion(**params) info = tifftools.read_tiff(image) assert len(info['ifds']) == 2 assert tifftools.Tag.StripOffsets.value not in info['ifds'][0]['tags'] assert tifftools.Tag.TileOffsets.value in info['ifds'][0]['tags'] os.unlink(image) # Bigger outputs should have more layers params = {'output': {'maxWidth': 3000, 'maxHeight': 3000}, 'encoding': 'TILED'} image, mimeType = source.getRegion(**params) info = tifftools.read_tiff(image) assert len(info['ifds']) == 5 assert tifftools.Tag.StripOffsets.value not in info['ifds'][0]['tags'] assert tifftools.Tag.TileOffsets.value in info['ifds'][0]['tags'] os.unlink(image)
def test_write_switch_to_bigtiff(tmp_path): path = datastore.fetch('hamamatsu.ndpi') info = tifftools.read_tiff(path) info['ifds'].extend(info['ifds']) info['ifds'].extend(info['ifds']) info['ifds'].extend(info['ifds']) destpath = tmp_path / 'sample.tiff' tifftools.write_tiff(info, destpath) destinfo = tifftools.read_tiff(destpath) assert destinfo['bigtiff'] is True
def test_write_bigtiff_from_datatype(tmp_path): path = os.path.join(os.path.dirname(__file__), 'data', 'good_single.tif') info = tifftools.read_tiff(path) info['ifds'][0]['tags'][23456] = { 'datatype': tifftools.Datatype.LONG8, 'data': [8], } destpath = tmp_path / 'sample.tiff' tifftools.write_tiff(info, destpath) destinfo = tifftools.read_tiff(destpath) assert destinfo['bigtiff'] is True
def test_write_bad_strip_offset(tmp_path, caplog): path = os.path.join(os.path.dirname(__file__), 'data', 'bad_strip_offset.tif') info = tifftools.read_tiff(path) destpath = tmp_path / 'sample.tiff' with caplog.at_level(logging.WARNING): tifftools.write_tiff(info, destpath) assert 'from desired offset' in caplog.text destinfo = tifftools.read_tiff(destpath) assert destinfo['ifds'][0]['tags'][ tifftools.Tag.StripOffsets.value]['data'][0] == 0
def test_tiff_set_self(tmp_path): path = datastore.fetch('d043-200.tif') dest = tmp_path / 'results.tif' shutil.copy(path, dest) with pytest.raises(Exception): tifftools.tiff_set(dest, setlist=[('ImageDescription', 'Dog digging')]) info = tifftools.read_tiff(str(dest)) assert int(tifftools.Tag.ImageDescription) not in info['ifds'][0]['tags'] tifftools.tiff_set(dest, overwrite=True, setlist=[('ImageDescription', 'Dog digging')]) info = tifftools.read_tiff(str(dest)) assert info['ifds'][0]['tags'][int( tifftools.Tag.ImageDescription)]['data'] == 'Dog digging'
def test_write_bigtiff_with_offset_data(tmp_path): path = datastore.fetch('hamamatsu.ndpi') info = tifftools.read_tiff(path) info['ifds'][0]['tags'][tifftools.Tag.FreeOffsets.value] = { 'datatype': tifftools.Datatype.LONG, 'data': [8] * 256, } info['ifds'][0]['tags'][tifftools.Tag.FreeByteCounts.value] = { 'datatype': tifftools.Datatype.LONG, 'data': [16777216] * 256, } destpath = tmp_path / 'sample.tiff' tifftools.write_tiff(info, destpath) destinfo = tifftools.read_tiff(destpath) assert destinfo['bigtiff'] is True
def test_write_wrong_counts(): path = os.path.join(os.path.dirname(__file__), 'data', 'good_single.tif') info = tifftools.read_tiff(path) info['ifds'][0]['tags'][tifftools.Tag.StripByteCounts.value]['data'].pop() with pytest.raises(Exception) as exc: tifftools.write_tiff(info, '-') assert 'Offsets and byte counts do not correspond' in str(exc.value)
def test_tiff_set_setfrom(tmp_path): path = datastore.fetch('d043-200.tif') dest = tmp_path / 'results.tif' tifftools.tiff_set(str(path) + ',1', dest, setfrom=[('Model', path)]) info = tifftools.read_tiff(str(dest)) assert info['ifds'][0]['tags'][int( tifftools.Tag.Model)]['data'] == 'NIKON D500'
def testConvertJp2kCompression(tmpdir): imagePath = datastore.fetch('sample_Easy1.png') outputPath = os.path.join(tmpdir, 'out.tiff') large_image_converter.convert(imagePath, outputPath, compression='jp2k') info = tifftools.read_tiff(outputPath) assert (info['ifds'][0]['tags'][tifftools.Tag.Compression.value]['data'][0] == tifftools.constants.Compression.JP2000.value) source = large_image_source_tiff.open(outputPath) image, _ = source.getRegion(output={ 'maxWidth': 200, 'maxHeight': 200 }, format=constants.TILE_FORMAT_NUMPY) assert (image[12][167] == [215, 135, 172]).all() outputPath2 = os.path.join(tmpdir, 'out2.tiff') large_image_converter.convert(imagePath, outputPath2, compression='jp2k', psnr=50) assert os.path.getsize(outputPath2) < os.path.getsize(outputPath) outputPath3 = os.path.join(tmpdir, 'out3.tiff') large_image_converter.convert(imagePath, outputPath3, compression='jp2k', cr=100) assert os.path.getsize(outputPath3) < os.path.getsize(outputPath) assert os.path.getsize(outputPath3) != os.path.getsize(outputPath2)
def test_bigtiff_bigendian(tmp_path, bigtiff, bigendian): path = datastore.fetch('d043-200.tif') dest = tmp_path / 'results.tif' cmd = ['merge', path, str(dest)] cmd.extend(bigtiff) cmd.extend(bigendian) tifftools.main(cmd) srcinfo = tifftools.read_tiff(path) destinfo = tifftools.read_tiff(dest) srcval = srcinfo['ifds'][0]['tags'][int( tifftools.Tag.EXIFIFD)]['ifds'][0][0]['tags'][int( tifftools.constants.EXIFTag.MakerNote)]['data'] destval = destinfo['ifds'][0]['tags'][int( tifftools.Tag.EXIFIFD)]['ifds'][0][0]['tags'][int( tifftools.constants.EXIFTag.MakerNote)]['data'] assert srcval == destval
def testConverterMainFullStats(tmpdir): imagePath = datastore.fetch('sample_Easy1.png') outputPath = os.path.join(tmpdir, 'out.tiff') main.main([imagePath, outputPath, '--full-stats']) info = tifftools.read_tiff(outputPath) desc = json.loads(info['ifds'][0]['tags'][tifftools.Tag.ImageDescription.value]['data']) assert 'psnr' in desc['large_image_converter']['conversion_stats']
def testConvertTiffFloatPixels(tmpdir): imagePath = datastore.fetch('d042-353.crop.small.float32.tif') outputPath = os.path.join(tmpdir, 'out.tiff') large_image_converter.convert(imagePath, outputPath) info = tifftools.read_tiff(outputPath) assert (info['ifds'][0]['tags'][tifftools.Tag.SampleFormat.value]['data'][0] == tifftools.constants.SampleFormat.uint.value)
def test_write_allow_existing(tmp_path): path = datastore.fetch('d043-200.tif') info = tifftools.read_tiff(path) destpath = tmp_path / 'sample.tiff' tifftools.write_tiff(info, destpath) len = os.path.getsize(destpath) tifftools.write_tiff(info, destpath, allowExisting=True) assert len == os.path.getsize(destpath)
def testConvert(tmpdir, convert_args, taglist): testDir = os.path.dirname(os.path.realpath(__file__)) imagePath = os.path.join(testDir, 'test_files', 'yb10kx5k.png') outputPath = os.path.join(tmpdir, 'out.tiff') large_image_converter.convert(imagePath, outputPath, **convert_args) info = tifftools.read_tiff(outputPath) for key, value in taglist.items(): assert info['ifds'][0]['tags'][key]['data'][0] == value
def testConverterMainFullStatsWithWebp(tmpdir): imagePath = datastore.fetch('d042-353.crop.small.float32.tif') outputPath = os.path.join(tmpdir, 'out.tiff') main.main([imagePath, outputPath, '--compression', 'webp', '--full-stats']) info = tifftools.read_tiff(outputPath) desc = json.loads(info['ifds'][0]['tags'][tifftools.Tag.ImageDescription.value]['data']) assert 'psnr' in desc['large_image_converter']['conversion_stats'] assert desc['large_image_converter']['conversion_stats']['psnr'] < 60
def testConverterMainStats(tmpdir): testDir = os.path.dirname(os.path.realpath(__file__)) imagePath = os.path.join(testDir, 'test_files', 'yb10kx5k.png') outputPath = os.path.join(tmpdir, 'out.tiff') main.main([imagePath, outputPath, '--stats']) info = tifftools.read_tiff(outputPath) desc = json.loads(info['ifds'][0]['tags'][tifftools.Tag.ImageDescription.value]['data']) assert 'conversion_stats' in desc['large_image_converter']
def testConvertOMETif(tmpdir): imagePath = datastore.fetch('sample.ome.tif') outputPath = os.path.join(tmpdir, 'out.tiff') # Note: change this when we convert multi-frame files differently large_image_converter.convert(imagePath, outputPath) info = tifftools.read_tiff(outputPath) assert len(info['ifds']) == 3 assert len(info['ifds'][0]['tags'][tifftools.Tag.SubIFD.value]['ifds']) == 4
def test_write_already_exists(tmp_path): path = datastore.fetch('d043-200.tif') info = tifftools.read_tiff(path) destpath = tmp_path / 'sample.tiff' tifftools.write_tiff(info, destpath) with pytest.raises(Exception) as exc: tifftools.write_tiff(info, destpath) assert 'File already exists' in str(exc.value)
def testConvertFromTestSourceFrames(tmpdir): outputPath = os.path.join(tmpdir, 'out.tiff') large_image_converter.convert('large_image://test?maxLevel=3&frames=4', outputPath) source = large_image_source_tiff.open(outputPath) metadata = source.getMetadata() assert metadata['levels'] == 4 assert len(metadata['frames']) == 4 info = tifftools.read_tiff(outputPath) assert len(info['ifds']) == 4
def testConvertFromMultiframeImageOnlyOneFrame(tmpdir): imagePath = datastore.fetch('sample.ome.tif') outputPath = os.path.join(tmpdir, 'out.tiff') large_image_converter.convert(imagePath, outputPath, onlyFrame=2) source = large_image_source_tiff.open(outputPath) metadata = source.getMetadata() assert metadata['levels'] == 5 info = tifftools.read_tiff(outputPath) assert len(info['ifds']) == 5
def testConvertPTIF(tmpdir): imagePath = datastore.fetch('sample_image.ptif') outputPath = os.path.join(tmpdir, 'out.tiff') large_image_converter.convert(imagePath, outputPath, compression='jpeg', quality=50) info = tifftools.read_tiff(outputPath) assert len(info['ifds']) == 11
def testConvertGeospatial(tmpdir): testDir = os.path.dirname(os.path.realpath(__file__)) imagePath = os.path.join(testDir, 'test_files', 'rgb_geotiff.tiff') inputPath = os.path.join(tmpdir, 'in.geo.tiff') shutil.copy(imagePath, inputPath) outputPath = large_image_converter.convert(inputPath, level=5) assert 'geo.tiff' in outputPath assert outputPath != inputPath info = tifftools.read_tiff(outputPath) assert tifftools.Tag.ModelTiepointTag.value in info['ifds'][0]['tags']
def testConvertFromMultiframeImageNoSubIFDS(tmpdir): imagePath = datastore.fetch('sample.ome.tif') outputPath = os.path.join(tmpdir, 'out.tiff') large_image_converter.convert(imagePath, outputPath, subifds=False) source = large_image_source_tiff.open(outputPath) metadata = source.getMetadata() assert metadata['levels'] == 5 assert len(metadata['frames']) == 3 info = tifftools.read_tiff(outputPath) assert len(info['ifds']) == 15
def test_write_single_subifd(tmp_path): path = os.path.join(os.path.dirname(__file__), 'data', 'good_single.tif') info = tifftools.read_tiff(path) info['ifds'][0]['tags'][tifftools.Tag.SubIFD.value] = { 'ifds': [copy.deepcopy(info['ifds'][0])] } dest1path = tmp_path / 'sample1.tiff' tifftools.write_tiff(info, dest1path) dest1info = tifftools.read_tiff(dest1path) assert len(dest1info['ifds'][0]['tags'][tifftools.Tag.SubIFD.value]['ifds'] [0]) == 1 info = tifftools.read_tiff(path) info['ifds'][0]['tags'][tifftools.Tag.SubIFD.value] = { 'ifds': [copy.deepcopy(info['ifds'])] } dest2path = tmp_path / 'sample2.tiff' tifftools.write_tiff(info, dest2path) dest2info = tifftools.read_tiff(dest2path) assert len(dest2info['ifds'][0]['tags'][tifftools.Tag.SubIFD.value]['ifds'] [0]) == 1
def test_write_bytecount_data(tmp_path): path = os.path.join(os.path.dirname(__file__), 'data', 'good_single.tif') info = tifftools.read_tiff(path) # Just use data from within the file itself; an actual sample file with # compression 6 and defined Q, AC, and DC tables would be better. info['ifds'][0]['tags'][tifftools.Tag.JPEGQTables.value] = { 'datatype': tifftools.Datatype.LONG, 'data': [8], } destpath = tmp_path / 'sample.tiff' tifftools.write_tiff(info, destpath) assert os.path.getsize(destpath) > os.path.getsize(path) + 64
def test_tiff_set_fromfile(tmp_path): path = datastore.fetch('d043-200.tif') dest = tmp_path / 'results.tif' tagfile = tmp_path / 'tag.txt' with open(tagfile, 'w') as fptr: fptr.write('Dog digging') tifftools.tiff_set(str(path), dest, setlist=[('ImageDescription', '@%s' % tagfile)]) info = tifftools.read_tiff(str(dest)) assert info['ifds'][0]['tags'][int( tifftools.Tag.ImageDescription)]['data'] == 'Dog digging'
def testTileFrames(): imagePath = datastore.fetch('sample.ome.tif') source = large_image_source_tiff.open(imagePath) params = {'encoding': 'PNG', 'output': {'maxWidth': 200, 'maxHeight': 200}} image, mimeType = source.tileFrames(**params) assert image[:len(utilities.PNGHeader)] == utilities.PNGHeader (width, height) = struct.unpack('!LL', image[16:24]) assert width == 400 assert height == 382 params['fill'] = 'corner:black' image, mimeType = source.tileFrames(**params) assert image[:len(utilities.PNGHeader)] == utilities.PNGHeader (width, height) = struct.unpack('!LL', image[16:24]) assert width == 400 assert height == 400 params['framesAcross'] = 3 image, mimeType = source.tileFrames(**params) assert image[:len(utilities.PNGHeader)] == utilities.PNGHeader (width, height) = struct.unpack('!LL', image[16:24]) assert width == 600 assert height == 200 params['frameList'] = [0, 2] image, mimeType = source.tileFrames(**params) assert image[:len(utilities.PNGHeader)] == utilities.PNGHeader (width, height) = struct.unpack('!LL', image[16:24]) assert width == 400 assert height == 200 params['frameList'] = [0] image, mimeType = source.tileFrames(**params) assert image[:len(utilities.PNGHeader)] == utilities.PNGHeader (width, height) = struct.unpack('!LL', image[16:24]) assert width == 200 assert height == 200 params.pop('frameList') params['encoding'] = 'TILED' image, mimeType = source.tileFrames(**params) info = tifftools.read_tiff(image) assert len(info['ifds']) == 3 os.unlink(image)
def test_tiff_set_stdin(tmp_path, monkeypatch): mock_stdin = io.BytesIO() mock_stdin.write(b'Dog digging') mock_stdin.seek(0) mock_stdin.seekable = lambda: False class Namespace(object): pass mock_obj = Namespace() mock_obj.buffer = mock_stdin monkeypatch.setattr('sys.stdin', mock_obj) path = datastore.fetch('d043-200.tif') dest = tmp_path / 'results.tif' tifftools.tiff_set(str(path), dest, setlist=[('ImageDescription', '@-')]) info = tifftools.read_tiff(str(dest)) assert info['ifds'][0]['tags'][int( tifftools.Tag.ImageDescription)]['data'] == 'Dog digging'
def create_thumbnail_and_label(tempPath, info, ifdCount, needsLabel, labelPosition, **kwargs): """ Create a thumbnail and, optionally, label image for the aperio file. :param tempPath: a temporary file in a temporary directory. :param info: the tifftools info that will be written to the tiff tile; modified. :param ifdCount: the number of ifds in the first tiled image. This is 1 if there are subifds. :param needsLabel: true if a label image needs to be added. :param labelPosition: the position in the ifd list where a label image should be inserted. """ thumbnailSize = 1024 labelSize = 640 maxLabelAspect = 1.5 tileSize = info['ifds'][0]['tags'][ tifftools.Tag.TileWidth.value]['data'][0] levels = int( math.ceil( math.log(max(thumbnailSize, labelSize) / tileSize) / math.log(2))) + 1 neededList = ['thumbnail'] if needsLabel: neededList[0:0] = ['label'] tiledPath = tempPath + '-overview.tiff' firstFrameIfds = info['ifds'][max(0, ifdCount - levels):ifdCount] tifftools.write_tiff(firstFrameIfds, tiledPath) ts = large_image_source_tiff.open(tiledPath) for subImage in neededList: if subImage == 'label': x = max(0, (ts.sizeX - min(ts.sizeX, ts.sizeY) * maxLabelAspect) // 2) y = max(0, (ts.sizeY - min(ts.sizeX, ts.sizeY) * maxLabelAspect) // 2) regionParams = { 'output': dict(maxWidth=labelSize, maxHeight=labelSize), 'region': dict(left=x, right=ts.sizeX - x, top=y, bottom=ts.sizeY - y), } else: regionParams = { 'output': dict(maxWidth=thumbnailSize, maxHeight=thumbnailSize) } image, _ = ts.getRegion(format=large_image.constants.TILE_FORMAT_PIL, **regionParams) if image.mode not in {'RGB', 'L'}: image = image.convert('RGB') if subImage == 'label': image = image.rotate(90, expand=True) imagePath = tempPath + '-image_%s.tiff' % subImage image.save(imagePath, 'TIFF', compression='tiff_jpeg', quality=int(kwargs.get('quality', 90))) imageInfo = tifftools.read_tiff(imagePath) ifd = imageInfo['ifds'][0] if subImage == 'label': ifd['tags'][tifftools.Tag.Orientation.value] = { 'data': [tifftools.constants.Orientation.RightTop.value], 'datatype': tifftools.Datatype.LONG, } description = AperioHeader + AssociatedHeader.format( name='label', width=ifd['tags'][tifftools.Tag.ImageWidth.value]['data'][0], height=ifd['tags'][tifftools.Tag.ImageHeight.value]['data'][0], ) ifd['tags'][tifftools.Tag.ImageDescription.value] = { 'data': description, 'datatype': tifftools.Datatype.ASCII, } ifd['tags'][tifftools.Tag.NewSubfileType.value] = { 'data': [tifftools.constants.NewSubfileType.ReducedImage.value], 'datatype': tifftools.Datatype.LONG, } info['ifds'][labelPosition:labelPosition] = imageInfo['ifds'] else: fullDesc = info['ifds'][0]['tags'][ tifftools.Tag.ImageDescription.value]['data'] description = fullDesc.split('[', 1)[0] + ThumbnailHeader.format( width=ifd['tags'][tifftools.Tag.ImageWidth.value]['data'][0], height=ifd['tags'][tifftools.Tag.ImageHeight.value]['data'][0], ) + fullDesc.split('|', 1)[1] ifd['tags'][tifftools.Tag.ImageDescription.value] = { 'data': description, 'datatype': tifftools.Datatype.ASCII, } info['ifds'][1:1] = imageInfo['ifds']