从照片提取坐标(EXIF + 水印 OCR)
何时用
- 拿到一批查勘/无人机照片,要在地图上定位、圈地、出位置图。
- 用户说"照片里有坐标""按坐标圈出来"。
★ 第一原则:无 EXIF ≠ 无坐标
先分类再提取,两类都要查:
| 文件名特征 | 坐标在哪 | 提取方式 |
|---|---|---|
dji_fly_YYYYMMDD_HHMMSS_..._photo_optimized.jpg |
EXIF GPSInfo |
PIL _getexif() 读 GPSLatitude/Longitude(DMS 转十进制,注意 S/W 方向) |
20260911144022758-26-1335316.jpg 这类纯时间戳名 |
图片水印文字 | 必须 OCR |
判据:文件大小明显更小(1~2MB vs 6MB)、Image._getexif() 只有 8 个条目(无 GPSInfo)、
raw 字节里搜不到 GPSLatitude。这时不要下结论说"没坐标",去看图——国内现场查勘绝大多数
用的是元道经纬相机,它把坐标烧录成水印文字。
水印长什么样(元道经纬相机)
左下角白字多行,固定字段:
经度:126.500444
纬度:45.614006
坐标系:WGS84坐标系
地址:某村退役军人服务站
时间:2026-09-11 11:23:23
海拔:176.8米
天气:21~24℃ 西风
备注:长按水印编辑备注
坐标已是 WGS84 十进制度,直接用,无需换算。
提取流程
- 先看一张图(用多模态读图)确认水印存在、字段名、是否横竖版。
- 装 OCR:
python -m pip install rapidocr_onnxruntime(纯 onnx,无需 tesseract,中文识别稳)。 - 直接对整图 OCR,不要先切水印区域——切区域容易削掉文字;RapidOCR 全图能直接出这 8 行。
- 正则抓取(注意全角冒号、OCR 会把冒号吃掉):
python grab(r"经度[::]?\s*([\d.]+)"), grab(r"纬度[::]?\s*([\d.]+)") - 逐条人工复核:OCR 会把
126.500551前的"经度:"吞掉、把纬度读成45.6067901(多一位)、
把126.501805读成1261501805。→ 把 OCR 的box一起打出来,读不准的看框内容补;
用同批次相邻点做合理性检验(偏离中位数 >1km 的孤立点先怀疑是 OCR 错,不是真跳点)。 - 版本差异:同一批里可能有横版(1685×1264)和竖版(1264×1685),水印位置不固定,不要用固定裁切框。
提速要点
- OCR 全图约 3~6 秒/张,几十张就后台跑并落盘 json(保留
lines原始文本便于追溯)。 - 有 EXIF 的文件直接跳过 OCR。
批次划分(→ 对应地块)
现场通常一块地拍一批,用时间间隔 > 5 分钟切批:
if mins(next) - mins(cur) > 5: new_batch()
每批点的跨度是关键诊断量:
- 跨度 40~120m → 这是拍摄位置(常落在田间路交叉口),不是地块边界,圈不出地块。
- 跨度 300m+ → 可能是航迹,但仍不等于地块界。
★ 必须向用户说清的事
照片坐标 = 拍摄点位置,不是地块范围。若照片点集中在一个小路口的几十米内,
而保单/台账面积是上百亩(十几公顷),差两个数量级,就绝不能拿凸包当"圈地"结果交付。
要地块边界只能:① 取确权/台账矢量;② 在该点周围的高分底图上人工判读勾绘。
配套
- 拿到坐标后要在图上定位/出图 → 用
gaode-basemap-gcj-fix抓高分底图。 - 落地成点图层:
gpd.GeoDataFrame(..., geometry=[Point(lon,lat)...], crs="EPSG:4326")
先存 WGS84,投影到高斯 3° 带口径需另做(面积口径见用户的面积铁律)。