Exif 信息的读取与删除(Pillow、Piexif、ExifTool)
我想要删除约百 G 图片中的一些信息,具体是:标题、主题、标记、备注、图像 ID。
虽然可以选中若干图片,属性 - 删除属性和个人信息,然后进行删除。
但是这种方式删除不了图像 ID,并且图片也不在同一个文件夹之中,因此还是要考虑写一个 Python 脚本来处理。
一番搜索后,考虑尝试的库有 Pillow、Piexif。
首先需要对 imghdr 打一个补丁,在文件遍历中显然是需要判断图片类型的,但是 imghdr 对 jpg 的识别有些问题。
imghdr / python - Can’t detec type of some images (image extension)
def test_jpeg1(h, f):
""" JPEG data in JFIF format """
if b'JFIF' in h[:23]:
return 'jpeg'
JPEG_MARK = b'\xff\xd8\xff\xdb\x00C\x00\x08\x06\x06' \
b'\x07\x06\x05\x08\x07\x07\x07\t\t\x08\n\x0c\x14\r\x0c\x0b\x0b\x0c\x19\x12\x13\x0f'
def test_jpeg2(h, f):
""" JPEG with small header """
if len(h) >= 32 and 67 == h[5] and h[:32] == JPEG_MARK:
return 'jpeg'
def test_jpeg3(h, f):
""" JPEG data in JFIF or Exif format """
if h[6:10] in (b'JFIF', b'Exif') or h[:2] == b'\xff\xd8':
return 'jpeg'
imghdr.tests.append(test_jpeg1)
imghdr.tests.append(test_jpeg2)
imghdr.tests.append(test_jpeg3)
此后使用的 imghdr 均为打过补丁的 imghdr。
这里补充一下我想要删除的 exif 信息对应的 tag 信息:
https://exiftool.org/TagNames/EXIF.html
tags 0x9c9b-0x9c9f are used by Windows Explorer;
special characters in these values are converted to UTF-8 by default, or Windows Latin1 with the -L option.
名称 | Tag ID | Tag Name | Values / Notes |
---|---|---|---|
标题 | 0x9c9b (40091) | XPTitle | ignored by Windows Explorer if ImageDescription exists |
备注 | 0x9c9c (40092) | XPComment | |
作者 | 0x9c9d (40093) | XPAuthor | ignored by Windows Explorer if Artist exists |
标记 | 0x9c9e (40094) | XPKeywords | |
主题 | 0x9c9f (40095) | XPSubject | |
作者 | 0x013b (315) | Artist | |
图像ID | 0xa420 (42016) | ImageUniqueID |
Pillow 读取 exif 信息简单示例。
def exif_pillow(dir_path):
"""
Pillow
https://github.com/python-pillow/Pillow
https://pillow.readthedocs.io/en/stable/?badge=latest
"""
for root, dirs, files in os.walk(dir_path):
for file in files:
img_path = os.path.join(root, file)
img_type = imghdr.what(img_path)
if img_type is None:
print(f'path = {img_path} is not graphic')
else:
print(f'img_path = {img_path}, img = {img_type}')
with Image.open(img_path) as img:
exif = img.getexif()
for key, val in exif.items():
if key in ExifTags.TAGS:
tag = ExifTags.TAGS[key]
if isinstance(val, bytes):
print(chardet.detect(val))
print(f'{key} - {tag}: {val}')
else:
print(f'{key} not in ExifTags.TAGS')
print('-------------------------------------------')
tag id 和 tag name 的对应关系是在字典 ExifTags.TAGS 中存储的,需要注意的是,有些 tag id 不存在于字典中。
另外在对应信息是 bytes 且内容存在中文时,编码上有些问题,不论是按照 UTF-8、GBK 或是 chardet 检测的编码来 encode,中文内容都无法正确获取。
Exif 实例可以当作字典一样去设值,然后通过 save 方法保存,但是在实际操作中会出现错误,或者保存后图片体积变化明显存在异常的情况。
Piexif 读取 exif 信息简单示例。
def exif_piexif(dir_path):
"""
Piexif
https://github.com/hMatoba/Piexif
https://piexif.readthedocs.io/en/latest/
https://piexif.readthedocs.io/en/latest/sample.html#with-pil-pillow
"""
for root, dirs, files in os.walk(dir_path):
for file in files:
img_path = os.path.join(root, file)
img_type = imghdr.what(img_path)
if img_type is None:
print(f'path = {img_path} is not graphic')
else:
print(f'img_path = {img_path}, img = {img_type}')
with Image.open(img_path) as img:
exif = piexif.load(img.info["exif"])
for tag, val in exif.items():
print(f'{tag}:')
if isinstance(val, dict):
for k, v in val.items():
if k in ExifTags.TAGS:
tag = ExifTags.TAGS[k]
print(f'{k} - {tag}: {v}')
else:
print(f'{k} not in ExifTags.TAGS')
else:
print(f'val: {val}')
print('-------------------------------------------')
piexif 自己的文档中就给出了结合 pillow 修改并保存 exif 信息的示例。
可以发现和 pillow 有些区别,pillow 是通过 PIL.Image.Image.getexif 获取 exif 实例。
而 piexif 的 load 返回的是字典,dump 返回的是 exif 的 bytes。
在实际保存中,还是会出现错误。
不过值得一提的是,piexif 配合 pillow 读取 exif 信息时,中文明显为 UTF-8 的编码形式。
piexif 的 remove 可以非常简单的移除 exif 信息,但是不包括标题(XPTitle)和标记(XPKeywords)这些 IPTC 信息。
综上,因为不满足我的需求或者遇到暂时无法解决的错误问题。我开始考虑使用 ExifTool。
ExifTool
项目地址:https://github.com/exiftool/exiftool
安装说明:https://exiftool.org/install.html
EXIF Tags:https://exiftool.org/TagNames/EXIF.html
程序文档:https://exiftool.org/exiftool_pod.html
这里仅简单介绍几个参数选项使用,全部选项请参照文档。
如果执行中遇到经典的 无法将“exiftool”项识别为 cmdlet、函数、脚本文件或可运行程序的名称 提示,那就认真看一遍安装说明。
- -r[.] (-recurse),递归处理子目录
- -g[NUM…] (-groupHeadings),按照标签组输出
- -P (-preserve),保留文件修改日期/时间
- -overwrite_original,直接覆盖,默认生成 _original 备份文件
- -TAG[+-^]=[VALUE],向标签写入新值
查看文件信息:
exiftool [file_path]
递归分组查看文件信息:
exiftool -g -r [dir_path]
递归清空指定信息,保留文件修改日期/时间,不生成备份文件:
exiftool -XPTitle= -XPComment= -XPKeywords= -XPAuthor= -XPSubject= -ImageUniqueID= -LastKeywordXMP= -LastKeywordIPTC= -ImageDescription= -Subject= -Title= -Description= -Keywords= -Artist= -overwrite_original -P -r [dir_path]
实际测试后可以发现 ExifTool 可以满足我的需求,操作简便,功能强大。
另外,ExifTool 也提供了 ExifToolGUI,如果使用,注意查看 Requirements and preparations 部分的说明。