Python图像元数据处理:pyexiv2库安装、使用与实战指南

📅 2026/7/9 16:58:58
Python图像元数据处理:pyexiv2库安装、使用与实战指南
1. 项目概述最近在做一个图片批量处理的小工具核心需求之一就是要读写图片的元数据比如EXIF、XMP这些。一开始我图省事直接用了Pillow库但很快就发现它功能太弱了很多专业相机写入的复杂标签要么读不出来要么写不进去特别是涉及到GPS坐标、镜头型号、版权信息这些稍微专业一点的字段时Pillow就显得力不从心。于是我开始寻找更专业的解决方案自然而然就找到了pyexiv2这个库。这个名字听起来就很“硬核”因为它本质上是C图像元数据库exiv2的Python绑定理论上应该能提供最完整、最底层的元数据操作能力。但说实话从决定用它到真正把它跑起来中间踩的坑可真不少。这库的安装和配置尤其是在Windows和macOS上远不像pip install pyexiv2一行命令那么简单。你会遇到各种依赖缺失、版本冲突、动态链接库找不到的问题。网上的资料又比较零散很多还是针对老版本比如那个2011年就停止更新的Python2绑定版pyexiv2的很容易让人走弯路。所以我决定把这次从环境搭建、基础使用到高级功能、疑难杂症排查的全过程记录下来形成这篇“问题解决笔记”。如果你也在用Python处理图片元数据并且对Pillow、PIL的局限性感到头疼那么这篇笔记应该能帮你省下不少折腾的时间。无论是摄影爱好者想批量整理照片信息还是开发者需要构建专业的图像资产管理工具pyexiv2都是一个值得深入研究的强大工具。2. 核心概念与工具选型解析在深入代码之前我们必须先理清几个关键概念这能帮你理解为什么选pyexiv2以及它和市面上其他方案的区别。2.1 图像元数据不仅仅是EXIF我们常说的“图片信息”或“EXIF信息”在专业领域统称为图像元数据。它就像一张图片的“身份证”和“履历表”记录了从拍摄到后期处理的完整信息流。主要分为以下几大标准EXIF (Exchangeable Image File Format)这是最广为人知的由相机嵌入。包含拍摄参数光圈、快门、ISO、焦距、相机型号、拍摄时间、甚至缩略图。对于摄影后期和照片管理至关重要。IPTC (International Press Telecommunications Council)新闻媒体行业标准侧重于描述性信息如标题、作者、版权、关键词、地点等。常用于图片库和版权管理。XMP (Extensible Metadata Platform)由Adobe推动的基于XML的开放标准非常灵活可以嵌入几乎所有类型的元数据并且能轻松与EXIF、IPTC互转。Lightroom、Photoshop等软件大量使用XMP来存储编辑历史、评分、标签等。ICC Profile (International Color Consortium)色彩配置文件确保图片在不同设备上显示的颜色一致。严格来说它属于色彩管理数据但也是图片文件的重要组成部分。很多Python库如Pillow只能处理EXIF的冰山一角对IPTC和XMP的支持非常有限或根本没有。而pyexiv2的背后是exiv2库这是一个久经沙场、功能完整的C库对上述所有标准都提供了原生、深度的支持。2.2 为什么是pyexiv2横向对比其他方案当你搜索“Python 读取 EXIF”时会冒出好几个库很容易混淆。这里我帮你梳理一下Pillow/PIL (Image.info[exif])优点安装简单无需额外依赖。适合读取基本的EXIF信息如日期、相机型号。缺点功能残缺。无法写入复杂的EXIF标签对IPTC、XMP几乎无支持解析某些厂商的自定义标签时可能出错。它更像一个“图像处理库附赠的元数据功能”。piexif优点纯Python实现专注于EXIF的读写比Pillow更准确。缺点同样只支持EXIF不支持IPTC和XMP。对于需要处理完整元数据生态的项目来说不够用。exifread优点同样是纯Python读取EXIF信息非常方便。缺点只读不写。如果你需要修改或写入元数据它就不适合了。pyexiv2 (旧版2011年停止维护)这是历史遗留项目仅支持Python 2。千万不要安装这个否则会和我们要用的新版冲突。py3exiv2 / python-exiv2这两个也是exiv2的Python绑定分别使用Boost.Python和SWIG进行封装。它们功能上可能与新版pyexiv2类似但活跃度和社区支持相对较弱。新版pyexiv2使用pybind11进行封装在易用性和现代Python特性支持上通常更好。pyexiv2 (新版本文主角)优点功能完整支持EXIF、IPTC、XMP、ICC Profile的读写基于强大的exiv2库稳定可靠支持Python 3.8跨平台Linux, macOS, WindowsAPI设计相对清晰。缺点安装稍复杂因为依赖C库exiv2有文件大小限制无法读写2GB的图片非线程安全。选型结论如果你的项目需要可靠、全面、可读写所有类型的图像元数据并且愿意在环境配置上花点功夫那么新版pyexiv2是目前Python生态下的最佳选择。它提供了最接近原生exiv2命令行工具的能力。2.3 新版pyexiv2的安装“陷阱”与正确姿势直接从PyPI执行pip install pyexiv2看起来很简单但这里隐藏着最大的坑。这个命令能成功的前提是你的系统已经正确安装了exiv2的共享库.so,.dylib,.dll。对于Linux用户通常可以通过包管理器如apt install libexiv2-dev轻松解决。但对于Windows和macOS用户事情就复杂了。Windows上的典型错误ImportError: DLL load failed while importing pyexiv2lib: 找不到指定的模块。macOS上的典型错误ImportError: dlopen(...): Library not loaded: rpath/libexiv2.xx.dylib这些错误的根源是pip安装的pyexiv2只是一个Python包装器wheel包里是编译好的Python扩展模块它运行时需要调用系统里的exiv2动态库。如果系统没有就会报错。解决方案实操步骤对于Windows用户最可靠的方法是使用conda安装。conda的强大之处在于它能管理二进制依赖。conda install -c conda-forge pyexiv2如果你坚持用pip需要手动下载exiv2的Windows二进制包。可以去exiv2的GitHub Release页面下载对应你系统架构通常是x86_64的zip包解压后将其bin目录添加到系统的PATH环境变量中。这个过程比较繁琐且容易遇到版本不匹配的问题。对于macOS用户同样首推conda安装conda install -c conda-forge pyexiv2。或者使用Homebrew先安装exiv2库然后再用pip安装pyexiv2。brew install exiv2 pip install pyexiv2注意使用Homebrew安装时要确保pip和brew的环境一致比如都是基于Apple Silicon或Intel。有时需要设置环境变量告诉编译器exiv2库的位置但用conda可以避免这些麻烦。对于Linux用户如Ubuntu/Debian这是最顺畅的。先安装系统依赖再pip安装即可。sudo apt update sudo apt install libexiv2-dev pip install pyexiv2我的踩坑心得在跨平台项目中我强烈推荐将pyexiv2的安装通过conda来管理并在项目文档中明确写明。这能极大降低团队协作和部署时的环境配置成本。如果你必须用pip那么一定要在README里详细说明exiv2库的手动安装步骤并把可能出现的错误和解决方案列出来。安装成功后可以通过一个简单的命令验证import pyexiv2 print(pyexiv2.__version__)如果没有报错恭喜你最艰难的一步已经过去了。3. 基础操作从打开图片到读写元数据环境搞定后我们来上手最常用的操作。pyexiv2提供了两种主要的API风格一种是面向对象的Image类另一种是更函数式的ImageMetadata类。我个人更推荐使用Image类它的接口更直观。3.1 打开图片与读取元数据假设我们有一张名为photo.jpg的图片。import pyexiv2 # 方法1使用Image类 (推荐) with pyexiv2.Image(photo.jpg) as img: # 读取所有EXIF数据 exif_data img.read_exif() # 读取所有IPTC数据 iptc_data img.read_iptc() # 读取所有XMP数据 xmp_data img.read_xmp() # 打印所有EXIF标签和值 for key, value in exif_data.items(): print(f{key}: {value}) # 访问单个已知的EXIF标签例如拍摄时间 # EXIF标签的格式通常是 Exif.Image.DateTime date_time exif_data.get(Exif.Image.DateTime) if date_time: print(f拍摄时间: {date_time}) # 访问IPTC标签例如关键词 keywords iptc_data.get(Iptc.Application2.Keywords) if keywords: # IPTC的Keywords可能是一个列表 print(f关键词: {keywords})几点重要说明使用上下文管理器 (with语句)这能确保图片文件被正确关闭释放资源是非常好的实践。标签命名空间注意EXIF、IPTC、XMP的标签键key都有固定的前缀和结构如Exif.Image.Make相机品牌、Iptc.Application2.Caption图片说明、Xmp.dc.subjectXMP主题。不熟悉的话可以先全部打印出来看看。数据类型读取出来的值都是字符串。即使原始数据是数字、分数如曝光时间1/500或坐标pyexiv2也会将其转换为可读的字符串格式。3.2 修改与写入元数据写入操作同样直观。你可以修改读取出来的字典然后写回文件。with pyexiv2.Image(photo.jpg) as img: # 1. 修改EXIF数据 exif_dict img.read_exif() # 修改拍摄时间 (格式必须为 YYYY:MM:DD HH:MM:SS) exif_dict[Exif.Image.DateTime] 2023:10:27 15:30:00 # 添加或修改版权信息 exif_dict[Exif.Image.Copyright] Copyright (c) 2023 Your Name - All Rights Reserved # 将修改后的字典写回图片 img.modify_exif(exif_dict) # 2. 修改IPTC数据 iptc_dict img.read_iptc() iptc_dict[Iptc.Application2.Keywords] [旅行, 风景, 秋天] # Keywords是列表 iptc_dict[Iptc.Application2.Caption] 这是一张美丽的秋日风景照片。 img.modify_iptc(iptc_dict) # 3. 修改XMP数据 xmp_dict img.read_xmp() xmp_dict[Xmp.dc.creator] [摄影师张三] # creator也可以是列表 xmp_dict[Xmp.dc.description] 使用pyexiv2库添加的XMP描述。 img.modify_xmp(xmp_dict) print(元数据已更新。)关键注意事项modify_*方法会覆盖整个对应的元数据块。如果你只想修改其中几个标签正确做法是先read然后在得到的字典上修改或添加键值对最后再modify回去。不要直接创建一个新的、不完整的字典去modify那会丢失其他已有的元数据。IPTC和XMP的数组值像Keywords、Creator这类允许多个值的字段在IPTC和XMP中通常以列表形式传递。这在写入时需要特别注意。写入后立即生效modify操作会直接修改磁盘上的图片文件。强烈建议在修改前备份原图尤其是在进行批量操作时。3.3 读取ICC Profile与预览图除了标准元数据pyexiv2还能处理一些高级数据。with pyexiv2.Image(photo.jpg) as img: # 读取ICC色彩配置文件 icc_profile img.read_icc() if icc_profile: print(fICC Profile长度: {len(icc_profile)} bytes) # icc_profile是bytes对象可以写入到.icc文件 # with open(profile_from_image.icc, wb) as f: # f.write(icc_profile) # 读取EXIF内嵌的预览图 (如果有的话通常是缩略图) previews img.read_previews() for i, preview in enumerate(previews): print(f预览图 {i}: 尺寸{preview.width}x{preview.height}, 大小{preview.size} bytes) # preview.data 是预览图的二进制数据 (通常是JPEG) # 可以保存下来preview.data.write_to_file(fpreview_{i}.jpg)4. 高级应用与实战技巧掌握了基础读写我们来看看pyexiv2在真实项目里能玩出什么花样。4.1 批量处理与自动化这是pyexiv2最常用的场景之一。例如为某个文件夹下所有图片统一添加版权信息。import os from pathlib import Path import pyexiv2 def batch_add_copyright(directory, copyright_text): 为指定目录下所有支持的图片文件添加版权信息。 image_extensions (.jpg, .jpeg, .png, .tiff, .tif, .webp) path Path(directory) for image_path in path.rglob(*): if image_path.suffix.lower() in image_extensions: try: with pyexiv2.Image(str(image_path)) as img: exif_data img.read_exif() # 只添加如果已存在则覆盖 exif_data[Exif.Image.Copyright] copyright_text img.modify_exif(exif_data) print(f已处理: {image_path}) except Exception as e: # 捕获并记录错误避免单个文件失败导致整个任务中断 print(f处理失败 {image_path}: {e}) # 使用示例 batch_add_copyright(/path/to/your/photos, Copyright (c) 2023 My Photography)实战技巧异常处理很重要图片文件可能损坏或者格式不被支持。用try...except包裹核心操作确保一个文件的错误不会影响批量任务。使用pathlibpathlib模块提供了更面向对象、更清晰的路径操作方式比传统的os.path更好用。先测试再批量写一个只处理一两张图片的测试函数确认逻辑正确后再跑全量任务。4.2 GPS坐标的读写与转换GPS信息是EXIF里非常有用但又有点特殊的数据。它通常以“度分秒”格式存储但我们需要的是十进制的经纬度以便在地图上使用。import pyexiv2 def get_gps_decimal(image_path): 从图片中读取GPS信息并转换为十进制格式。 with pyexiv2.Image(image_path) as img: exif img.read_exif() gps_lat exif.get(Exif.GPSInfo.GPSLatitude) gps_lat_ref exif.get(Exif.GPSInfo.GPSLatitudeRef) # N or S gps_lon exif.get(Exif.GPSInfo.GPSLongitude) gps_lon_ref exif.get(Exif.GPSInfo.GPSLongitudeRef) # E or W if not all([gps_lat, gps_lat_ref, gps_lon, gps_lon_ref]): return None # pyexiv2返回的GPS坐标格式通常是 DD/1 MM/1 SS/1 (度/分/秒) # 例如38/1 53/1 4783/100 表示 38度53分47.83秒 def dms_to_decimal(dms_str, ref): 将DD/1 MM/1 SS/1字符串转换为十进制度数。 parts dms_str.split() if len(parts) ! 3: return None degrees float(parts[0].split(/)[0]) / float(parts[0].split(/)[1]) if / in parts[0] else float(parts[0]) minutes float(parts[1].split(/)[0]) / float(parts[1].split(/)[1]) if / in parts[1] else float(parts[1]) seconds float(parts[2].split(/)[0]) / float(parts[2].split(/)[1]) if / in parts[2] else float(parts[2]) decimal degrees minutes / 60.0 seconds / 3600.0 # 处理南纬和西经为负数 if ref in [S, W]: decimal -decimal return decimal lat_decimal dms_to_decimal(gps_lat, gps_lat_ref) lon_decimal dms_to_decimal(gps_lon, gps_lon_ref) return (lat_decimal, lon_decimal) def set_gps_decimal(image_path, lat_decimal, lon_decimal): 将十进制经纬度写入图片的GPS EXIF标签。 def decimal_to_dms(decimal): 将十进制度数转换为度分秒元组 (度, 分, 秒)。 degrees int(abs(decimal)) minutes_full (abs(decimal) - degrees) * 60 minutes int(minutes_full) seconds (minutes_full - minutes) * 60 # 返回pyexiv2需要的格式每个值都是一个有理数 (分子/分母) # 通常分母为1秒可能需要更高精度 return (f{degrees}/1, f{minutes}/1, f{round(seconds*100)}/100) # 秒保留两位小数 lat_dms decimal_to_dms(lat_decimal) lon_dms decimal_to_dms(lon_decimal) lat_ref N if lat_decimal 0 else S lon_ref E if lon_decimal 0 else W with pyexiv2.Image(image_path) as img: exif_dict img.read_exif() exif_dict[Exif.GPSInfo.GPSLatitude] .join(lat_dms) exif_dict[Exif.GPSInfo.GPSLatitudeRef] lat_ref exif_dict[Exif.GPSInfo.GPSLongitude] .join(lon_dms) exif_dict[Exif.GPSInfo.GPSLongitudeRef] lon_ref # 还可以设置GPS时间戳、海拔等 img.modify_exif(exif_dict) # 使用示例 coords get_gps_decimal(photo_with_gps.jpg) if coords: print(f经纬度: {coords}) # set_gps_decimal(photo.jpg, 39.9042, 116.4074) # 设置北京坐标为什么这么麻烦因为EXIF标准规定GPS坐标必须以“有理数”格式存储度、分、秒。pyexiv2忠实于这个标准所以我们需要进行转换。这个函数是处理GPS数据的核心工具建议保存下来复用。4.3 从二进制数据Bytes直接操作元数据有时我们的图片不是来自文件而是来自网络请求或数据库的二进制流。pyexiv2支持直接从bytes对象创建Image对象。import requests import pyexiv2 # 从网络下载图片 url https://example.com/photo.jpg response requests.get(url) image_data response.content # 关键使用 pyexiv2.Image.from_bytes with pyexiv2.Image.from_bytes(image_data) as img: exif img.read_exif() print(f远程图片的相机型号: {exif.get(Exif.Image.Model, 未知)}) # 甚至可以修改后再获取修改后的二进制数据 exif[Exif.Image.ImageDescription] Downloaded and processed by pyexiv2 img.modify_exif(exif) # 获取修改后的图片二进制数据 modified_image_data img.get_bytes() # 现在可以将 modified_image_data 保存到文件或上传到别处这个功能非常强大它意味着你可以在不创建临时文件的情况下完成对图片元数据的流水线处理非常适合构建Web服务或数据处理管道。5. 常见问题与排查技巧实录即使安装成功在实际使用中你依然会遇到各种奇怪的问题。下面是我踩过的一些坑和解决办法。5.1 导入错误与库加载失败这是最常见的一类问题根本原因都是系统找不到exiv2的动态链接库。错误信息ImportError: DLL load failed while importing pyexiv2lib: The specified module could not be found.(Windows) 或Library not loaded: rpath/libexiv2.xx.dylib(macOS)。排查步骤确认exiv2已安装在终端运行exiv2 --versionLinux/macOS或检查Windows上是否安装了exiv2。如果命令不存在说明库没装。检查库路径Linux/macOS使用lddLinux或otool -LmacOS检查pyexiv2lib模块依赖的库路径。例如找到pyexiv2lib的路径在Python中print(pyexiv2.__file__)然后执行ldd /path/to/pyexiv2lib.so | grep exiv2。看它指向的库文件是否存在。Windows使用Dependency Walker或Process Monitor工具查看加载失败的具体是哪个DLL。环境变量确保exiv2的安装目录包含libexiv2.dll的目录在系统的PATH环境变量中Windows或在LD_LIBRARY_PATHLinux或DYLD_LIBRARY_PATHmacOS中。终极解决方案如前所述使用conda安装。conda会确保所有依赖包括exiv2库被正确安装和链接。5.2 文件大小限制与线程安全pyexiv2的官方文档明确指出了两个缺陷文件大小限制无法读取大于2GB的图片无法修改大于1GB的图片。这是底层exiv2库在某些平台上的限制。应对策略在处理图片前先检查文件大小。对于超大文件考虑使用其他命令行工具如exiv2命令本身或先进行格式转换/压缩。import os file_size os.path.getsize(huge_image.cr2) if file_size 1 * 1024**3: # 1GB print(警告文件超过1GB修改元数据可能失败。)非线程安全因为内部使用了C全局变量所以不要在多个线程中同时操作不同的Image对象。应对策略使用线程锁threading.Lock来确保同一时间只有一个线程在执行pyexiv2操作或者使用多进程multiprocessing来代替多线程。更简单的做法是在Web服务器等并发环境中将元数据处理任务放入队列由单个工作线程顺序处理。5.3 编码与特殊字符问题当元数据或文件路径中包含非ASCII字符如中文、日文、emoji时可能会遇到编码错误。问题写入包含中文的标签后在某些软件中显示为乱码。原因与解决pyexiv2内部使用UTF-8编码。确保你的Python脚本文件本身以UTF-8保存并且在操作字符串时明确使用UTF-8。在Python文件开头加# -*- coding: utf-8 -*-。在写入字符串时确保它是str类型Python 3默认就是UTF-8。如果从其他源如GBK编码的数据库读取数据需要先正确解码和转码。# 假设从GBK编码的数据库读取了一个标题 title_gbk b\xd6\xd0\xce\xc4\xb1\xea\xcc\xe2 # “中文标题”的GBK字节 title_utf8 title_gbk.decode(gbk) # 先解码为Unicode字符串 # 现在title_utf8是一个Python str可以安全写入 with pyexiv2.Image(photo.jpg) as img: exif img.read_exif() exif[Exif.Image.ImageDescription] title_utf8 img.modify_exif(exif)5.4 特定标签读写失败有些EXIF标签是只读的或者有严格的格式要求。错误修改了Exif.Photo.PixelXDimension这类标签但无效。原因这类标签是描述图片核心属性的如像素尺寸通常由相机或图像处理软件自动生成不应手动修改。即使写入了其他软件也可能忽略。建议只修改描述性元数据如版权、描述、关键词、GPS、艺术家等避免修改技术性元数据如尺寸、压缩模式、光圈值等。修改前最好查阅EXIF标准或使用exiv2命令行工具exiv2 -pt image.jpg查看哪些标签是可写的。5.5 性能优化与批量处理建议处理成千上万张图片时性能很重要。避免重复打开关闭虽然with语句是好的但在极端性能要求下对于批量处理可以考虑手动控制。但要注意异常处理和资源释放。缓存操作如果要对大量图片进行相同的修改如添加同一版权信息准备好要写入的字典在循环内直接使用modify而不是每次都read再modify除非你需要保留原有其他标签。使用多进程由于GIL和库本身非线程安全CPU密集型的批量处理更适合用multiprocessing.Pool来并行处理每个进程处理一部分文件。from multiprocessing import Pool import pyexiv2 def process_single_file(filepath): try: with pyexiv2.Image(filepath) as img: # ... 你的处理逻辑 ... img.modify_exif(your_exif_dict) return (filepath, True) except Exception as e: return (filepath, False, str(e)) if __name__ __main__: all_image_paths [...] # 你的图片路径列表 with Pool(processes4) as pool: # 使用4个进程 results pool.map(process_single_file, all_image_paths) for result in results: print(result)6. 调试与辅助工具推荐工欲善其事必先利其器。除了pyexiv2还有一些工具能极大提升你处理元数据的效率。6.1 exiv2 命令行工具这是pyexiv2的底层库的命令行版本是调试和学习的终极利器。安装pyexiv2时通常也会安装这个工具通过conda或系统包管理器。查看所有元数据人类可读格式exiv2 -pt image.jpg-p指定输出格式t表示“打印标签”。这个命令会以清晰的格式列出EXIF、IPTC、XMP的所有标签比在Python里打印字典直观得多。查看特定元数据exiv2 -Pkt image.jpg # 只打印EXIF标签 exiv2 -Pit image.jpg # 只打印IPTC标签 exiv2 -PXt image.jpg # 只打印XMP标签修改元数据exiv2 -Mset Exif.Image.Copyright Copyright 2023 Me image.jpg exiv2 -Madd Iptc.Application2.Keywords Travel -Madd Iptc.Application2.Keywords Nature image.jpg-M选项允许你通过命令行直接修改。这在写脚本或快速测试时非常方便。删除元数据exiv2 -d a image.jpg # 删除所有元数据 exiv2 -d e image.jpg # 只删除EXIF数据当你用pyexiv2写出的标签在其他软件中显示不正常时先用exiv2 -pt命令看看原始数据到底是什么能快速定位是写入格式问题还是软件读取兼容性问题。6.2 可视化元数据查看器对于不熟悉命令行标签名的开发者图形化工具能帮助你快速浏览和理解数据结构。exiftool (跨平台)这是另一个功能极其强大的元数据命令行工具有时比exiv2支持更多相机型号的特殊标签。它的GUI版本叫exiftoolgui。Jeffrey’s Image Metadata Viewer (在线)一个优秀的在线工具上传图片即可查看所有元数据并且交互式地展示GPS位置。操作系统内置macOS的“预览”应用和Windows的“文件属性”详情页都能查看基本EXIF信息。6.3 编写健壮代码的模板最后分享一个我常用的、包含了错误处理、日志记录和进度显示的批量处理模板你可以以此为起点进行修改import pyexiv2 from pathlib import Path import logging from tqdm import tqdm # 进度条库需要安装pip install tqdm logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) logger logging.getLogger(__name__) def safe_process_image(image_path, process_func): 安全地处理单张图片封装异常处理。 try: with pyexiv2.Image(str(image_path)) as img: process_func(img) # 传入img对象由外部函数定义具体处理逻辑 return True, None except pyexiv2.ImageError as e: logger.error(f图片格式错误或损坏 [{image_path}]: {e}) return False, str(e) except Exception as e: logger.error(f处理图片时发生未知错误 [{image_path}]: {e}, exc_infoTrue) return False, str(e) def my_processing_logic(img): 定义你的具体元数据处理逻辑。 # 示例添加一个统一的XMP标识 xmp img.read_xmp() xmp[Xmp.dc.description] Processed by my automated pipeline. img.modify_xmp(xmp) # 可以在这里添加更多的EXIF/IPTC操作... def batch_process_directory(input_dir, output_dirNone, extensions(.jpg, .jpeg, .tiff, .png)): 批量处理目录中的图片。 :param input_dir: 输入目录 :param output_dir: 输出目录如果为None则原地修改 :param extensions: 要处理的图片后缀元组 input_path Path(input_dir) if not input_path.exists(): logger.error(f输入目录不存在: {input_dir}) return image_files [] for ext in extensions: image_files.extend(input_path.rglob(f*{ext})) image_files.extend(input_path.rglob(f*{ext.upper()})) logger.info(f找到 {len(image_files)} 张待处理图片。) success_count 0 fail_count 0 # 使用tqdm显示进度条 for img_path in tqdm(image_files, desc处理图片): if output_dir: # 如果需要输出到新目录先复制文件这里简化处理实际可能需要保持目录结构 output_path Path(output_dir) / img_path.relative_to(input_path) output_path.parent.mkdir(parentsTrue, exist_okTrue) # 注意这里简单复制原图不会被修改。处理函数需要能接受一个“目标路径”参数。 # 更复杂的逻辑需要调整safe_process_image函数。 pass # 原地修改 success, error safe_process_image(img_path, my_processing_logic) if success: success_count 1 else: fail_count 1 # 可以将失败路径记录到文件 with open(failed_files.log, a) as f: f.write(f{img_path}: {error}\n) logger.info(f处理完成。成功: {success_count}, 失败: {fail_count}) if __name__ __main__: # 使用示例 batch_process_directory(/path/to/input/photos)这个模板提供了日志记录、完整的异常捕获、进度反馈并且将核心处理逻辑分离使得代码更清晰、更易维护和测试。记住处理生产环境的图片数据时一定要先备份并在小样本上充分测试后再全量运行。pyexiv2很强大但一旦误操作覆盖了重要元数据恢复起来会很麻烦。希望这篇详尽的笔记能让你在Python图像元数据处理的道路上少走弯路。