Views
No views yet
geolocal/StreetCLIP
vision encoder (1024-dim pooled features).N discrete geocells (a partition of Europe
into bounded regions). The cell centroids ship alongside each head as
geocell_info.json, so a prediction can be turned into a (lat, lon) by
indexing into the file.1import json, torch, torch.nn as nn
2from huggingface_hub import hf_hub_download
3from transformers import CLIPImageProcessor, CLIPModel
4from PIL import Image
5
6REPO = "lebfla11/streetview-eu-geocell-heads"
7
8# 1. Pull a head + its partition (★ headline: v13 ce_haversine λ=0.05)
9head_path = hf_hub_download(REPO, "ablation_v13_weighted/lam_0.05/best.pt")
10info_path = hf_hub_download(REPO,
11 "data_collection/outputs/geocells_h3_res4_eu/geocell_info.json")
12
13centroids = {int(k): (v["centroid_lat"], v["centroid_lon"])
14 for k, v in json.load(open(info_path)).items()}
15n_cells = len(centroids)
16
17# 2. Build the head (matches HeadOnlyModel(feat_dim=1024, fusion='feature'))
18class Head(nn.Module):
19 def __init__(self, n_cells, dropout=0.3):
20 super().__init__()
21 self.head = nn.Sequential(
22 nn.LayerNorm(1024), nn.Dropout(dropout), nn.Linear(1024, n_cells))
23 def forward(self, feats):
24 # feats: (B, V, 1024) for V views, mean-pool then head
25 if feats.dim() == 3:
26 feats = feats.mean(dim=1)
27 return self.head(feats)
28
29head = Head(n_cells).eval()
30ck = torch.load(head_path, map_location="cpu", weights_only=False)
31head.load_state_dict(ck["model"] if isinstance(ck, dict) and "model" in ck else ck,
32 strict=False)
33
34# 3. Encode an image with frozen StreetCLIP
35clip = CLIPModel.from_pretrained("geolocal/StreetCLIP").vision_model.eval()
36proc = CLIPImageProcessor.from_pretrained("geolocal/StreetCLIP")
37img = Image.open("street_view.jpg").convert("RGB")
38px = proc(images=img, return_tensors="pt")["pixel_values"]
39with torch.no_grad():
40 feat = clip(pixel_values=px).pooler_output.float() # (1, 1024)
41 logits = head(feat)
42 probs = logits.softmax(-1)
43
44top5 = probs[0].topk(5)
45for prob, cell in zip(top5.values, top5.indices):
46 lat, lon = centroids[int(cell)]
47 print(f"cell {int(cell):4d} {prob.item()*100:5.1f}% ({lat:.3f}, {lon:.3f})")fusion='logit' (the v12 logit_lr5e-3 family — not
shipped here, see the demo Space for context), apply the linear head per
view and average the logits instead of the features.| Architecture | Linear classification head: LayerNorm(1024) → Dropout → Linear(1024, N_cells) on top of frozen StreetCLIP vision encoder |
| Backbone | geolocal/StreetCLIP (frozen, 1024-d pooled output, image size 336×336) |
| Multi-view fusion | Mean-pool of per-view features before the head (fusion='feature') |
| Training framework | PyTorch 2.6, AMP, AdamW, cosine LR schedule, label smoothing 0.1, ~80 epochs (v12 family) with patience 15. The headline v13 head is a 10-epoch warm-started fine-tune from v12 seed_44. |
| Loss | v12 family: class-weighted cross-entropy (inverse cell frequency). v13 (headline): weighted CE + λ · mean haversine on the posterior-expected lat/lon (probability-weighted spherical mean over all cell centroids). λ = 0.05 for the headline checkpoint. |
| Selection metric | balscore = ∛(within_25 × within_200 × within_750), the geometric mean of three distance-band accuracies |
| Trainable params | 1024 × N_cells + 2048 (LayerNorm) per head — under 6 M for the largest partition |
lat,lon key between splits). Earlier runs/clusters/* runs
that used a leaky split are not included here.| Path in repo | Partition | # cells | Top-1 | balscore |
|---|---|---|---|---|
★ ablation_v13_weighted/lam_0.05/best.pt | H3 res=4 EU | 1 898 | 29.7 % | 63.1 |
ablation_v13_weighted/lam_0.01/best.pt | H3 res=4 EU | 1 898 | 29.3 % | 62.8 |
ablation_v12/seed_44/best.pt | H3 res=4 EU | 1 898 | 28.8 % | 62.1 |
sweep_v5/streetclip/h3_res4_eu/best.pt | H3 res=4 EU | 1 898 | 28.3 % | 61.5 |
sweep_v5/streetclip/k2000_eu/best.pt | K-Means k=2000 EU | 1 792 | 27.5 % | 60.0 |
sweep_v5/streetclip/k4000_eu/best.pt | K-Means k=4000 EU | 3 180 | 22.9 % | 60.0 |
sweep_v5/streetclip/kdtree19_eu/best.pt | K-d tree t=19 EU | 5 444 | 15.7 % | 58.6 |
sweep_v5/streetclip/kdtree39_eu/best.pt | K-d tree t=39 EU | 2 059 | 21.8 % | 58.4 |
sweep_v5/streetclip/k1000_eu/best.pt | K-Means k=1000 EU | 957 | 36.2 % | 57.9 |
sweep_v5/streetclip/nuts3_eu/best.pt | NUTS-3 EU | 1 194 | 37.6 % | 56.8 |
sweep_v5/streetclip/kdtree78_eu/best.pt | K-d tree t=78 EU | 1 029 | 27.5 % | 55.3 |
sweep_v5/streetclip/k500_eu/best.pt | K-Means k=500 EU | 487 | 43.4 % | 51.7 |
sweep_v5/streetclip/kdtree155_eu/best.pt | K-d tree t=155 EU | 515 | 36.1 % | 50.7 |
data_collection/outputs/geocells_<name>/geocell_info.json (centroids only —
the training-time geocell_map.json and splits.json are not shipped).lat,lon key between splits)ablation_v13_weighted/lam_0.05)| Metric | v13 λ=0.05 (★) | v12 seed_44 (prior best) |
|---|---|---|
| Top-1 cell accuracy | 29.7 % | 28.8 % |
| Top-5 cell accuracy | 58.6 % | 57.8 % |
| Median haversine error | 51.9 km | 54.6 km |
| Mean haversine error | 122.6 km | 130.3 km |
| within 1 km | 0.1 % | 0.1 % |
| within 25 km | 31.4 % | 30.5 % |
| within 200 km | 81.0 % | 80.0 % |
| within 750 km | 98.6 % | 98.1 % |
| within 2500 km | 100.0 % | 100.0 % |
| balscore | 63.1 | 62.1 |
ablation_v12/seed_44/best.pt
with the new ce_haversine_weighted loss
(L = CE + λ · mean_haversine(posterior-expected lat/lon, true GPS),
λ=0.05). The fine-tune additionally improves cross-domain generalization on
out-of-distribution test sets (im2gps Europe top-1 14.4 → 20.0; OSM-Europe-1k
4-view mean km 716 → 629).1@misc{streetclip2023,
2 title = {StreetCLIP: A Robust Image-Language Model for Generalizable Geolocation},
3 author = {Lukas Haas and Silas Alberti and Michal Skreta},
4 year = {2023},
5 url = {https://huggingface.co/geolocal/StreetCLIP}
6}
7
8@mastersthesis{leber2026streetview,
9 title = {Street View Image Geolocation in Europe via Geocell Classification},
10 author = {Florian Leber},
11 school = {FH JOANNEUM University of Applied Sciences},
12 year = {2026}
13}florian.leber@edu.fh-joanneum.at
Master's thesis at FH JOANNEUM University of Applied Sciences.
Source: https://git-iit.fh-joanneum.at/leberflo19/europestreetviewgeolocator