360 Secure SkillHub
360 安全技能中心

Geo Tag Photos

核心功能为无GPS元数据的JPG照片补全定位:经扫描筛选、AI识别全球知名地标、调用Nominatim获取坐标、生成报告,用户确认后写入EXIF。默认干运行,需指定--write和备份目录才修改文件,仅支持JPG,不可用于隐私追踪等违规场景。输入为JPG照片,输出含带GPS的照片及各类分析报告,照片仅本地处理。

ucsdzehualiu
ucsdzehualiu
热度 16.10
最近更新 2026-06-22 11:07:58
分类:生活依赖:代码执行 / 读写文件 / 第三方 API / 内部调用大模型应用场景:旅行 / 数据处理 / 文件转换

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add geo-tag-photos
Metadata
name
geo-tag-photos
description
Recover lost GPS metadata for JPG photos by recognizing landmarks via vision and writing GPS coordinates back into EXIF. Default dry-run; explicit --write with --backup-dir is required to modify files. Global coverage via Nominatim (OpenStreetMap), no API keys.
version
1.0.1
author
ucsdzehualiu
license
MIT
trigger_keywords
["geo-tag-photos","photo geolocation","照片定位","推断拍摄地点","EXIF GPS 写入","geotag photos","recover photo location"]

geo-tag-photos

Recover GPS metadata for JPG photos. The model identifies landmarks visually, the script geocodes those landmarks via Nominatim (OpenStreetMap) and writes the coordinates back into EXIF.

Use only on your own photos

This skill infers location from visual content. Do not use it to track other people, surveil private individuals, or de-anonymize photos that were intentionally stripped of location data. Inferred coordinates are best-guess estimates and must not be used for legal, forensic, evidentiary, or law-enforcement purposes.

What it can and cannot do

Can Cannot
Identify global public landmarks (Eiffel, Opera House, Taj Mahal …) Identify ordinary streets, residences, interiors, plain portraits, generic nature
Reach city- / landmark-level precision Provide street- or building-level precision
Process JPG / JPEG Process HEIC / PNG / TIFF / RAW (convert first)
Write GPS + ImageDescription + UserComment Modify any other EXIF field
Handle a few hundred photos at a time Reliably batch thousands (rate limits + cache hit-rate degrade)

Setup

cd <skill-folder>
pip install -r requirements.txt

The script will refuse to run if any of piexif, Pillow, requests is missing.

Workflow (5 phases)

You must walk the user through these phases in order. Each phase has a clear output the user can inspect.

Phase 1: scan

Identify which photos already have GPS (skip them) and which need inference.

python scripts/photo_geolocator.py scan --dir <photos> --out exif_status.csv

Read the CSV: any row with note=NO_GPS is a candidate for inference. Anything with note=NOT_JPG must be skipped (or converted by the user first).

Phase 2: AI vision (you do this directly)

For every JPG with NO_GPS, use the Read tool to view the image and extract:

  • landmark — the landmark name (e.g. "Eiffel Tower"). If no recognizable landmark, leave empty.
  • city — best-guess city, or empty.
  • country — best-guess country, or empty.
  • confidence — high / medium / low. Be honest. low rows will be skipped on write by default.
  • evidence — short text: visual cues you used (architecture style, signage language, flags, geography).

Aggregate into a JSON file ai_results.json:

[
  {"filename": "p1.jpg", "landmark": "Eiffel Tower", "city": "Paris",
   "country": "France", "confidence": "high", "evidence": "Iron lattice tower visible"},
  {"filename": "p2.jpg", "landmark": "", "city": "", "country": "",
   "confidence": "low", "evidence": "interior, no landmarks"}
]

Photos with no recognizable landmark: set confidence: low and empty strings. The pipeline will skip them on write.

Phase 3: geocode

python scripts/photo_geolocator.py geocode --input ai_results.json --out geocoded.csv

This calls Nominatim (rate-limited to 1 req / 1.1 s) and caches results locally. Failures are recorded in the CSV — they do not stop the run.

Phase 4: report

python scripts/photo_geolocator.py report \
  --dir <photos> --geocoded geocoded.csv --out report.csv

The report shows every photo and what action will be taken: WRITE, SKIP_HAS_GPS, SKIP_NOT_JPG, SKIP_LOW_CONFIDENCE, SKIP_NO_GEOCODE, SKIP_AI_UNKNOWN.

Show the report path to the user. Tell them to review it. Wait for explicit confirmation before phase 5.

Phase 5: write (only after user confirms)

First, dry-run to print the planned changes (no files modified):

python scripts/photo_geolocator.py write --dir <photos> --csv report.csv

Then, after user confirmation, the real write:

python scripts/photo_geolocator.py write --dir <photos> --csv report.csv \
  --write --backup-dir <photos>.bak

--backup-dir is mandatory and the script refuses if it's non-empty or inside the source directory. Maximum 500 photos per invocation.

After writing, run scan again and compare to the report. The script also verifies internally and reports any roundtrip mismatches.

Hard limits enforced by the script

  • JPG-only — non-JPG files are listed in scan but never written
  • --write without --backup-dir → exit code 2
  • Backup dir must be empty or non-existent → exit code 2
  • Backup dir cannot be inside source dir → exit code 2
  • Maximum 500 photos per write invocation → exit code 2
  • confidence: low rows skipped unless --include-low
  • Photos that already have GPS skipped unless --overwrite-existing (with warning)

Privacy

The script sends only text (landmark, city, country) to Nominatim. Photos never leave your machine via the script. The AI vision step happens inside your Claude session; whether the photos leave the machine for that step is governed by your Claude setup, not by this skill.

Troubleshooting

  • Geocoding returns wrong coordinates. Nominatim is fuzzy with ambiguous names (e.g. "Springfield"). Refine the AI step: include city + country with the landmark.
  • Cache holds bad entries. python scripts/photo_geolocator.py cache --clear
  • exif_io.EXIFError: not a JPG. Convert your HEIC / PNG / TIFF / RAW to JPG first (e.g. magick mogrify -format jpg *.heic).
  • Many photos report SKIP_AI_UNKNOWN. Phase 2 didn't emit entries for every photo — re-run vision for the missing ones.