LamaSu/where-is-this

GitHub: LamaSu/where-is-this

一个从粗到细的照片地理定位 pipeline,利用 VLM 冷读、Mapillary 街景检索和几何验证,在无法精确定位时诚实地返回区域而非错误的 GPS 坐标。

Stars: 0 | Forks: 0

# 这是哪里 **弄清楚一张照片——或一段视频——是在哪里拍摄的。** 一个由粗到细的地理定位 pipeline,封装为两个 [Claude Code](https://claude.com/claude-code) skill:首先是结构化的、GeoGuessr 风格的冷读,将范围缩小到特定区域;然后是可选的检索 + 几何验证阶段,在有开放街景图像覆盖的地方,精准定位到具体的 GPS 坐标。 它建立在一个令人不安的事实之上: 因此,参考数据集是按需获取的,一次只获取一个区域。必须先有东西来为该区域命名。在 2026 年,最适合这项工作的可下载产物就是前沿 VLM 自身的推理能力——这就是为什么这里的阶段 1 是一个**剧本,而不是模型**。 ## 架构 ``` photo / reel │ ▼ ┌─────────────────────────────────────────┐ │ 0. FREE ANSWER │ EXIF GPS · platform geotag │ geoguess.py exif · reel.py │ caption · spoken place-names └─────────────────────────────────────────┘ → often just done │ (usually stripped) ▼ ┌─────────────────────────────────────────┐ │ 1. COARSE — cold read (the VLM itself) │ driving side · road lines │ SKILL.md meta-playbook, zero deps │ bollards · plates · script └─────────────────────────────────────────┘ flora · soil · sun · poles │ region + confidence → country / region / locality ▼ ┌─────────────────────────────────────────┐ │ 2. FETCH — open imagery for THAT region │ Mapillary Graph API (CC-BY-SA) │ mapillary_fetch.py │ tiled <0.009° bboxes └─────────────────────────────────────────┘ │ geo-tagged reference set ▼ ┌─────────────────────────────────────────┐ │ 3. FINE — retrieve │ MegaLoc 8448-d descriptors │ pinpoint.py + megaloc.py │ exact cosine NN (numpy) └─────────────────────────────────────────┘ │ top-k candidates ▼ ┌─────────────────────────────────────────┐ │ 4. VERIFY — geometric │ SuperPoint + LightGlue │ verify.py │ cv2 RANSAC inlier count └─────────────────────────────────────────┘ │ ▼ GPS pin · or an honest `region_only` ``` 第 4 步是最关键的部分。无论像不像,检索*总是*会返回一个最近邻结果。几何验证让 pipeline 能够说**“我不知道”**,而不是自信地插上一个错误的坐标。 ## 目录说明 | 文件 | 作用 | |---|---| | `skills/geolocate/SKILL.md` | 元剧本——线索清单、答案格式、置信度等级。阶段 1 的核心引擎。 | | `skills/geolocate/geoguess.py` | EXIF/GPS 提取器。仅依赖 Pillow。 | | `skills/geolocate/reel.py` | Reel/视频处理:yt-dlp 下载 → ffmpeg 关键帧 → Whisper 转录 → 字幕/地理标签。写入 `bundle.json`。 | | `skills/geolocate/mapillary_fetch.py` | Mapillary Graph API 抓取,在 2026 年 1 月的 bbox 上限下自动分块。仅使用标准库。 | | `skills/geolocate/pinpoint.py` | 协调 embed → search → verify → GPS(或 `region_only`)。 | | `skills/geolocate/verify.py` | SuperPoint + LightGlue + RANSAC 基础矩阵内点。 | | `skills/megaloc/megaloc.py` | MegaLoc 封装——`info`、`describe`、`compare`、`search`、`where`、`pairs`。可作为独立的 place-recognition CLI 使用。 | | `docs/global-geolocation-landscape.md` | 每次采用/扩展/构建决策背后的技术环境报告,包含已对照原始来源验证的许可证。 | ## 安装 将这两个 skill 目录复制到你的 Claude Code skill 文件夹中——它们作为同级目录互相依赖,所以请将它们放在一起: ``` git clone https://github.com/LamaSu/where-is-this cp -r where-is-this/skills/geolocate where-is-this/skills/megaloc ~/.claude/skills/ ``` 然后,按阶段执行: ``` # Stage 1(推理 + EXIF)— 这约占准确率的 90% pip install pillow # Stage 2(精确定位) pip install torch torchvision huggingface_hub numpy opencv-python pip install "git+https://github.com/cvg/LightGlue.git" # Reel/video 导入(还需要 PATH 中存在 ffmpeg + ffprobe) pip install yt-dlp openai-whisper # Mapillary token — 免费的只读 client token export MAPILLARY_TOKEN="MLY|..." # https://www.mapillary.com/dashboard/developers ``` 在 CPU 上运行。不需要 GPU(并且在验证时也并未使用 GPU)。 ## 使用 **一张照片。** 询问 Claude 这是在哪里;该 skill 会运行剧本并报告线索、推测结果以及校准后的置信度等级。或者直接驱动各个组件: ``` python ~/.claude/skills/geolocate/geoguess.py exif photo.jpg python ~/.claude/skills/geolocate/pinpoint.py \ --image photo.jpg --lat 52.3730 --lon 4.8930 --radius-km 0.3 ``` **一段 reel 或短视频。** 比照片包含更多信息——独特的镜头、说出的地名、屏幕文本、字幕,有时甚至有地理标签: ``` python ~/.claude/skills/geolocate/reel.py "" --out ./out ``` 一段旅行视频通常会展示*多个*地点。每个不同的镜头会被单独定位并单独报告——它们不会被平均化为一个模糊的推测。 **独立的 place recognition**,不涉及地理定位——询问哪些你自己的图像展示的是同一个地方。`--database` 接受一个文件夹、一个 glob 匹配模式,或一个由 `describe` 构建的 `.npy` 文件: ``` python ~/.claude/skills/megaloc/megaloc.py search --query query.jpg --database photos/ ``` ## 实际测量结果 已在 CPU 上针对真实数据(而非模拟数据)进行了端到端验证: - 针对 Amsterdam's Dam Square 现场抓取的 12 张真实 Mapillary 图像,在一个留出的查询样本上实现了 **6.0 米的误差**。 - **LightGlue 验证起到了门控作用:** 相同的图像对 → 1024 个内点;不相关的场景 → 0;一个真实的留出近邻样本 → 在约 3 米处有 168 个内点;一个超出区域的查询样本 → 0 个内点并返回 `region_only` 拒绝识别。最后一种情况才是重要的——它拒绝凭空捏造一个坐标。 - 在一段公开视频上进行的**Reel 处理**:提取了 8 个关键帧、正确的转录,以及从元数据中恢复的平台地理标签。 从结构上讲,这属于小样本(Small-N)。这些是证明该链路可行的存在性证明,而不是基准测试数据。 ## 诚实的局限性 - 准确度受限于画面所呈现的内容。在带有道路、标志和植被的户外场景中表现强劲;在室内、开阔海洋、紧凑裁剪和通用场景中表现微弱甚至毫无用处。 - “全球性”意味着*受限于覆盖范围*。Mapillary 在城市和道路沿线密度很高,但在其他地方却很稀疏;如果没有覆盖,那么阶段 1 划定的区域就是你的最终答案。 - **粗略推测是成败的关键。** 一个错误或过于宽泛的区域会抓取到错误的图像,再多的精细匹配也无法挽回这种错误。 - 搜索使用的是精确的 numpy 余弦相似度,在区域规模下表现良好。FAISS 是文档中提到的城市规模升级方案,但在 Windows 上需要一个单独的进程(torch 和 faiss 都会加载 OpenMP → 导致 `OMP Error #15`)。 ## 许可证与归属 本仓库采用 MIT 许可证。它不打包任何模型、权重或图像——所有重型内容都是在运行时根据上游的条款从上游获取的: | 组件 | 许可证 | 已验证 | |---|---|---| | [MegaLoc](https://github.com/gmberton/MegaLoc) (gmberton) | MIT | ✅ 原始来源 | | [hloc](https://github.com/cvg/Hierarchical-Localization) | Apache-2.0 | ✅ 原始来源 | | [FAISS](https://github.com/facebookresearch/faiss) | MIT | ✅ 原始来源 | | Mapillary imagery | CC-BY-SA — 归属 + 相同方式共享 | ✅ 原始来源 | | [LightGlue](https://github.com/cvg/LightGlue) / SuperPoint | ⚠️ **请检查上游** | ❌ 此处未验证 | 在任何商业用途之前,有两个值得认真对待的警示: - **SuperPoint 权重** 历史上一直带有非商业研究许可证,这与 LightGlue 自身仓库的许可证是分开的。请自行向上游验证。 - **Mapillary 的商业和衍生数据库条款** 在此处未经核实——在研究期间,该条款页面返回了 403 错误。CC-BY-SA 图像许可证已确认;但围绕构建衍生数据库的平台条款并未确认。 `docs/global-geolocation-landscape.md` 记录了为什么其他选项被拒绝的原因——特别是 PIGEON/PIGEOTTO(权重未公开,无法自行托管)和 StreetCLIP(CC-BY-NC,仅限非商业用途)。 ## 预期用途 用于定位你自己的照片、旅行内容,以及基于公开图像的开源情报工作——此外还能让你在玩 GeoGuessr 时有更好的表现。请不要将其用于针对个人的追踪:通过他人并非为此目的发布的图像,来推断其居住、工作或当前所在的位置,这恰恰是本工具明确不为之构建的用途。
标签:凭据扫描, 图像地理定位, 图像特征匹配, 地理空间推理, 实时处理, 开源情报(OSINT), 视觉大模型(VLM), 计算机视觉, 逆向工具