Pillow TiffTags 模块

下面给你一个 Pillow(PIL Fork)中 TiffTags 模块 的完整入门指南,包含 TIFF 图像元数据读取、写入、解析、分辨率、方向、压缩、批量处理、ICC 配置文件、地理 TIFF 等高级应用。
(基于 Pillow ≥ 9.0,Python 3.8+)


1. 安装 Pillow(支持 TIFF)

pip install --upgrade pillow

注意:Pillow 默认支持 TIFF,但某些功能(如 LZW 压缩)需要系统库 libtiff


2. 基本导入

from PIL import Image
from PIL.TiffTags import TAGS

3. TIFF 图像元数据(Tag)概述

TIFF 是一种多功能图像格式,支持:

特性说明
多页(Multi-page)一文件多图
高位深16/32 位
压缩LZW, ZIP, JPEG, PackBits
元数据分辨率、方向、相机信息、ICC
地理信息GeoTIFF(坐标、投影)

4. 读取所有 TIFF Tag

img = Image.open("image.tif")
for tag_id, value in img.tag.items():
    tag_name = TAGS.get(tag_id, tag_id)
    print(f"{tag_name} ({tag_id}): {value}")

常用 Tag ID:

  • 256: ImageWidth
  • 257: ImageLength
  • 258: BitsPerSample
  • 259: Compression
  • 282: XResolution
  • 283: YResolution
  • 296: ResolutionUnit
  • 274: Orientation
  • 34665: ExifIFD(EXIF)
  • 34737: ICCProfile

5. 解析常用字段

def get_tiff_info(img_path):
    img = Image.open(img_path)
    tag = img.tag

    info = {
        "尺寸": img.size,
        "模式": img.mode,
        "页数": getattr(img, "n_frames", 1),
        "分辨率": (
            tag.get(282),  # XResolution
            tag.get(283),  # YResolution
            tag.get(296, 2)  # ResolutionUnit (1=无单位, 2=英寸, 3=厘米)
        ),
        "方向": tag.get(274, 1),
        "压缩": tag.get(259, 1),
        "ICC配置文件": bool(tag.get(34675))
    }

    return info

# 使用
info = get_tiff_info("scan.tif")
for k, v in info.items():
    print(f"{k}: {v}")

6. 解析分辨率(DPI)

def get_dpi(img_path):
    img = Image.open(img_path)
    x_res = img.tag.get(282)  # XResolution
    y_res = img.tag.get(283)  # YResolution
    unit = img.tag.get(296, 2)  # ResolutionUnit

    if x_res and y_res and unit == 2:  # 英寸
        return (float(x_res[0][0])/x_res[0][1], float(y_res[0][0])/y_res[0][1])
    return None

dpi = get_dpi("scan.tif")
if dpi:
    print(f"DPI: {dpi[0]:.0f} x {dpi[1]:.0f}")

7. 读取多页 TIFF

def read_multipage_tiff(tiff_path):
    img = Image.open(tiff_path)
    pages = []

    try:
        i = 0
        while True:
            img.seek(i)
            page = img.copy()
            pages.append(page)
            i += 1
    except EOFError:
        pass  # 结束

    return pages

pages = read_multipage_tiff("document.tif")
print(f"共 {len(pages)} 页")
for i, page in enumerate(pages):
    page.save(f"page_{i+1}.png")

8. 写入 TIFF + 自定义 Tag

def save_tiff_with_tags(input_path, output_path, dpi=300, compression="tiff_lzw"):
    img = Image.open(input_path).convert("RGB")

    # 设置分辨率
    img.info["dpi"] = (dpi, dpi)

    # 自定义 Tag
    img.tag[270] = "Pillow 生成的 TIFF"  # ImageDescription
    img.tag[306] = "2025:04:05 12:00:00"   # DateTime
    img.tag[274] = 1                       # Orientation: TopLeft

    img.save(output_path, compression=compression, tiffinfo=img.tag)
    print(f"已保存: {output_path}")

save_tiff_with_tags("input.jpg", "output.tif")

9. 提取 ICC 配置文件

def extract_icc_profile(tiff_path, output_icc="profile.icc"):
    img = Image.open(tiff_path)
    icc = img.tag.get(34675)  # ICCProfile

    if icc:
        with open(output_icc, "wb") as f:
            f.write(icc[0])
        print(f"ICC 配置文件已提取: {output_icc}")
    else:
        print("无 ICC 配置文件")

extract_icc_profile("color.tif")

10. 批量处理:转换 + 压缩 + 添加 DPI

import os

def batch_convert_to_tiff(input_dir, output_dir, dpi=300):
    os.makedirs(output_dir, exist_ok=True)

    for fname in os.listdir(input_dir):
        if fname.lower().endswith(('.png', '.jpg', '.jpeg')):
            in_path = os.path.join(input_dir, fname)
            out_path = os.path.join(output_dir, os.path.splitext(fname)[0] + ".tif")

            img = Image.open(in_path).convert("RGB")
            img.info["dpi"] = (dpi, dpi)
            img.save(out_path, compression="tiff_lzw", dpi=(dpi, dpi))
            print(f"已转换: {out_path}")

