
简介这份资源面向需要在照片中批量写入定位信息的开发者与测绘、航拍从业者解决数码照片缺乏坐标记录、难以与地理数据对应的问题。核心思路是借助Python脚本读取CSV中的经纬度与高程数据将其写入JPG文件的EXIF属性实现坐标信息的批量注入省去逐张手工编辑的繁琐。压缩包共12个文件包含10张JPG示例照片、1个main.py脚本和1份实验数据CSV整体约28.67MB示例照片与数据文件可直接用于验证脚本效果。目前已有1909人学习下载说明该方案在相关场景中具有一定参考价值。读者可获得一套可运行的坐标写入脚本、配套测试数据与示例照片理解EXIF字段结构、经纬度与高程的写入方式并在此基础上按自身数据格式调整批量处理逻辑快速迁移到实际项目中。1. 往照片里塞进 POS 信息一个被低估的现场取证刚需很多做外业采集、巡检、工程验收的人手机相册里躺着几千张照片每张都对应一个坐标、一个时间、一个作业点可一旦导出到电脑这些信息就全丢了——照片还是那张照片但它和哪条线路、哪个桩号、哪次任务的关联彻底断了。往照片中写入 POS 信息说的就是这件事把定位Position和姿态/时间Orientation/Time这类 POS 数据在拍摄或后处理阶段直接嵌进图片文件本身让照片自带我在哪、我朝哪、我什么时候拍的。它解决的不是好看而是可追溯。测绘、电力巡检、道路病害采集、农业遥感、无人机航拍这些场景里照片一旦脱离 POS 就是废片。适合谁适合手里有带定位的相机或手机、又需要把照片交给别人做后续处理的一线人员也适合想自己写脚本批量处理的开发者。下面按原理—实现—坑—进阶的顺序讲透。2. POS 信息到底写进照片的哪个位置EXIF、XMP 与文件名三条路2.1 先搞清楚 POS 数据能落在哪几个容器里照片不是一块铁板它是一个有结构的容器。POS 信息能塞进去的地方主要有三处选哪处决定了你后面用什么工具读、别人能不能读到。第一处是 EXIF。这是 JPEG/TIFF 最传统的元数据区GPS 经纬度、海拔、拍摄时间、相机朝向都在这里有标准字段。GPSLatitude、GPSLatitudeRef、GPSLongitude、GPSLongitudeRef、GPSAltitude、GPSImgDirection 是几个关键标签。优点是几乎所有看图软件、GIS 软件都认缺点是字段容量有限姿态角、精度因子、坐标系这些扩展信息没地方放。第二处是 XMP。Adobe 主导的 XML 元数据框架可以自定义命名空间想写多少写多少。无人机航拍里常见的 Yaw/Pitch/Roll、RTK 固定解状态、坐标系 EPSG 码塞 XMP 最合适。缺点是老软件不认得靠 ExifTool 或专门的解析库。第三处是文件名和伴随文件。有些采集软件干脆把坐标写进文件名比如DJI_20240101_120000_116.397_39.908.JPG再配一个同名.pos或.mrk文本。这条路最土但最稳任何系统都能读代价是要额外维护一个索引。提示如果照片最终要进 GIS 或做空三优先 EXIF 写基础 GPSXMP 写扩展姿态文件名保留可读的编号三条路一起用不冲突。2.2 用 ExifTool 命令行写入最小可复现操作先装 ExifTool这是元数据操作的事实标准跨平台。下面这条命令把经纬度、海拔、拍摄方向写进一张照片。# 写入 GPS 基础信息到单张照片 exiftool \ -GPSLatitude39.908722 \ -GPSLatitudeRefN \ -GPSLongitude116.397499 \ -GPSLongitudeRefE \ -GPSAltitude45.6 \ -GPSAltitudeRef0 \ -GPSImgDirection127.5 \ -GPSImgDirectionRefT \ -DateTimeOriginal2024:01:01 12:00:00 \ ./IMG_0001.JPG逻辑说明ExifTool 的参数是标签值的形式Ref类标签是方向参考纬度 N/S、经度 E/W、海拔 0 表示海平面以上、方向 T 表示真北。GPSImgDirection就是相机朝向角正北为 0顺时针增加。写完可以用exiftool -GPS:all ./IMG_0001.JPG回读验证。参数说明经纬度必须用十进制度不要写成度分秒字符串否则 ExifTool 会按字面存进去导致 GIS 读出来是错的。海拔单位是米。时间格式固定为YYYY:MM:DD HH:MM:SS冒号分隔日期这是 EXIF 标准的怪癖写成横杠会失败。2.3 批量写入从 POS 文本到照片的映射脚本单张写没意义现场都是成百上千张。常见做法是采集软件导出一个 POS 文本每行是文件名,经度,纬度,海拔,朝向,时间然后用脚本批量灌进去。import subprocess import csv # POS 文件格式: filename,lon,lat,alt,yaw,datetime POS_FILE pos.txt PHOTO_DIR ./photos with open(POS_FILE, newline, encodingutf-8) as f: reader csv.reader(f) for row in reader: fname, lon, lat, alt, yaw, dt row # 纬度方向由正负号决定负数为南纬 lat_ref N if float(lat) 0 else S lon_ref E if float(lon) 0 else W cmd [ exiftool, f-GPSLatitude{abs(float(lat))}, f-GPSLatitudeRef{lat_ref}, f-GPSLongitude{abs(float(lon))}, f-GPSLongitudeRef{lon_ref}, f-GPSAltitude{alt}, -GPSAltitudeRef0, f-GPSImgDirection{yaw}, -GPSImgDirectionRefT, f-DateTimeOriginal{dt}, f{PHOTO_DIR}/{fname}, ] subprocess.run(cmd, checkTrue) print(fdone: {fname})逻辑说明脚本读 POS 文本逐行拼 ExifTool 命令。关键点是纬度方向不能直接写负数EXIF 用Ref字段表达南北所以取绝对值再配 N/S。经度同理。checkTrue让任何一张失败就抛异常避免静默漏写。参数说明abs()处理正负号是必须的很多人图省事直接写负纬度结果 GIS 里照片跑到南极。时间字段如果 POS 里是2024-01-01 12:00:00要先替换成冒号格式再传否则 ExifTool 报格式错误。3. 姿态角与坐标系为什么你的照片在 GIS 里躺平了3.1 Yaw/Pitch/Roll 和 GPSImgDirection 不是一回事现场采集常拿到三个姿态角Yaw偏航、Pitch俯仰、Roll横滚。很多人直接把 Yaw 写进GPSImgDirection结果在三维软件里照片朝向对不上。原因是GPSImgDirection只表达相机光轴在水平面的投影方向而 Yaw 的定义依赖载体坐标系两者在相机有俯仰时并不相等。正确做法是如果只是二维地图展示Yaw 近似当方向角用问题不大如果要做空三或三维重建姿态角应该写进 XMP 自定义字段保留原始定义让后续软件自己换算。下面是把三个角写进 XMP 的例子。# 写入自定义 XMP 姿态字段 exiftool \ -XMP-drone:Yaw127.5 \ -XMP-drone:Pitch-15.2 \ -XMP-drone:Roll2.1 \ -XMP-drone:EPSG4326 \ ./IMG_0001.JPG逻辑说明XMP-drone里的drone是自定义命名空间前缀ExifTool 允许这样临时定义。EPSG 码写清楚坐标系4326 是 WGS84 经纬度如果是投影坐标要写对应码否则下游软件会按经纬度误读。参数说明姿态角单位是度Pitch 向下为负还是正取决于你的采集软件约定写之前一定确认这个符号搞反在三维里就是上下颠倒。EPSG 码不要省省了下游就得猜。3.2 坐标系转换WGS84 和 CGCS2000 别混着写国内项目经常遇到 CGCS2000 坐标它和 WGS84 在米级精度上差异很小但严格来说不是同一个椭球。如果采集设备输出的是 CGCS2000 经纬度直接当 WGS84 写进 EXIF在要求厘米级的场景会引入系统偏差。常见做法是EXIF 里统一写 WGS84转换在采集端完成如果必须保留原始坐标系就在 XMP 里写明 EPSG 码并在项目文档里记录转换参数。下面是一个用 pyproj 做转换的片段。from pyproj import Transformer # CGCS2000 经纬度 - WGS84 经纬度 transformer Transformer.from_crs(EPSG:4490, EPSG:4326, always_xyTrue) lon_wgs, lat_wgs transformer.transform(116.397499, 39.908722) print(lon_wgs, lat_wgs)逻辑说明always_xyTrue保证输入输出都是经度在前、纬度在后这是 pyproj 最容易踩的坑默认顺序是纬度在前。EPSG:4490是 CGCS2000 地理坐标EPSG:4326是 WGS84。参数说明转换前确认源坐标系码别拿投影坐标如 EPSG:4547当地理坐标传进去那样结果会离谱到没法看。转换后建议保留 8 位小数EXIF 精度足够。4. 避坑与排查写入 POS 信息时最容易翻车的五件事4.1 照片在 GIS 里位置整体偏移几百米现象批量写入后所有照片在 GIS 里统一往一个方向偏。原因源 POS 用的是投影坐标米被当成经纬度度写进去了或者坐标系码写错。解决回读一张照片的经纬度看数值范围经度应在 -180 到 180纬度在 -90 到 90超出这个范围一定是坐标系搞错了。用exiftool -GPS:all逐张抽查。4.2 部分照片写入成功、部分没写进去现象脚本跑完有的照片有 GPS有的没有。原因文件名大小写不一致、POS 文本里有空行、或者照片本身是只读属性。解决脚本里加文件存在性检查POS 解析时跳过空行写入前用os.chmod确保可写。别用checkFalse吞掉错误那等于给自己埋雷。4.3 时间字段写进去但排序乱了现象照片按时间排序时顺序不对。原因EXIF 时间格式必须是YYYY:MM:DD HH:MM:SS如果 POS 里是 Unix 时间戳或带时区偏移直接写会失败或被忽略。解决统一转成标准格式时区问题在采集端就归一化到本地时间或 UTC别混着来。4.4 写入后原图被改坏、打不开现象某张照片写入后无法预览。原因ExifTool 默认是原地修改如果磁盘满或进程被杀文件可能写了一半。解决加-o输出到新目录保留原图或者先备份再批量写。血泪经验是永远不要在没有备份的原始数据上直接跑批量写入。4.5 手机拍的照片读不到 GPS现象明明手机拍照时开了定位导出后 EXIF 里没有 GPS。原因部分手机默认不把 GPS 写进原图或者通过社交软件传输时被剥离。解决用原图导出别走压缩通道确认相机设置里定位权限开启。如果原图确实没有只能靠外部 POS 文件补补的时候注意时间对齐。5. 进阶把 POS 写入做成可验证的流水线写到这一步单张、批量、坐标系、避坑都过了剩下的是怎么让这件事可复现、可验证。我一般会搭一条最小流水线采集端导出 POS 文本 → 脚本批量写入 → 自动回读校验 → 生成一份校验报告。校验报告里至少包含每张照片的经纬度、时间、写入状态任何一张对不上就标红。下面是一个回读校验的片段思路是写入后立刻读回来和源数据比对误差超过阈值就报警。import subprocess import re def read_gps(path): out subprocess.run( [exiftool, -GPSLatitude, -GPSLongitude, -s, -s, -s, path], capture_outputTrue, textTrue ).stdout nums re.findall(r[-]?\d\.\d, out) return nums # [lat, lon] # 写入后校验容差 1e-6 度 lat_src, lon_src 39.908722, 116.397499 lat_read, lon_read map(float, read_gps(./IMG_0001.JPG)) assert abs(lat_read - lat_src) 1e-6, 纬度写入不一致 assert abs(lon_read - lon_src) 1e-6, 经度写入不一致 print(校验通过)逻辑说明-s -s -s让 ExifTool 只输出值不输出标签名方便正则提取。容差设 1e-6 度大约对应 0.1 米足够覆盖浮点误差。校验不通过就中断别让坏数据流到下游。参数说明正则只抓带小数点的数避免把整数标签误抓进来。如果照片本身没有 GPSread_gps返回空列表map会报错这其实是好事——说明写入根本没生效早发现早处理。一个具体技巧把 POS 文本和照片目录做成参数化用配置文件管理坐标系、时间格式、字段映射这样换一个项目只改配置不改代码。我吃过亏的地方是硬编码字段顺序结果换了个采集软件POS 列顺序变了整批数据全错位。后来养成习惯任何批量处理前先拿三张照片做小样验证确认无误再全量跑。这个习惯救过我很多次。希望帮到你。本文还有配套的精品资源点击获取