写了个相机 RAW 照片 GPS 轨迹对齐、智能插值与离线反地理编码工具 Photools

vincentchyu 2026-08-26 10:56 1

[开源] 写了个相机 RAW 照片 GPS 轨迹对齐、智能插值与离线中文地名归档工具 photools


各位 V 友好。


平时喜欢带着相机到处跑,同时也是个写 Go 的程序员。每次外拍或者旅行回来,把几千张 RAW 照片导进电脑整理时,总会被照片的地理信息折磨:



  1. 相机时钟偏差:相机没内置 GPS ,用手机或佳明手表录了 GPX 轨迹,但相机时间和 GPS 总会差个几秒或几分钟,手动对齐繁琐,跨时区更是灾难;

  2. 室内/盲区断点:进了室内、隧道或者短暂停留时,手机没打上点,导致同一机位拍的照片一部分有坐标、一部分完全空白;

  3. 地名逆地理体验糟糕:Lightroom 之类的工具要么依赖联网,要么反查出来的中文地名不规范,更别提自动打上「某某风景名胜区 / 5A 景区」的 POI 了;

  4. 传统脚本慢如蜗牛:以前自己写 Python 调 ExifTool 批处理,每张照片都 fork/exec 一次子进程,上万张 RAW 照片能让风扇狂转半小时;

  5. 双格式同步容易漏:RAW + JPG 同拍或者带调色 XMP 时,元数据经常不同步。


折腾了一段时间,自己用 Go 撸了一套专门解决照片导入电脑后“第一公里”的元数据处理工具:photools。目前已经在 GitHub 开源。



  • GitHub 地址: https://github.com/vincentchyu/photools

  • 支持平台:macOS (原生 SwiftUI 客户端 / CLI / TUI) & Linux




做了哪些事情?


整个处理链路设计成了分阶段屏障流水线( Stage Barriers Pipeline ):


[待处理照片 RAW/JPG] ──▶ 1. GPX 轨迹匹配 ──▶ 2. GPS 智能插值 ──▶ 3. 离线逆地理 ──▶ 4. 拍摄日期归档 ──▶ [归档库/YYYY/MMDD/]


1. GPX 轨迹精准匹配与秒级偏差补偿



  • 支持 -geosync 时间偏移补偿(比如相机慢了 5 秒,直接设 +00:00:05 自动校准);

  • 采用主文件优先模型( Primary Asset Model ):以 RAW 为主决策源,写入坐标并二次校验,然后无污染同步到伴随的 JPG 和 XMP 。


2. 室内与断点自愈( GPS 智能时间插值)


很多时候在景区室内或峡谷拍的照片没命中 GPX 轨迹:



  • 球面大圆时间加权插值:利用前一个有效机位和后一个有效机位,按拍摄时间在球面上插值推算;

  • 同机位近邻继承:单侧锚点(刚进室内)直接继承最邻近机位,推算完动态合入索引,后面的照片就能自愈继承。


3. 纯离线 3D KD-Tree 高精逆地理(全国 90 万+ POI )



  • 不调任何高德、腾讯或 Google 在线 API ;

  • 纯离线加载全国 90 万+ 行政区划与风景名胜 POI 数据库;

  • 毫秒级将 国家、省份、城市、区县、景区 POI 规范中文名写入 IPTC/XMP ,在 Lightroom 或系统相册里直接看中文地名。


4. 真实拍摄日期原子归档



  • 读取 EXIF 真实拍摄时间,重命名为 YYYY-MM-DD-basename 并安全移动到 Processed/YYYY/MMDD/

  • 支持软降级容错(--allow-no-gps),没有 GPS 的照片也会安全归档,并自动生成一份 Markdown 排查清单(Logs/inbox_pending_report_latest.md)。




一些工程与性能实现


为了让批量处理几十万张 RAW 时不卡顿,做了一些底层优化:


1. ExifTool Stay-Open 守护进程池(速度提升 ~20 倍)


传统脚本慢是因为频繁启动 Perl 进程(单次 30~50ms )。photools 实现了基于管道通信的 StayOpenPool 常驻守护池(exiftool -stay_open True -@ -),单次读取开销压到了 1~2ms,并且支持子进程崩溃自愈。


2. 3D KD-Tree 空间索引 + O(log K) 时间分桶



  • 空间:把经纬度投影为三维笛卡尔坐标 $(x, y, z)$ 建平衡二叉树,最近邻查询微秒级;

  • 时间:插值阶段在内存中按天分桶维护有序切片,通过二分查找在 ~200ns 内定位前后邻近机位,批次首轮统一提取坐标,避免循环反复读 I/O 。


3. Go ➔ SwiftUI 进程内 C-Shared FFI 直通


macOS 客户端用原生 SwiftUI 写的,没有在本地开 HTTP 服务或搞 RPC ,而是把 Go 核心编译成了 libphotools.dylib 动态库,SwiftUI 通过 C-Shared FFI 在进程内直接调用,延迟在 0.1ms 左右。




界面展示


1. 工作区引导与离线 3D KD-Tree 反查



2. 多阶段流水线执行过程全景



3. 执行审计看板与待处理诊断清单



4. 终端 TUI 工作台(基于 Bubble Tea )


习惯在终端或 Linux/NAS 跑的同学,也可以直接敲 ./photools tui 启动交互式终端界面:



  • [1/2/3/4] 快捷键开关插件;

  • [O] 调出插件设置;

  • [S] 全局设置(支持 [Tab] 路径补全);

  • [Enter] 预检与执行。




写在最后


项目完全开源,没有商业化诉求,主要是为了解决自己和身边摄影朋友的实际整理痛点。


如果你也是相机常客、对照片元数据/GPS 轨迹有强迫症,欢迎试用、提 Issue 或 PR !



  • 项目 Repo: https://github.com/vincentchyu/photools

  • 依赖:需要系统安装 exiftoolbrew install exiftool

最新回复 (4)
  • bearbest 08-26 11:34
    1
    之前确实有类似的困扰,试用一下。
    另外问一下 GPS 运动轨迹记录 OP 用的什么软件或设备?
  • vincentchyu 楼主 08-26 12:52
    2
    @bearbest 我是用 workoutdoors ,还有苹果健康,上传 strava ,两步路组合使用,还没有找到不依赖手机的 gps 记录设备
  • zzzain46 08-26 13:09
    3
    @vincentchyu #2 这个跟 GPX Tracker 有什么功能差异吗
  • vincentchyu 楼主 08-26 16:22
    4
    @zzzain46 这个是清洗摄影档案的,不是记录的你说的那个应该是记录吧
* 帖子来源V2EX
返回