用 Python 做个 GPS 照片搜索器!
Original...About 2 min
用 Python 做个 GPS 照片搜索器!
需求
给点 GPS 经纬度,查找给点目录下地理坐标接近的照片。
GPS 解析
现在大部分手机拍照会记录 GPS 信息(如果启用功能),该信息会被记录到 EXIF 元数据中。在 Python 可以使用 exifread 包来读取 EXIF 信息。一个简单的示例:
with open(path, 'rb') as f:
    exif = exifread.process_file(f)这里 exif 是一个 dict,value 类型为一个聚合体,包含该属性的 tag、offset 等。原始值需要通过 exif[...].printble 获取。
GPS 的主要属性储存在以下 4 个域中:
- 'GPS GPSLatitude':纬度。
- 'GPS GPSLatitudeRef':纬度符号,为- 'N'或- 'S'。
- 'GPS GPSLongitude':经度。
- 'GPS GPSLongitudeRef':经度符号,为- 'E'或- 'W'。
其中经纬度的格式为 [D, M, S1/S2],即度分秒,表示为 。例如:'[118, 46, 114514/125000]'。通常度和分都是整数,秒是分数,但可能有例外,度和分也是分数形式。
 当然也有高度 GPSAltitude,不过一般手机不会记录,这里直接忽略。
最简单的提取方式就是正则表达式了。
import re
def parse_gps(gps: Any):
    M = re.match(r"\[(\d+)(/(\d+))?, (\d+)(/(\d+))?, (\d+)(/(\d+))?\]", gps.printable)
    assert M
    d = int(M[1]) / (int(M[3] or 1))
    m = int(M[4]) / (int(M[6] or 1))
    s = int(M[7]) / (int(M[9] or 1))
    return d + m / 60 + s / 3600这样识别 GPS 的代码如下:
import exifread
def read_gps(path: Any):
    try:
        with open(path, 'rb') as f:
            exif = exifread.process_file(f)
    except:
        return None
    if 'GPS GPSLatitudeRef' not in exif:
        return None
    lat = parse_gps(exif['GPS GPSLatitude']) * (1 if exif['GPS GPSLatitudeRef'].printable == 'N' else -1)
    lon = parse_gps(exif['GPS GPSLongitude']) * (1 if exif['GPS GPSLongitudeRef'].printable == 'E' else -1)
    return (lat, lon)经纬度距离
最简单的方法就是使用 geopy 包了,准确度也有保障。
from geopy.distance import geodesic
dist = geodesic((lat1, lon1), (lat2, lon2))  # Distance对象,表示两个坐标间的距离
dist.m   # 距离数值大小,单位为m
dist.km  # 距离数值大小,单位为km完成搜索器
这步就很简单了,遍历目录读照片的 GPS,找距离上邻近的就行。
from more_itertools import ilen
from tqdm import tqdm
def find_neighbors(dir: Any,
                   anchor: tuple[float, float],
                   distance: float = 10):
    for f in tqdm(Path(dir).iterdir(), total=ilen(Path(dir).iterdir())):
        if gps := read_gps(f):
            if geodesic(gps, anchor).m < distance:
                print(f)
find_neighbors("path/to/photos", (32.0, 120.0))我这里处理速度大概是 350it/s。人工看了一下结果,有些照片的拍摄地距离 anchor 似乎超过了限制,可能是 GPS 记录问题?
可以考虑在 jupyter 里显示满足条件的照片。





