Skip to content

About

无损批量修改照片元数据的瑞士军刀(Rust):拍摄时间、EXIF、GPS、方向、XMP、IPTC

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

32 Commits

Folders and files

Repository files navigation

PIC-Killer

CI Release Downloads License: MIT

English | 中文

照片元数据瑞士军刀 —— 用 Rust 实现的命令行工具。

无损批量修改照片的 EXIF 元数据:拍摄时间、作者版权、相机镜头、GPS 定位、方向,以及查看与清除。

只改写元数据段,不重新编码图像,照片的压缩像素数据逐比特不变,真正无损。

支持格式:JPEG / PNG / TIFF / WebP / HEIC / AVIF / JXL。


功能一览

子命令 作用
time 修改拍摄时间:固定值 / 相对偏移 / 序列递增 / 从文件名提取
show 查看或导出元数据(表格 / JSON / CSV)
set 设置常见标签:作者、版权、描述、相机、镜头、方向等
gps 设置或清除 GPS 定位
strip 清除元数据保护隐私:全部 / 仅 GPS
rotate 无损旋转标记:在现有方向上叠加旋转/镜像
copy 从一张参考照片复制元数据到一批照片
rename 按拍摄时间批量重命名文件(--from-name 的逆操作)
xmp 读写 XMP:标题/描述/作者/评分/关键词/城市等(JPEG 与 PNG)
iptc 读写旧版 IPTC-IIM:标题/说明/关键词/作者/城市/版权等(JPEG)
restore 从 .bak 备份还原文件,撤销之前的修改
geotag 用 GPX 轨迹按拍摄时间批量地理标记(插值写 GPS)
apply 从 CSV 批量导入元数据并写回(表格里批量编辑的写回端)
report 统计一批照片的元数据覆盖(拍摄时间/GPS/相机分布/时间跨度)
verify 体检:查元数据问题(损坏/坐标越界/时间异常/字段不一致)
completions 生成 shell 补全脚本(bash/zsh/fish/powershell)或 man 手册页

文档

完整文档在 docs/,每份均有中英双语对照版本:

文档 说明
新手入门手册 20 分钟入门:安装、安全网、五个真实任务
用户手册 完整参考:16 个命令、每个选项、字段表、格式支持
审计报告 工程审计:架构、发现、安全性、测试覆盖
功能预研 竞品定位、能力差距、候选功能

下载安装

预编译二进制(推荐)

前往 Releases 页 下载对应平台的压缩包,解压即得单个可执行文件:

平台 下载文件
Windows x64 pic-killer-<版本>-x86_64-pc-windows-msvc.zip
macOS (Apple Silicon) pic-killer-<版本>-aarch64-apple-darwin.tar.gz
macOS (Intel) pic-killer-<版本>-x86_64-apple-darwin.tar.gz
Linux x64 pic-killer-<版本>-x86_64-unknown-linux-gnu.tar.gz(静态版:…-musl)
Linux ARM64 pic-killer-<版本>-aarch64-unknown-linux-gnu.tar.gz

每次发布都附带 SHA256SUMS.txt,可校验下载完整性:

sha256sum -c SHA256SUMS.txt

从源码构建

需要 Rust 1.88+(代码使用 let-chain):

git clone https://github.com/adomore/pic-killer.git
cd pic-killer
cargo build --release          # 产物:target/release/pic-killer
cargo install --path .         # 或直接安装到 PATH

通用说明

所有子命令都接受一个或多个文件、目录或通配符作为处理对象:

  • -r, --recursive 递归子目录(目录默认只处理一层)
  • --ext <列表> 指定处理的扩展名(默认 jpg,jpeg,png,tif,tiff,webp)
  • --where <条件> 按元数据筛选要处理的文件(见下)
  • 内置通配符:pic-killer set "*.jpg" ...(Windows 的 cmd/PowerShell 不会为外部程序展开 *,本工具自己展开 */?)

--where 条件筛选

任何命令都能用 --where 只处理满足条件的文件——例如「只给缺 GPS 的照片补定位」:

pic-killer show .\photos -r --where no-gps          # 只列出没有 GPS 的
pic-killer time .\photos -r --where no-date --from-name   # 只给缺拍摄时间的从文件名补
pic-killer set  .\photos -r --where make=Canon --artist 张三   # 只改 Canon 拍的

支持的条件(单条件;大小写不敏感):

条件 含义
has-gps / no-gps 有/无 GPS 定位
has-date / no-date 有/无拍摄时间
has-xmp / no-xmp 有/无 XMP
has:名称 / no:名称 某标签存在/不存在(覆盖 EXIF/XMP/IPTC)
名称=值 名称!=值 标签值等于/不等于
名称~值 名称!~值 标签值包含/不包含

多个条件可用 &&(都满足)或 ||(任一满足)组合,例如 --where "no-gps && make=Canon"、--where "make=Canon || make=Nikon"。 两者不能混用,混用会直接报错。另外 != / !~ / no: 是取反语义,写了个不存在的标签名 (如 camera!=Canon)本会匹配全部文件,这种情况也会被拒绝——详见 用户手册 §6。

会写入的子命令(time/set/gps/strip/rotate/copy/xmp/iptc/geotag)还支持:

  • -n, --dry-run 仅预览,不写入
  • --backup 处理前备份为 <文件名>.bak
  • -y, --yes 跳过确认提示
  • -v, --verbose 详细输出
  • -j, --jobs <N> 并行线程数(默认按 CPU 核数自动,1=顺序)

并行处理 + 进度条:这些命令默认多线程处理,大图库批量操作明显更快; 在终端里会显示进度条(画在 stderr,不影响 show --json/--csv 等 stdout 输出)。 逐文件写入互不干扰、结果仍按原顺序打印。


time · 修改拍摄时间

# 设为固定时间
pic-killer time .\photos --set "2024-01-01 12:00:00"

# 相对偏移(修相机时钟/时区)——支持 +1y2mo3d4h5m6s 任意组合
pic-killer time .\photos --shift "+2h" -r
pic-killer time .\photos --shift "-3d"

# 序列递增(强制排序),首张起每张 +1 分钟
pic-killer time .\photos --sequential "2021-01-01 08:00:00" --interval "+1m"

# 从文件名提取日期(IMG_20230115_143022.jpg、2022-07-04 09.15.00.jpg 等)
pic-killer time .\photos --from-name

偏移单位(大小写不敏感、可组合):y年 mo月 w周 d天 h时 m分 s秒。 注意 m 是分钟、mo 是月;月/年按日历运算。

其它选项:

  • --tags <列表> 写入哪些时间字段:original,digitized,modify(默认全部)
  • --tz <偏移> 一并写入时区偏移标签(OffsetTime*),如 --tz +08:00
  • --also-file-time 同时把文件系统修改时间设为该拍摄时间

show · 查看元数据

# 人类可读表格
pic-killer show .\photo.jpg

# 只看名称含 gps 的标签
pic-killer show .\photo.jpg --filter gps

# 导出为 JSON 或 CSV(便于脚本 / 表格处理)
pic-killer show .\photos -r --json > meta.json
pic-killer show .\photos -r --csv  > meta.csv

若含 GPS,会额外打印一行十进制经纬度和海拔。

set · 设置标签

pic-killer set .\photos --artist "张三" --copyright "© 2024 张三"
pic-killer set .\photos --make "Canon" --model "EOS R5" --lens-model "RF 24-70"
pic-killer set .\photo.jpg --description "海边日落" --user-comment "备注"

# 方向(无损旋转标记):normal / cw90 / ccw90 / 180 / mirror-h / mirror-v 或 1-8
pic-killer set .\photo.jpg --orientation cw90

# 通用设置任意字符串或数值标签(可重复)
pic-killer set .\photo.jpg --set-string "OwnerName=Zhang" --set-string "Software=PicKiller"
pic-killer set .\photo.jpg --set-string "iso=100" --set-string "fnumber=2.8" --set-string "exposuretime=1/200"

# 删除指定标签(可重复)
pic-killer set .\photo.jpg --remove artist --remove copyright

常用标签的专用选项:--artist --copyright --description --software --make --model --lens-model --user-comment --owner --orientation。

gps · 定位

# 设置坐标(十进制度:北纬/东经为正,南纬/西经为负),海拔可选
pic-killer gps .\photo.jpg --lat 39.9042 --lon 116.4074 --alt 50

# 清除 GPS
pic-killer gps .\photos -r --clear

strip · 清除元数据

# 清除全部元数据(保护隐私)
pic-killer strip .\photos -r

# 只清除 GPS 定位,保留其它信息
pic-killer strip .\photos -r --gps

rotate · 无损旋转

在照片现有方向基础上叠加旋转/镜像(会与已有方向正确复合,而非简单覆盖)。 这是无损的:只改 EXIF 方向标记,不动像素。

pic-killer rotate .\photo.jpg --cw        # 顺时针 90°
pic-killer rotate .\photo.jpg --ccw       # 逆时针 90°
pic-killer rotate .\photo.jpg --r180      # 旋转 180°
pic-killer rotate .\photo.jpg --flip-h    # 水平镜像
pic-killer rotate .\photo.jpg --flip-v    # 垂直镜像
pic-killer rotate .\photos -r --reset     # 重置为正常

set --orientation 是绝对设定方向码;rotate 是在当前方向上相对叠加, 例如对已顺时针 90° 的照片再 --cw 会变成 180°。

copy · 复制元数据

从一张参考照片把元数据复制到一批照片(如整组连拍统一时间/地点)。

# 默认复制全部可复制的元数据(自动跳过尺寸/方向等与具体图像绑定的字段)
pic-killer copy .\burst\*.jpg --from .\reference.jpg

# 只复制拍摄时间,或只复制 GPS
pic-killer copy .\photos -r --from .\ref.jpg --time
pic-killer copy .\photos -r --from .\ref.jpg --gps

rename · 按时间重命名

按拍摄时间批量重命名(time --from-name 的逆操作)。同名自动加序号,无拍摄时间则跳过。

# 默认模板 %Y%m%d_%H%M%S → 20230115_143022.jpg
pic-killer rename .\photos -r

# 自定义模板(strftime 语法,不含扩展名)
pic-killer rename .\photos --pattern "%Y-%m-%d_%H.%M.%S"

# 先预览
pic-killer rename .\photos -r --dry-run

xmp · 读写 XMP

读写 XMP 元数据——相机、Lightroom、手机等常把标题、评分、关键词、版权存在这里 (现代 IPTC Core 也是基于 XMP 的)。支持 JPEG 与 PNG(PNG 存于 iTXt chunk)。

XMP 是独立于 EXIF 的元数据块,两者互不影响;写 XMP 会保留所有未识别的既有属性, 只增改指定的那几个。