batch_convert_to_tiff("images", "tiff_output")

11. GeoTIFF 支持(读取地理信息)

def get_geotiff_info(tiff_path):
    img = Image.open(tiff_path)
    tag = img.tag

    # GeoTIFF 常用 Tag
    geotags = {
        33550: "PixelScale",      # 像素尺寸 (x, y, z)
        33922: "TiePoints",       # 控制点
        34735: "GeoKeyDirectory", # 投影信息
        34737: "GeoAsciiParams"
    }

    geo_info = {}
    for tag_id, name in geotags.items():
        if tag_id in tag:
            geo_info[name] = tag[tag_id]

    return geo_info

# 使用
info = get_geotiff_info("map.tif")
for k, v in info.items():
    print(f"{k}: {v}")

12. 高级技巧

12.1 自动按 DPI 缩放打印

def print_size_from_dpi(tiff_path):
    dpi = get_dpi(tiff_path)
    if not dpi:
        return "未知 DPI"

    img = Image.open(tiff_path)
    width_inch = img.width / dpi[0]
    height_inch = img.height / dpi[1]
    return f"{width_inch:.2f} x {height_inch:.2f} 英寸"

print(print_size_from_dpi("scan.tif"))

12.2 合并多页为 PDF(借助 reportlab

pip install reportlab
from reportlab.pdfgen import canvas

def tiff_to_pdf(tiff_path, pdf_path):
    pages = read_multipage_tiff(tiff_path)
    c = canvas.Canvas(pdf_path, pagesize=pages[0].size)

    for page in pages:
        temp = "temp_page.png"
        page.save(temp)
        c.drawImage(temp, 0, 0)
        c.showPage()
    c.save()
    os.remove(temp)

tiff_to_pdf("doc.tif", "doc.pdf")

13. 常见问题 & 解决方案

问题原因解决
tag 为空非 TIFF 格式检查文件头
LZW 压缩失败系统无 libtiff安装 libtiff 或用 tiff_deflate
分辨率显示为 (1,1)未设置 DPIimg.info["dpi"] = (300,300)
多页 TIFF 只能读第一页未用 seek()使用 while True: img.seek(i)
ICC 丢失save() 未传 icc_profileimg.save(..., icc_profile=img.info.get("icc_profile"))

14. 官方文档

  • https://pillow.readthedocs.io/en/stable/reference/TiffTags.html
  • TIFF 规范:https://www.adobe.io/open/standards/TIFF.html

一键工具脚本:tiff_tool.py

#!/usr/bin/env python3
import argparse
from PIL import Image
from PIL.TiffTags import TAGS

def main():
    parser = argparse.ArgumentParser(description="TIFF 信息工具")
    parser.add_argument("tiff", help="TIFF 文件路径")
    parser.add_argument("-d", "--dpi", action="store_true", help="显示 DPI")
    parser.add_argument("-p", "--pages", action="store_true", help="显示页数")
    parser.add_argument("-i", "--icc", help="提取 ICC 到文件")

    args = parser.parse_args()
    img = Image.open(args.tiff)

    if args.icc:
        icc = img.tag.get(34675)
        if icc:
            with open(args.icc, "wb") as f:
                f.write(icc[0])
            print(f"ICC 已提取: {args.icc}")
        else:
            print("无 ICC")
        return

    print(f"文件: {args.tiff}")
    print(f"尺寸: {img.size}")
    print(f"模式: {img.mode}")
    if args.pages:
        try:
            pages = 0
            while True:
                img.seek(pages)
                pages += 1
            print(f"页数: {pages}")
        except:
            pass

    if args.dpi:
        dpi = get_dpi(args.tiff)
        print(f"DPI: {dpi[0] if dpi else '未知'} x {dpi[1] if dpi else '未知'}")

    print("\n主要 Tag:")
    for tag_id in [256, 257, 258, 259, 282, 283, 296, 274]:
        if tag_id in img.tag:
            name = TAGS.get(tag_id, tag_id)
            print(f"  {name} ({tag_id}): {img.tag[tag_id]}")

if __name__ == "__main__":
    main()

使用

python tiff_tool.py scan.tif -d -p
python tiff_tool.py color.tif -i profile.icc

总结:TiffTags 核心用法

功能代码
读取 Tagimg.tag[tag_id]
解析名称TAGS.get(tag_id)
设置 DPIimg.info["dpi"] = (300,300)
多页img.seek(i)
保存img.save(..., tiffinfo=img.tag)

需要我帮你实现 扫描仪自动处理、GeoTIFF 地图叠加、医学影像 DICOM 转 TIFF、批量压缩归档 等完整项目吗?直接说需求,我给你完整可运行代码!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注