Skip to content

Instantly share code, notes, and snippets.

@scossu
Last active March 22, 2019 14:36
Show Gist options
  • Save scossu/77c05daac125e8250f916b0ddc8dd50a to your computer and use it in GitHub Desktop.
Save scossu/77c05daac125e8250f916b0ddc8dd50a to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
#
# Convert TIFF to a Pyramidal TIFF using PyVips.
# Usage: tiff_to_ptiff.py <input image path> <output image path>
import logging
import sys
from io import BytesIO
from os import path
import pyvips
from pyvips.enums import Interpretation as chmode, Intent
SRGB_PROFILE_FPATH = path.join(gcis.basedir, "resources", "sRGB2014.icc")
GRAY_PROFILE_FPATH = path.join(
gcis.basedir, "resources", "generic_gray_gamma_2.2.icc"
)
logger = logging.getLogger(__name__)
def tiff_to_ptiff(data, out_path=None, auto_flatten=False):
"""
Convert a TIFF image to a pyramidal TIFF using pyvips and tiffcp.
:param BytesIO data: Streaming buffer to read from.
:param str out_path: Path to write to. If omitted, a BytesIO object is
returned.
:param bool auto_flatten: If set to True, when an image with alpha is
encountered, the channel will be discarded and the image flattened.
If False (the default), an exception is raised in order to allow
manual inspection and correction of the image.
:rtype: BytesIO or None
:return: Byte buffer with encoded TIFF image contents, if ``out_path`` was
not set, otherwise ``None``.
"""
pyvips.cache_set_max(0)
img = pyvips.Image.new_from_buffer(data.read(), "", access="sequential")
channels = img.interpretation
if channels not in (chmode.SRGB, chmode.RGB, chmode.B_W):
raise ValueError(f"Color mode {channels} is not supported.")
# Since only RGB and gray are supported, 4 bands means RGBA, 2 bands
# means Gray + A.
if img.bands == 4 or img.bands == 2:
if auto_flatten:
logger.info("Removing alpha channel.")
img = img.flatten()
else:
raise ValueError(
"Alpha channel detected. Not trying to flatten automatically."
)
# Set the right ICC profile unconditionally.
logger.info("Setting ICC profile.")
profile_fpath = (
GRAY_PROFILE_FPATH if channels == chmode.B_W else SRGB_PROFILE_FPATH
)
# with open(profile_fpath) as fh:
# img.set('icc-profile-data', fh.read())
img = img.icc_transform(profile_fpath, intent=Intent.PERCEPTUAL)
kw = {
"compression": "jpeg",
"Q": 90,
"profile": profile_fpath,
"tile": True,
"tile_width": 256,
"tile_height": 256,
"pyramid": True,
"bigtiff": True,
#"region_shrink": "average",
}
if out_path:
img.tiffsave(out_path, **kw)
else:
return BytesIO(img.tiffsave_buffer(**kw))
if __name__ = "__main__":
with open(sys.argv[0], 'rb') as fh:
tiff_to_ptiff(fh, sys.argv[1])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment