TIFF: Tagged Image File Format — The Gold Standard for Professional Imaging
TIFF (Tagged Image File Format) is the professional raster image format trusted by photographers, publishers, medical imaging systems, and archivists worldwide. While JPEG has become dominant for web delivery and consumer photography, TIFF remains the unchallenged standard wherever image quality, bit depth, and metadata richness cannot be compromised. Understanding TIFF's architecture reveals why it has survived and thrived for over 35 years.
What Is TIFF?
TIFF was developed by Aldus Corporation (later acquired by Adobe) and first published in 1986. Version 6.0 (1992) is the current stable specification; TIFF/EP and BigTIFF are modern extensions. The format was designed from the start to be flexible and extensible — a "container for containers" approach that has allowed it to absorb decades of new capabilities without breaking backward compatibility.
TIFF's core design principles:
- Tag-based structure: All metadata and technical parameters stored as typed key-value pairs (tags)
- Multiple subfile support: A single TIFF can contain multiple images (pages, pyramid levels, thumbnails)
- Codec-agnostic: The image data can be compressed with LZW, ZIP/Deflate, JPEG, PackBits, or stored raw
- Flexible color models: RGB, CMYK, Lab*, YCbCr, grayscale, bilevel, multi-channel (N channels)
- Any bit depth: 1, 4, 8, 16, 32, 64 bits per sample; integer or floating-point
TIFF File Structure: IFD Architecture
A TIFF file begins with an 8-byte header:
TIFF File Layout
├── Header (8 bytes)
│ ├── Byte order mark: 'II' (little-endian Intel) or 'MM' (big-endian Motorola)
│ ├── Magic number: 42 (0x002A) — always 42
│ └── Offset to first IFD (4 bytes)
├── Image File Directory 0 (IFD0) — First image
│ ├── Entry count (2 bytes)
│ ├── Directory entries (12 bytes each)
│ │ ├── Tag ID (2 bytes) — e.g., 256 = ImageWidth
│ │ ├── Data type (2 bytes) — BYTE/SHORT/LONG/RATIONAL/ASCII/etc.
│ │ ├── Count (4 bytes)
│ │ └── Value/Offset (4 bytes)
│ └── Next IFD offset (4 bytes, 0 = last IFD)
├── Image File Directory 1 (optional — thumbnail, second page)
├── [Additional IFDs...]
└── Image data blocks (referenced by StripOffsets or TileOffsets)
The tag-based IFD structure is TIFF's most important design feature. Any reader that encounters an unknown tag ID simply ignores it — this is why TIFF files from 1990 still open in 2024 software.
Essential TIFF Tags
| Tag ID | Name | Type | Description |
|---|---|---|---|
| 256 | ImageWidth | SHORT/LONG | Width in pixels |
| 257 | ImageLength | SHORT/LONG | Height in pixels |
| 258 | BitsPerSample | SHORT | Bits per channel (8, 16, 32…) |
| 259 | Compression | SHORT | 1=None, 5=LZW, 6=JPEG, 8=Deflate, 32773=PackBits |
| 262 | PhotometricInterpretation | SHORT | 2=RGB, 6=YCbCr, 5=CMYK, 8=CIE Lab* |
| 273 | StripOffsets | LONG | Byte offsets to image data strips |
| 278 | RowsPerStrip | SHORT/LONG | Number of rows in each strip |
| 279 | StripByteCounts | LONG | Bytes per compressed strip |
| 284 | PlanarConfiguration | SHORT | 1=Chunky (RGBRGB…) 2=Planar (RRR…GGG…BBB…) |
| 317 | Predictor | SHORT | 1=None, 2=Horizontal differencing (improves LZW) |
| 320 | ColorMap | SHORT | Palette for 8-bit indexed images |
| 322 | TileWidth | SHORT/LONG | Tile width for tiled TIFF |
| 323 | TileLength | SHORT/LONG | Tile height |
| 324 | TileOffsets | LONG | Byte offsets to image tiles |
| 325 | TileByteCounts | LONG | Bytes per compressed tile |
| 330 | SubIFDs | LONG | Offsets to sub-image IFDs (pyramid levels) |
| 34675 | ICCProfile | UNDEFINED | Embedded ICC color profile |
| 34737 | GeoKeyDirectoryTag | SHORT | GeoTIFF coordinate system |
Strips vs Tiles
TIFF supports two image data layouts:
Strip layout: Image data divided into horizontal strips. Strip height (RowsPerStrip) is specified at the tag level. Good for sequential top-to-bottom access.
Tile layout: Image data divided into rectangular tiles (typically 256×256 or 512×512 pixels). Required for very large images where random access is needed without reading the entire file. GeoTIFF and medical imaging use tiled TIFF extensively.
from PIL import Image
import struct
def inspect_tiff(filepath):
"""Read key TIFF tags from a file."""
with open(filepath, 'rb') as f:
# Read byte order
byte_order = f.read(2)
endian = '<' if byte_order == b'II' else '>'
# Magic number and IFD offset
magic = struct.unpack(f'{endian}H', f.read(2))[0]
ifd_offset = struct.unpack(f'{endian}I', f.read(4))[0]
# Seek to first IFD
f.seek(ifd_offset)
num_entries = struct.unpack(f'{endian}H', f.read(2))[0]
tags = {}
for _ in range(num_entries):
tag_id = struct.unpack(f'{endian}H', f.read(2))[0]
dtype = struct.unpack(f'{endian}H', f.read(2))[0]
count = struct.unpack(f'{endian}I', f.read(4))[0]
value_or_offset = f.read(4)
if dtype == 3 and count == 1: # SHORT, single value
value = struct.unpack(f'{endian}H', value_or_offset[:2])[0]
tags[tag_id] = value
elif dtype == 4 and count == 1: # LONG, single value
value = struct.unpack(f'{endian}I', value_or_offset)[0]
tags[tag_id] = value
print(f"Width: {tags.get(256, '?')}")
print(f"Height: {tags.get(257, '?')}")
print(f"BitsPerSample: {tags.get(258, '?')}")
print(f"Compression: {tags.get(259, '?')}")
print(f"Photometric: {tags.get(262, '?')}")
inspect_tiff('image.tiff')
Working with TIFF in Python (Pillow + tifffile)
from PIL import Image
import tifffile
import numpy as np
# Open and convert TIFF
with Image.open('scan.tiff') as img:
print(f"Mode: {img.mode}, Size: {img.size}")
print(f"Info: {img.tag_v2}") # All TIFF tags as dict
# Convert 16-bit TIFF to 8-bit JPEG
if img.mode == 'I;16' or img.mode == 'I':
# Scale 16-bit to 8-bit
arr = np.array(img)
arr_8bit = (arr / 256).astype(np.uint8)
Image.fromarray(arr_8bit).save('output.jpg', 'JPEG', quality=92)
else:
img.convert('RGB').save('output.jpg', 'JPEG', quality=92)
# Multi-page TIFF (e.g., scanned document)
with Image.open('multipage.tiff') as img:
pages = []
for i in range(img.n_frames):
img.seek(i)
pages.append(img.copy())
print(f"Pages: {len(pages)}")
# Save each page as individual JPEG
for i, page in enumerate(pages):
page.convert('RGB').save(f'page_{i+1:03d}.jpg', 'JPEG', quality=95)
# High-bit-depth TIFF with tifffile
with tifffile.TiffFile('raw_16bit.tiff') as tif:
arr = tif.asarray() # NumPy array
print(f"Shape: {arr.shape}, dtype: {arr.dtype}")
# arr.shape = (height, width, channels) or (height, width) for grayscale
# Write 16-bit TIFF with embedded ICC profile
with open('AdobeRGB1998.icc', 'rb') as f:
icc_profile = f.read()
img_16 = Image.fromarray(arr.astype(np.uint16), mode='I;16')
img_16.save('output_16bit.tiff',
compression='tiff_lzw',
icc_profile=icc_profile)
# Create multi-page TIFF from images
images = [Image.open(f) for f in ['p1.jpg', 'p2.jpg', 'p3.jpg']]
images[0].save('multipage.tiff',
save_all=True,
append_images=images[1:],
compression='tiff_lzw')
TIFF Compression Options
| Compression | Tag Value | Ratio | Speed | Best For |
|---|---|---|---|---|
| None | 1 | 1:1 | Fastest | Maximum compat., archival |
| PackBits | 32773 | ~2:1 | Fast | Bilevel, simple graphics |
| LZW | 5 | ~3:1 | Fast | General, most common lossless |
| Deflate/ZIP | 8 | ~4:1 | Medium | Best lossless, smaller than LZW |
| JPEG | 6 | ~15:1 | Fast | Lossy — photos in TIFF container |
| LERC | 34887 | ~6:1 | Medium | Geospatial/scientific |
LZW with Predictor=2 (horizontal differencing) significantly improves compression ratio for photographic images by encoding the difference between adjacent pixels rather than raw values.
TIFF in Professional Workflows
Flatbed scanner output: TIFF is the default output format for most professional scanners. 16-bit grayscale or 48-bit RGB captures more dynamic range than 8-bit, enabling better shadow/highlight recovery in post-processing.
Print production: TIFF with CMYK color space, 8 or 16 bits per channel, at 300 DPI is the standard for offset printing. Layers are flattened before export. Embedded ICC profiles ensure color consistency between monitor and press.
Digital pathology: Whole slide imaging (WSI) uses pyramid TIFF or TIFF-based formats like SVS (Aperio), SCN (Leica), and NDPI (Hamamatsu) for multi-gigapixel slide images. The pyramid structure stores multiple resolution levels for efficient zooming.
Photography RAW workflow: CameraRAW → 16-bit ProPhoto RGB TIFF → non-destructive edits → 8-bit sRGB JPEG for delivery. The 16-bit TIFF is the master; all retouching happens at 16-bit depth.
FFmpeg and TIFF
# Convert JPEG to TIFF (lossless)
ffmpeg -i input.jpg -vf "format=rgb24" output.tiff
# Extract video frame as 16-bit TIFF
ffmpeg -i video.mp4 -ss 00:01:00 -vframes 1 -vf "format=rgb48" frame.tiff
# Convert TIFF sequence to video
ffmpeg -framerate 24 -i frame_%04d.tiff -c:v libx264 -crf 18 output.mp4
# Compress TIFF with LZW via ImageMagick (not FFmpeg)
convert input.tiff -compress LZW output.tiff
convert input.tiff -compress Zip -quality 90 output.tiff
TIFF vs JPEG vs PNG vs RAW
| Feature | TIFF | JPEG | PNG | Camera RAW |
|---|---|---|---|---|
| Compression | Lossless (LZW/ZIP) or Lossy (JPEG-in-TIFF) | Lossy | Lossless | Lossless (proprietary) |
| Bit depth | 1–64 bits | 8-bit | 8/16-bit | 12–16 bit |
| Color spaces | RGB, CMYK, Lab, YCbCr, multi-channel | YCbCr only | RGB/RGBA | Camera-specific |
| Layers/pages | Multiple IFDs | No | No | No |
| Metadata | Extensive (XMP, IPTC, EXIF, ICC) | EXIF/XMP | Limited | Extensive |
| Max file size | 4 GB (classic) / 18 EB (BigTIFF) | ~65 MB practical | ~1 GB | Varies |
| Professional use | Print, archival, medical | Web delivery | Web, icons | Camera acquisition |
| File size (typical) | Large (3–50 MB/image) | Small (0.1–5 MB) | Medium (0.5–10 MB) | Medium (5–50 MB) |
Summary
TIFF's enduring dominance in professional imaging stems from its foundational design choices: tag-based extensibility, multi-image support, codec flexibility, and arbitrary bit depth. Whether you are archiving 16-bit scans, preparing CMYK artwork for print, or building a medical imaging pipeline, TIFF provides the fidelity and metadata richness that consumer formats cannot match. For web delivery, convert to JPEG or WebP; for professional archival and editing masters, TIFF remains the gold standard.
Related conversions
Most teams that read this guide convert images in one of these directions: