关于 photo-metadata
photo-metadata 是一项技能,用于在照片文件中嵌入并检查编辑、无障碍、版权和图像来源元数据。它介绍 ExifTool 工作流程、审慎撰写说明、移除 GPS,以及 C2PA 处理的限制。
主要功能
- 01写入 EXIF、IPTC 和 XMP 元数据
- 02添加说明并分别编写替代文本
- 03标记 AI 生成或编辑的图像
- 04从发布副本中移除 GPS
- 05读取 C2PA 清单但不签名
- 06写入后核验元数据
适用场景
- 01为新闻机构准备新闻照片
- 02添加摄影者署名和再利用许可
- 03披露图像由 AI 生成或编辑
- 04分享前移除敏感位置数据
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jamditis/ claude-skills-journalism --skill photo-metadata安装后在 Claude Code 中输入/photo-metadata确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
embed.pyreference.mdtest_embed.pyagents/openai.yaml详细介绍
照片元数据
概览
嵌入图像文件的元数据会随文件一起传输。照片内容管理系统(Photo Mechanic、Lightroom、Capture One)和通讯社收稿系统会从照片的 IPTC 和 XMP 区块读取说明文字、署名和权利信息,而不是从单独的文档或收到照片的电子邮件中读取。如果说明文字、署名、替代文本和许可信息不在文件内部,那么照片一经下载、转发或重新上传,这些信息就会消失。
一次 exiftool 操作即可同时写入 EXIF、IPTC 和 XMP 层,并且不会改动其他任何标签(相机设置、拍摄时间)。现代软件会优先读取 XMP,其次是旧版 IPTC-IIM;EXIF 仅用于日期和 GPS 信息,因此应始终写入 XMP,并在 JPEG/TIFF 中额外写入 IIM 作为兼容副本(HEIC、AVIF 和 WebP 都没有 IIM 存储位置;请参见 reference.md)。
自从这套工作流还只是“说明文字、署名、版权”以来,有两件事发生了变化。第一,图像的制作方式如今也应写入元数据:IPTC 的数字来源类型字段会标记照片是由相机拍摄,还是由 AI 生成,Meta、Google 等平台以及欧盟《人工智能法案》也越来越多地读取这一字段。第二,**加密来源凭证(C2PA /“内容凭证”)**正在进入通讯社图片和相机;exiftool 可以读取这一签名层,但无法写入。下文将介绍这两者。
**能力出众的模型已经知道这些字段名称。**难点不在操作方法,而在下文的判断。请优先说明这一点。
适用场景
- 为通讯社准备新闻照片,让合作新闻编辑室能够搜索、署名并转载
- 发布或分享照片前,添加必需的摄影师署名和再使用许可
- 标注图像的制作方式:未经修改的照片、AI 生成的插图、经 AI 编辑的合成图
- 为一组拍摄作品批量添加标签(一个图像文件夹)
- 让图像更易于访问(嵌入替代文本),并明确权利状态(版权或知识共享许可)
- 读取并检查来自机构的图像所含的 C2PA 内容凭证是否合理
**不适用场景:**编辑像素(这里只处理元数据);为 HTML <img> 编写替代文本(请使用 accessibility-compliance);将网页存档以作为证据(请使用 web-archiving);签署内容凭证(exiftool 无法完成,请使用下文提到的 c2patool)。
纪律(Agent 容易犯的错误)
即使能力很强的 Agent 也会犯这些错误。它们比任何标签名称都重要。
- **只描述画面中可见的内容。**描述画面展示的内容,而不是别人告诉你的内容。不要推断画面中看不到的事件、意图、身份、关系或法律身份。“示威者聚集抗议法院裁决”是在陈述画面中没有的信息;“人群在法院外举着标语”才是对照片的描述。
- **根据可见证据标注人物。**只有在画面中能看到标记、带标签的背心、制服、徽章或臂章时,才注明机构或角色。否则写“身穿战术装备的警员”或“穿蓝色衬衫的男子”。除非画面中的证据明确无误,否则绝不声称某人的移民或法律身份(不写“被拘留者”或“无证移民”)。
- **始终编写替代文本,它不是说明文字。**两者都要写:在
XMP-iptcCore:AltTextAccessibility中写简短的屏幕阅读器描述,在IPTC:Caption-Abstract中写可发布的说明文字。IPTC 刻意将两者区分开:说明文字陈述事实并显示在页面上;替代文本则由屏幕阅读器朗读,因此不要把一段文字直接复制到另一处。Agent 经常会写说明文字,却漏掉替代文本。 - **标注图像的生成方式,绝不弄虚作假。**如果图像由 AI 生成或经 AI 编辑,请在
XMP-iptcExt:DigitalSourceType中注明;如果是未经修改的照片,则用digitalCapture明确标注。诚实地标注;谨慎处理,并且对于传入文件,绝不删除现有的 Digital Source Type 或 C2PA 凭证,因为这会抹去他人有意添加的披露信息。 - **当位置可能危及他人时,删除 GPS 信息。**抗议者、消息来源、虐待幸存者或未成年人,嵌入的坐标可能会暴露其住所或安全屋。从发布用的衍生文件中删除 GPS(
-gps:all=),同时保留说明文字和署名;只有当位置信息属于编辑证据时,才保留含完整 GPS 信息的存档母版。GPS 是文件中风险最高的单个标签。 - **保持结构化字段中立。**编辑立场或有争议的标签应放在
Headline中,而不是City、Caption-Abstract或位置字段中。合作新闻编辑室会采用自己的措辞;干净的结构化字段能让他们自行处理。 - **从源文件验证往返结果。**从写入后的文件中读取元数据,而不是从缓冲区读取。每次上传或传输后,都要从目标位置重新读取;收到 200 响应只能证明字节已被接受,不能证明元数据得以保留。大多数社交平台会在上传时重新编码,并剥除 IPTC、XMP、GPS 以及 C2PA(参见
reference.md),所以“我已嵌入”不等于“它已送达”。
快速参考:承载关键信息的字段
| 角色 | IPTC (IIM) | XMP | EXIF |
|---|---|---|---|
| 摄影师 | By-line | dc:Creator | Artist |
| 署名 | Credit(机构,最多 32 个字符) | photoshop:Credit(全名 / 机构) | - |
| 说明文字 | Caption-Abstract | dc:Description | ImageDescription |
| 替代文本(简短) | - | iptcCore:AltTextAccessibility | - |
| 扩展说明 | - | iptcCore:ExtDescrAccessibility(用于复杂图像;不是说明文字) | - |
| 制作方式 | - | iptcExt:DigitalSourceType(完整 CV URI) | - |
| 关键词 | Keywords(可重复) | dc:Subject | - |
| 版权 | CopyrightNotice | dc:Rights | Copyright |
| 许可证(CC) | - | xmpRights:Marked/WebStatement/UsageTerms,cc:License(旧版) | - |
| 许可证 / 发现 | - | xmpRights:WebStatement,plus:LicensorName/LicensorURL(Google) | - |
| 标题 | Headline | photoshop:Headline | - |
| 地点 | Sub-location/City/Province-State/Country-* | iptcCore:Location,photoshop:City/State/Country | - |
| 日期 | DateCreated | photoshop:DateCreated | DateTimeOriginal(权威来源) |
Digital Source Type 值(完全由 AI 生成 → trainedAlgorithmicMedia,AI 编辑 → compositeWithTrainedAlgorithmicMedia,直接拍摄的照片 → digitalCapture)、完整的 IPTC 受控词汇表、IPTC-IIM 字节限制、PLUS/Google 许可和 Creative Commons 字段集、C2PA 工具,以及 AP 说明文字配方:请参阅 reference.md。
一次性写入全部三个层
CAPTION="A crowd holds signs outside the Mercer County Courthouse, Friday, June 19, 2026, in Trenton, N.J. (Dana Rivera/Example News Collective)"
ALT="A crowd of people holding handmade signs stands on the steps of a stone courthouse."
# how the image was made, a full IPTC CV URI (see reference.md for all values)
DST="http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture"
exiftool -codedcharacterset=utf8 -overwrite_original -P \
-EXIF:Artist="Dana Rivera" -XMP-dc:Creator="Dana Rivera" -IPTC:By-line="Dana Rivera" \
-IPTC:Credit="Example News Collective" -XMP-photoshop:Credit="Dana Rivera / Example News Collective" \
-IPTC:Caption-Abstract="$CAPTION" -XMP-dc:Description="$CAPTION" -EXIF:ImageDescription="$CAPTION" \
-XMP-iptcCore:AltTextAccessibility="$ALT" \
-XMP-iptcExt:DigitalSourceType="$DST" \
-IPTC:Keywords="protest" -IPTC:Keywords+="Trenton" \
-XMP-dc:Subject="protest" -XMP-dc:Subject+="Trenton" \
-EXIF:Copyright="(c) 2026 Example News Collective. Licensed CC BY 4.0." \
-IPTC:CopyrightNotice="(c) 2026 Example News Collective. CC BY 4.0." \
-XMP-dc:Rights="(c) 2026 Example News Collective. Licensed CC BY 4.0." \
-XMP-xmpRights:Marked=True \
-XMP-xmpRights:WebStatement="https://creativecommons.org/licenses/by/4.0/" \
-XMP-xmpRights:UsageTerms="Licensed CC BY 4.0. Credit: Dana Rivera / Example News Collective." \
-XMP-cc:License="https://creativecommons.org/licenses/by/4.0/" \
-XMP-cc:AttributionName="Dana Rivera / Example News Collective" \
-IPTC:City="Trenton" -IPTC:Province-State="New Jersey" \
-IPTC:Country-PrimaryLocationName="United States" -IPTC:Country-PrimaryLocationCode="USA" \
"-IPTC:DateCreated<EXIF:DateTimeOriginal" "-IPTC:TimeCreated<EXIF:DateTimeOriginal" \
"-XMP-photoshop:DateCreated<EXIF:DateTimeOriginal" \
photo.jpg
-P 会保留文件的修改时间;如果希望写入时更新时间戳,请去掉它。复杂图像(图表、信息图)的扩展无障碍说明应写入 XMP-iptcCore:ExtDescrAccessibility,这是一个与说明文字分开的字段,仅在替代文本加上周围文字仍无法传达图像内容时添加。
然后从文件中验证(这是 Agent 会跳过的步骤):
exiftool -G1 -s -IPTC:By-line -IPTC:Caption-Abstract -XMP-iptcCore:AltTextAccessibility \
-XMP-iptcExt:DigitalSourceType -XMP-cc:License -IPTC:Keywords photo.jpg
标注图像的制作方式(AI 和合成内容)
XMP-iptcExt:DigitalSourceType 使用 IPTC 受控词汇表记录内容来源。其值必须是完整 URI;exiftool 不会对其进行验证,因此裸词或拼写错误会被静默接受,毫无用处。每家新闻编辑室都需要的三个值:
BASE="http://cv.iptc.org/newscodes/digitalsourcetype"
# a straight camera photo, worth stating even for real news images
exiftool -XMP-iptcExt:DigitalSourceType="$BASE/digitalCapture" photo.jpg
# fully AI-generated (a trained model produced the whole image)
exiftool -XMP-iptcExt:DigitalSourceType="$BASE/trainedAlgorithmicMedia" ai.jpg
# a real photo edited with generative AI (inpaint / outpaint / generative fill)
exiftool -XMP-iptcExt:DigitalSourceType="$BASE/compositeWithTrainedAlgorithmicMedia" edited.jpg
Meta 和 Google 会读取此字段以自动标记 AI 内容;欧盟《AI 法案》的机器可读披露义务(第 50 条,自 2026 年 8 月起执行)正推动这项功能从锦上添花变为必需。IPTC 2025.1 新增了配套字段:AISystemUsed、AISystemVersionUsed、AIPromptInformation、AIPromptWriterName(exiftool ≥ 13.40)。完整词汇表以及应避免使用的已弃用术语:请参阅 reference.md。
内容凭证(C2PA):exiftool 可以读取,但无法签名
内容凭证是一份经过加密签名、与图像像素绑定的 C2PA 清单,记录了图像的创作者、使用的工具,以及是否涉及 AI。如今,越来越多的相机(Leica M11-P、Nikon Z6III、Sony Alpha)和机构(AFP、AP、BBC 试点项目)开始采用内容凭证。它与 IPTC/XMP 属于不同层面,回答的问题也不同:不是“文件声称了什么”,而是“谁签署了它,以及它此后是否被更改”。
exiftool 可以读取,但无法写入或验证:
exiftool -G1 -a -jumbf:all incoming.jpg # report the C2PA/JUMBF manifest (no signature check)
这会将清单显示为数据,并不会验证签名或签署者。如需真正验证,请将文件上传到 verify.contentauthenticity.org,确认签署者是你预期的机构。如需创建凭证,请使用 Adobe/CAI 工具、c2patool(brew install c2patool)或 pip install c2pa-python,而不是 exiftool。
向已签名文件写入元数据会破坏其凭证。 C2PA 硬绑定会对素材进行哈希,而哈希值包含嵌入的元数据,因此,任何 exiftool 写入、说明文字、署名、GPS 清除操作,甚至本技能中的标记,都会导致清单仍然存在,但变为无效。“绝不移除凭证”是必要条件,但并不足够。对于收到的已签名文件,要么保持原件不变,并在之后会用 c2patool 重新签名的衍生文件上处理元数据;要么接受嵌入的凭证不再有效,并如实说明。不要将元数据嵌入已签名原件后,仍把它的凭证视为有效。
向新闻编辑室说明时,还应提醒两点:有效凭证只能证明签名和信任链有效,不能证明场景真实(相机完全可以为屏幕照片签名);大多数社交平台会在上传时移除清单,因此平台上的来源信息往往只能通过“持久化”水印/指纹恢复来保留。参见 reference.md。
清除 GPS,生成适合发布的衍生文件
移除位置信息,同时保留说明文字、署名、版权和来源类型:
exiftool -gps:all= "-xmp:GPS*=" -overwrite_original photo.jpg
exiftool -a -G1 -gps:all "-xmp:GPS*" photo.jpg # verify, this must print nothing
请使用 -xmp:GPS*= 通配符,而不只是三个主要坐标:目的地和图像方向字段(GPSDestLatitude、GPSImgDirection)同样属于位置信息,否则会被保留下来。请将包含完整 GPS 信息的文件作为锁定的归档主文件保存,在坐标属于编辑证据(地理定位、核验)时尤其如此。发布时使用已清除 GPS 的副本。标记完成后,embed.py --strip-gps 可对整个文件夹执行此操作。
可在 Google 图片搜索中显示的许可信息
如需获得 Google 图片搜索中的“可授权使用”徽章和可用的“获取此图片”链接,请设置权利声明网页(触发条件)和 PLUS 许可方字段:
exiftool -XMP-xmpRights:Marked=True \
-XMP-xmpRights:WebStatement="https://example.org/license/photo123" \
-XMP-plus:LicensorName="Example News" -XMP-plus:LicensorURL="https://example.org/buy/photo123" \
photo.jpg
权利声明网页对应的是 xmpRights:WebStatement,而不是 dc:Rights,这是一个常见且代价高昂的混淆。Creative Commons 许可证也通过同一个 WebStatement 字段,使用 CC 许可协议页面 URL。详情和完整的 PLUS 字段集:reference.md。
批量标记文件夹
处理一次拍摄的文件时,可通过清单驱动 exiftool,而不必为每个文件单独运行命令。本目录中的 embed.py 接受一个文件夹和一个 JSON 清单(包含固定的署名、许可、许可方和数字来源类型字段,以及每张图片各自的替代文本、说明文字、扩展描述、关键词和可选的来源类型覆盖值),写入已标记的副本,读回每个副本以确认元数据已写入,并可通过 --strip-gps 移除副本中的 GPS 信息。它接受数字来源类型的简写(digitalCapture)或完整 URI;对于其他值会直接拒绝,而不会嵌入无效值。运行 python3 embed.py --help。
常见错误(基准测试所得)
| 错误 | 修正方法 |
|---|---|
| 写了说明文字,却没有替代文本 | 始终也要填写 AltTextAccessibility,它们是不同的字段 |
将说明文字复制到替代文本(或 ExtDescrAccessibility)中 | IPTC 将两者区分开来;请撰写适合屏幕阅读器的实际描述,仅对复杂图像使用 ExtDescr |
By-line/Credit/City 被静默截断 | 这些 IIM 字段最多可容纳 32 个字符;请将完整署名放在 XMP-photoshop:Credit 中 |
| 说明文字描述了画面中没有的内容 | 只描述可见内容;将无法看到的背景信息移到其他位置 |
| AI 生成的图像未标注 | 将 DigitalSourceType 设为 trainedAlgorithmicMedia(或正确的复合值) |
DigitalSourceType 设置成了单独的词语 | 该值必须是完整的 http://cv.iptc.org/... URI;exiftool 不会验证它 |
| 删除了收到文件中的 Digital Source Type 或 C2PA | 切勿删除披露信息;请保留所接收文件的来源信息 |
| 发布时仍嵌有 GPS 信息 | 当位置信息可能危及主体或信息来源时,使用 -gps:all= 将其删除 |
将 WebStatement 放入 dc:Rights | Google/许可网页声明应放在 xmpRights:WebStatement 中 |
将编辑立场标签放在 City 或说明文字中 | 将框架信息放在 Headline 中;结构化字段应保持中立 |
| 误以为上传后元数据仍然保留 | 从目标位置重新读取;大多数社交平台都会删除 IPTC/XMP/GPS/C2PA 信息 |
| 将关键词写成用逗号连接的单个字符串 | 请写入可重复的 Keywords 记录(以及 dc:Subject 列表) |
| 仅以纯文本添加 CC 许可说明 | 添加 xmpRights:WebStatement(CC deed URL)+ xmpRights:Marked |
现实影响
嵌入式元数据能让合作新闻编辑室找到照片、正确署名,并在明确的许可下重新发布,而无需联系摄影师。如今,图像也会通过元数据说明是由人还是模型制作,并携带经过签名的 Content Credential。一旦这些信息被删除,同一张照片就会变成一份无主文件:没有署名,没有许可,也没有来源信息。