用几何学和 CUDA 编程给一座无名小岛定位

查看原文 HN 讨论

文章摘要

作者 yassa9 挑战的是 OSINT 研究者 Sofia Santos(Gralhix)设计的第 004 号练习题:给出一张热带度假村的航拍照片,要求回答三个问题——度假村叫什么名字、岛的坐标是多少、拍照时相机朝向哪个方位。绝大多数人会直接丢进 Google Lens 反查,但作者认为那样「浪费了一次有趣的机会」,于是决定纯靠数学、地理数据集和 GPU 暴力搜索把它算出来。

第一步是 exiftool 读元数据,结果只有「WEBP 无损、736×515」,毫无价值。于是他转向几何指纹:照片里能看到三块陆地——P0 是度假村所在的小沙洲,P1 是右侧的岛,P2 是左前方带山峰的岛。因为是无人机拍摄且高度未知,无法做严格的透视反演,他只用像素点击 GUI(01_triangle_gui.py)记录三点坐标,算出三角形在 P0 处的夹角和两边距离比,并给这两个值各留 ±20% 的容差窗口。

搜索底库用的是 OpenStreetMap 的 land-polygons-split-4326 全球海岸线矢量集(WGS84,882 MB)。过滤漏斗一层层收:①纬度带 −30°~+30°(凭天空和热带植被判断),剩 141,131 个陆地多边形;②局部密度过滤,5 km 内质心数 ≤10,排除密集礁群和群岛杂波,降到 51,576;③20 km 半径聚类,至少 3 点才算一个簇(用 scipy 的 cKDTree),得到 23,500 个簇;④每簇内做 C(n,3) 三点组合,为避免爆炸把每簇按面积分层采样(小、大、中各三分之一)截到 60 点,最终生成 80,690,777 个三元组。

匹配环节丢给 GPU:每个三元组一个 CUDA 线程,线程内先按陆地面积排序挑出最小的当 P0,再用二维叉积的符号(顺时针/逆时针)区分 P1 和 P2,然后独立计算 P0 处夹角与距离比,命中的用 atomicAdd 原子计数写进共享输出数组。在一块 RTX 3050(sm_86)上,占用 5169 MB 显存,核函数只跑了 204.1 ms,8070 万个三元组中 158,784 个通过;去重后剩 8,915 个唯一三元组。

之后是一串纯启发式的形态学筛选:「开放矩形」检查(沿 P0→P1 边、朝 P2 反方向构造一个长为边长两倍的矩形,若里面还有别的陆地就淘汰,因为照片里那片是开阔水面)→948 个;珊瑚沙洲形状检查,用 Polsby-Popper 紧凑度(4π·面积/周长²)要求 >0.5,再要求 1.5 km 内至少有 1 个面积 <0.05 km² 的微型沙洲「光环」→213 个;椭圆度检查,最小旋转外接矩形的长短边比要在 1.05~2.2 之间,填充率要 ≥0.589(即完美椭圆理论上限 π/4≈0.785 的 75%)→137 个;NDVI 植被检查,通过 Element 84 运营的 Earth Search STAC API(免费、无需 key)拉最新低云 Sentinel-2 影像,采样红光和近红外算 NDVI,阈值 0.6 →66 个;最后是高程检查,用 AWS 公开数据上的 Copernicus DEM GLO-30(30 m)瓦片,要求 P0 本身 ≤50 m,而在相机朝向(P1、P2 方位角的角平分线)±50°、2~20 km 的扇形内最高点在 100~500 m 之间 →26 个。

26 个幸存者集中在南亚、澳洲和大洋洲,只有一个在巴西附近。脚本给每个候选做点在多边形内的国家归属查询,输出一张带 Google Maps 卫星图链接的 HTML 表格。前 7 个肉眼一看就不对,第 8 个属于密克罗尼西亚联邦——正是答案。最终结果:度假村名叫 Oan,坐标 7°21′48.4″N、151°45′20.7″E(即 7.363444, 151.755750),相机方位角算出 324.97°,即西北方向(NW)。文末列出了所有数据来源与许可(OSM ODbL 1.0、Copernicus DEM、Sentinel-2、Natural Earth 10m admin-0 公有领域),代码和最终报告都在 GitHub 上。

HN 评论精华

这篇帖子拿到 526 分、87 条评论,讨论主线有两条:一是延伸出「这套思路其实就是巡航导弹的地形匹配制导」,二是围绕作者「本文非 LLM 生成」的声明爆发了一场真伪之争。