# 设置标题/描述/作者/评分/关键词
pic-killer xmp .\photo.jpg --title "西湖日出" --description "清晨的西湖" `
  --creator "张三" --creator "李四" --rating 5 --keywords "风景,西湖,日出"

# 城市/国家、颜色标签
pic-killer xmp .\photo.jpg --city 杭州 --country 中国 --label 红色

# 通用设置任意属性(前缀:名称=值),删除属性
pic-killer xmp .\photo.jpg --set "photoshop:Headline=头条" --remove dc:description

# 清除整个 XMP 包
pic-killer xmp .\photos -r --clear

专用选项:--title --description --creator(可重复) --rights --rating(0-5) --label --keywords(逗号分隔) --city --country。查看用 show(会额外列出 XMP 段)。

Sidecar .xmp(支持 RAW):加 --sidecar 就写到旁挂的 <主干>.xmp 文件、完全不碰原图。 因为不解析原文件,所以连 little_exif 不认识的 RAW(CR2/NEF/ARW 等)也能写——正是 Lightroom/darktable 读取的那个 sidecar。show 会自动读取并显示 sidecar(原图读不了时也显示)。

pic-killer xmp shot.CR2 --sidecar --title 日出 --rating 5   # 生成 shot.xmp,不动 CR2
pic-killer xmp shot.CR2 --sidecar --clear                    # 删除 shot.xmp

iptc · 读写 IPTC-IIM

读写旧版 IPTC-IIM 元数据(存于 JPEG 的 APP13 / Photoshop 8BIM 块,新闻/图库工作流常用)。 仅支持 JPEG。写入以 UTF-8 编码并保留其它 8BIM 资源块(缩略图、色彩配置等)。

# 设置标题/说明/关键词/作者/城市/版权
pic-killer iptc .\photo.jpg --title "开幕式" --description "现场" `
  --keywords "体育,开幕" --creator "记者甲" --city 北京 --copyright "© 新华社"

# 通用设置(字段名或 记录:数据集)、删除、清除
pic-killer iptc .\photo.jpg --set "2:105=头条" --remove keywords
pic-killer iptc .\photos -r --clear

专用选项:--title --description --keywords --creator(可重复) --headline --city --state --country --copyright --credit --source --instructions。

EXIF、XMP、IPTC 三套元数据彼此独立,可并存于同一张 JPEG,本工具保证互不破坏。

restore · 从备份还原

与 --backup 配套:把之前用 --backup 生成的 <文件名>.bak 还原回去,一键撤销修改。

# 先带备份修改
pic-killer set .\photo.jpg --artist 张三 --backup

# 反悔了,一键还原(默认还原后移除 .bak)
pic-killer restore .\photo.jpg

# 保留 .bak 以便反复还原
pic-killer restore .\photos -r --keep-backup

# 先预览哪些文件有备份可还原
pic-killer restore .\photos -r --dry-run

没有 .bak 的文件会被跳过;还原是字节级的(.bak 就是原件的完整副本)。

geotag · GPX 地理标记

用 GPS 轨迹(.gpx)按拍摄时间给照片批量补 GPS——手机/手表录轨迹,相机没定位时的经典工作流。 按每张照片的拍摄时刻在轨迹里线性插值出经纬度(和海拔)。

# 相机是北京时间,轨迹是 UTC:用 --tz 指明相机时区
pic-killer geotag .\photos -r --gpx .\track.gpx --tz +08:00

# 只给「还没有 GPS」的照片补(配合 --where)
pic-killer geotag .\photos -r --gpx .\track.gpx --tz +08:00 --where no-gps

# 相机时钟慢了 30 秒?用 --offset 修正
pic-killer geotag .\photos --gpx .\track.gpx --offset +30s

选项:

  • --gpx <文件> GPX 轨迹(必填)
  • --tz <偏移> 相机时钟的时区,如 +08:00(默认按系统本地时区解释拍摄时间)
  • --offset <偏移> 额外时间偏移,修正相机时钟误差,如 -5m
  • --max-gap <秒> 与最近轨迹点相差超过此值则不标记(默认 600)

拍摄时间是本地时间、GPX 时间通常是 UTC,两者需对齐——所以 --tz 很关键。 无拍摄时间、或落在轨迹空档(超 --max-gap)的照片会被安全跳过。

apply · 从 CSV 导入

用一份 file,field,value 三列 CSV 批量写回元数据——配合 show --csv --for-apply 导出,就能 「导出 → 表格里批量编辑 → 写回」。同一文件的多个字段会合并成一次写入(并行、原子)。

pic-killer show .\photos -r --csv --for-apply > meta.csv
pic-killer apply --from meta.csv

注意要加 --for-apply。不带它的 show --csv 是转储格式(五列,含分组与十六进制码, GPS 还被拆成了分量标签),喂不回 apply。

CSV 示例(含逗号的值用引号包裹;可带表头,自动跳过;容忍 Excel 的 UTF-8 BOM):

file,field,value
photo1.jpg,artist,张三
photo1.jpg,datetimeoriginal,2023-03-03 12:00:00
photo1.jpg,gps,"31.23,121.47,15"
photo1.jpg,iso,200
photo1.jpg,xmp:title,西湖日出
photo1.jpg,xmp:rating,5
photo1.jpg,iptc:city,杭州

字段名加 xmp: / iptc: 前缀即写入对应体系(EXIF/XMP/IPTC 可在同一份 CSV 里混用,同一文件合并成各段一次写入)。

支持的字段(EXIF):datetimeoriginal/createdate/modifydate/alldates、 artist copyright description software make model lensmodel owner 等字符串标签、 数值/有理数标签 iso fnumber exposuretime(可写 1/200) focallength exposurecompensation 等、 orientation、usercomment、gps(值 纬度,经度[,海拔])、gps.clear。

report · 元数据统计

只读地扫描一批照片,汇总元数据覆盖情况——快速看出「有多少缺 GPS/拍摄时间、都是什么相机拍的」。

pic-killer report .\photos -r
pic-killer report .\photos -r --where no-gps   # 也可配合 --where

输出包含:总数、有/无拍摄时间、有/无 GPS、时间跨度、相机型号分布。

verify · 元数据体检

只读地检查一批照片的元数据问题,分「问题」(错误)与「警告」两级:

pic-killer verify .\photos -r

检查项:元数据损坏/无法读取、GPS 坐标越界、拍摄时间格式异常、拍摄时间在未来、 DateTimeOriginal 与 CreateDate 不一致、方向值越界(非 1-8)。发现问题时返回非零退出码, 便于脚本/CI 门禁。

shell 补全 / man 手册

# PowerShell:加入 $PROFILE
pic-killer completions powershell | Out-String | Invoke-Expression
# bash:写入补全目录
pic-killer completions bash > /etc/bash_completion.d/pic-killer
pic-killer completions --man > pic-killer.1        # man 手册页

支持 bash / zsh / fish / powershell / elvish。


无损原理

JPEG 由若干「段」组成:EXIF 信息放在 APP1 段里,真正的图像像素是独立的压缩扫描数据。 本工具只删除旧的元数据段、插入新的,完全不触碰扫描数据,因此不存在重新压缩、 不会有任何画质损失。用任意图像库解码改前 / 改后的照片,得到的像素完全一致。

安全设计

  • 原子写入:先写临时文件,fsync 后再原子重命名覆盖,批量处理中途中断也不会损坏原文件。
  • 默认保护文件时间戳:写入会新建文件,但默认恢复原文件系统 mtime/atime,除非 --also-file-time。
  • 可预览可备份:--dry-run 先看再做,--backup 留底。

已知限制

  • 元数据往返:底层 EXIF 库(little_exif)识别数十个常见标签(时间、Make、Model、 Artist、GPS、光圈快门等),这些都会完整保留;但极少数它不认识的冷门标签(部分厂商 MakerNote)在重写时可能丢失。图像像素永远无损。
  • HEIC / AVIF / JXL:底层库支持,但成熟度不及 JPEG,建议先用 --backup 或 --dry-run 试跑。
  • WebP:仅支持无损与扩展格式的 WebP。
  • XMP:支持 JPEG 与 PNG;TIFF/WebP/HEIC 的 XMP 尚未做。
  • IPTC-IIM:支持 JPEG(APP13);写入按 UTF-8 编码。
  • 缩略图替换暂未支持(涉及 IFD1 数据偏移,风险较高)。
  • 旧版 Windows 控制台里中文若显示为乱码,先执行 chcp 65001 切到 UTF-8。

许可

MIT

About

无损批量修改照片元数据的瑞士军刀(Rust):拍摄时间、EXIF、GPS、方向、XMP、IPTC

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages