Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Upload 55 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- README.md +42 -0
- app.py +545 -0
- custom.ttf +0 -0
- images/1117948158689819.jpg +0 -0
- images/1131929937219152.jpg +0 -0
- images/132981045530287.jpg +0 -0
- images/1373307973052511.jpg +0 -0
- images/175745731085288.jpg +0 -0
- images/1906611242827635.jpg +0 -0
- images/2569566893353047.jpg +0 -0
- images/262995582382853.jpg +0 -0
- images/2688573051442365.jpg +0 -0
- images/2815113128739763.jpg +0 -0
- images/2926954904217999.jpg +0 -0
- images/296619375337754.jpg +0 -0
- images/2967574993525954.jpg +0 -0
- images/297765091851450.jpg +0 -0
- images/298997258922815.jpg +0 -0
- images/312137333614311.jpg +0 -0
- images/314603156759488.jpg +0 -0
- images/316183296688571.jpg +0 -0
- images/320045362820818.jpg +0 -0
- images/3295650104022182.jpg +0 -0
- images/3741052562672207.jpg +0 -0
- images/374123653951353.jpg +0 -0
- images/393835759099320.jpg +0 -0
- images/4065883540137904.jpg +0 -0
- images/474703873606185.jpg +0 -0
- images/477541399978996.jpg +0 -0
- images/479790613361193.jpg +0 -0
- images/4887875507910938.jpg +0 -0
- images/4898606693529789.jpg +0 -0
- images/495204901603170.jpg +0 -0
- images/503058357484613.jpg +0 -0
- images/509010086792207.jpg +0 -0
- images/517681129360654.jpg +0 -0
- images/521919388810193.jpg +0 -0
- images/524652062251031.jpg +0 -0
- images/529442031384910.jpg +0 -0
- images/537681453897872.jpg +0 -0
- images/546314506564628.jpg +0 -0
- images/688692811847809.jpg +0 -0
- images/732681614433401.jpg +0 -0
- images/743171673023530.jpg +0 -0
- images/744683329529751.jpg +0 -0
- images/797108350987275.jpg +0 -0
- images/803654143860113.jpg +0 -0
- images/810457353224255.jpg +0 -0
- images/827026111505364.jpg +0 -0
- images/900447860688305.jpg +0 -0
README.md
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: Plonk
|
3 |
+
emoji: 👀
|
4 |
+
colorFrom: yellow
|
5 |
+
colorTo: purple
|
6 |
+
sdk: gradio
|
7 |
+
sdk_version: 3.44.0
|
8 |
+
app_file: app.py
|
9 |
+
pinned: false
|
10 |
+
license: mit
|
11 |
+
---
|
12 |
+
|
13 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
14 |
+
![teaser.png](teaser.png)
|
15 |
+
# OpenStreetView-5M <br><sub>The Many Roads to Global Visual Geolocation 📍🌍</sub>
|
16 |
+
|
17 |
+
OpenStreetView-5M is hosted at [huggingface/datasets/osv5m/osv5m](https://huggingface.co/datasets/osv5m/osv5m)
|
18 |
+
Demo for [OpenStreetView-5M: The Many Roads to Global Visual Geolocation](https://imagine.enpc.fr/~guillaume-astruc/osv-5m).
|
19 |
+
|
20 |
+
**First authors:** [Guillaume Astruc](https://gastruc.github.io/), [Nicolas Dufour](https://nicolas-dufour.github.io/), [Ioannis Siglidis](https://imagine.enpc.fr/~siglidii/)
|
21 |
+
**Second authors:** [Constantin Aronssohn](), Nacim Bouia, [Stephanie Fu](https://stephanie-fu.github.io/), [Romain Loiseau](https://romainloiseau.fr/), [Van Nguyen Nguyen](https://nv-nguyen.github.io/), [Charles Raude](https://imagine.enpc.fr/~raudec/), [Elliot Vincent](https://imagine.enpc.fr/~vincente/), Lintao XU, Hongyu Zhou
|
22 |
+
**Last author:** [Loic Landrieu](https://loiclandrieu.com/)
|
23 |
+
**Research Institute:** [Imagine](https://imagine.enpc.fr/), _LIGM, Ecole des Ponts, Univ Gustave Eiffel, CNRS, Marne-la-Vallée, France_
|
24 |
+
|
25 |
+
OpenStreetView-5M is the first large-scale open geolocation benchmark of streetview images.
|
26 |
+
To get a sense of the difficulty of the benchmark, you can play our [demo](https://huggingface.co/spaces/osv5m/plonk).
|
27 |
+
Our dataset was used in an extensive benchmark of which we provide the best model.
|
28 |
+
For more details and results, please check out our [paper](arxiv) and [project page](https://imagine.enpc.fr/~guillaume-astruc/osv-5m).
|
29 |
+
|
30 |
+
### Citing 💫
|
31 |
+
|
32 |
+
```bibtex
|
33 |
+
@article{osv5m,
|
34 |
+
title = {{OpenStreetView-5M}: {T}he Many Roads to Global Visual Geolocation},
|
35 |
+
author = {Astruc, Guillaume and Dufour, Nicolas and Siglidis, Ioannis
|
36 |
+
and Aronssohn, Constantin and Bouia, Nacim and Fu, Stephanie and Loiseau, Romain
|
37 |
+
and Nguyen, Van Nguyen and Raude, Charles and Vincent, Elliot and Xu, Lintao
|
38 |
+
and Zhou, Hongyu and Landrieu, Loic},
|
39 |
+
journal = {CVPR},
|
40 |
+
year = {2024},
|
41 |
+
}
|
42 |
+
```
|
app.py
ADDED
@@ -0,0 +1,545 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Requires gradio==4.27.0"""
|
2 |
+
import io
|
3 |
+
import shutil
|
4 |
+
import os
|
5 |
+
import json
|
6 |
+
import uuid
|
7 |
+
import time
|
8 |
+
import math
|
9 |
+
import datetime
|
10 |
+
import numpy as np
|
11 |
+
|
12 |
+
from uuid import uuid4
|
13 |
+
from PIL import Image
|
14 |
+
from math import radians, sin, cos, sqrt, asin, exp
|
15 |
+
from os.path import join
|
16 |
+
from collections import defaultdict
|
17 |
+
from itertools import tee
|
18 |
+
|
19 |
+
import matplotlib.style as mplstyle
|
20 |
+
mplstyle.use(['fast'])
|
21 |
+
import pandas as pd
|
22 |
+
|
23 |
+
import gradio as gr
|
24 |
+
import reverse_geocoder as rg
|
25 |
+
import cartopy.crs as ccrs
|
26 |
+
import cartopy.feature as cfeature
|
27 |
+
import matplotlib.pyplot as plt
|
28 |
+
|
29 |
+
from gradio_folium import Folium
|
30 |
+
from geographiclib.geodesic import Geodesic
|
31 |
+
from folium import Map, Element, LatLngPopup, Marker, Icon, PolyLine, FeatureGroup
|
32 |
+
from folium.map import LayerControl
|
33 |
+
from folium.plugins import BeautifyIcon
|
34 |
+
from huggingface_hub import CommitScheduler
|
35 |
+
|
36 |
+
MPL = False
|
37 |
+
IMAGE_FOLDER = './images'
|
38 |
+
CSV_FILE = './select.csv'
|
39 |
+
BASE_LOCATION = [0, 23]
|
40 |
+
RULES = """<h1>OSV-5M (plonk)</h1>
|
41 |
+
<center><img width="256" alt="Rotating globe" src="https://upload.wikimedia.org/wikipedia/commons/6/6b/Rotating_globe.gif"></center>
|
42 |
+
<h2> Instructions </h2>
|
43 |
+
<h3> Click on the map 🗺️ (left) to the location at which you think the image 🖼️ (right) was captured! </h3>
|
44 |
+
<h3> Click "Select" to finalize your selection and then "Next" to move to the next image. </h3>
|
45 |
+
"""
|
46 |
+
css = """
|
47 |
+
@font-face {
|
48 |
+
font-family: custom;
|
49 |
+
src: url("/file=custom.ttf");
|
50 |
+
}
|
51 |
+
|
52 |
+
h1 {
|
53 |
+
text-align: center;
|
54 |
+
display:block;
|
55 |
+
font-family: custom;
|
56 |
+
}
|
57 |
+
img {
|
58 |
+
text-align: center;
|
59 |
+
display:block;
|
60 |
+
}
|
61 |
+
h2 {
|
62 |
+
text-align: center;
|
63 |
+
display:block;
|
64 |
+
font-family: custom;
|
65 |
+
}
|
66 |
+
h3 {
|
67 |
+
text-align: center;
|
68 |
+
display:block;
|
69 |
+
font-family: custom;
|
70 |
+
font-weight: normal;
|
71 |
+
}
|
72 |
+
"""
|
73 |
+
|
74 |
+
space_js = """
|
75 |
+
<script src="https://cdn.jsdelivr.net/npm/@rapideditor/country-coder@5.2/dist/country-coder.iife.min.js"></script>
|
76 |
+
<script>
|
77 |
+
function shortcuts(e) {
|
78 |
+
var event = document.all ? window.event : e;
|
79 |
+
switch (e.target.tagName.toLowerCase()) {
|
80 |
+
case "input":
|
81 |
+
case "textarea":
|
82 |
+
break;
|
83 |
+
default:
|
84 |
+
if (e.key.toLowerCase() == " " && !e.shiftKey) {
|
85 |
+
document.getElementById("latlon_btn").click();
|
86 |
+
}
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
function shortcuts_exit(e) {
|
91 |
+
var event = document.all ? window.event : e;
|
92 |
+
switch (e.target.tagName.toLowerCase()) {
|
93 |
+
case "input":
|
94 |
+
case "textarea":
|
95 |
+
break;
|
96 |
+
default:
|
97 |
+
if (e.key.toLowerCase() == "e" && e.shiftKey) {
|
98 |
+
document.getElementById("exit_btn").click();
|
99 |
+
}
|
100 |
+
}
|
101 |
+
}
|
102 |
+
document.addEventListener('keypress', shortcuts, false);
|
103 |
+
document.addEventListener('keypress', shortcuts_exit, false);
|
104 |
+
</script>
|
105 |
+
"""
|
106 |
+
|
107 |
+
def sample_points_along_geodesic(start_lat, start_lon, end_lat, end_lon, min_length_km=2000, segment_length_km=5000, num_samples=None):
|
108 |
+
geod = Geodesic.WGS84
|
109 |
+
distance = geod.Inverse(start_lat, start_lon, end_lat, end_lon)['s12']
|
110 |
+
if distance < min_length_km:
|
111 |
+
return [(start_lat, start_lon), (end_lat, end_lon)]
|
112 |
+
|
113 |
+
if num_samples is None:
|
114 |
+
num_samples = min(int(distance / segment_length_km) + 1, 1000)
|
115 |
+
point_distance = np.linspace(0, distance, num_samples)
|
116 |
+
points = []
|
117 |
+
for pd in point_distance:
|
118 |
+
line = geod.InverseLine(start_lat, start_lon, end_lat, end_lon)
|
119 |
+
g_point = line.Position(pd, Geodesic.STANDARD | Geodesic.LONG_UNROLL)
|
120 |
+
points.append((g_point['lat2'], g_point['lon2']))
|
121 |
+
return points
|
122 |
+
|
123 |
+
class GeodesicPolyLine(PolyLine):
|
124 |
+
def __init__(self, locations, min_length_km=2000, segment_length_km=1000, num_samples=None, **kwargs):
|
125 |
+
kwargs1 = dict(min_length_km=min_length_km, segment_length_km=segment_length_km, num_samples=num_samples)
|
126 |
+
assert len(locations) == 2, "A polyline must have at least two locations"
|
127 |
+
start, end = locations
|
128 |
+
geodesic_locs = sample_points_along_geodesic(start[0], start[1], end[0], end[1], **kwargs1)
|
129 |
+
super().__init__(geodesic_locs, **kwargs)
|
130 |
+
|
131 |
+
def inject_javascript(folium_map):
|
132 |
+
js = """
|
133 |
+
document.addEventListener('DOMContentLoaded', function() {
|
134 |
+
map_name_1.on('click', function(e) {
|
135 |
+
window.state_data = e.latlng
|
136 |
+
});
|
137 |
+
});
|
138 |
+
"""
|
139 |
+
folium_map.get_root().html.add_child(Element(f'<script>{js}</script>'))
|
140 |
+
|
141 |
+
def make_map_(name="map_name", id="1"):
|
142 |
+
map = Map(location=BASE_LOCATION, zoom_start=1)
|
143 |
+
map._name, map._id = name, id
|
144 |
+
|
145 |
+
LatLngPopup().add_to(map)
|
146 |
+
inject_javascript(map)
|
147 |
+
return map
|
148 |
+
|
149 |
+
def make_map(name="map_name", id="1", height=500):
|
150 |
+
map = make_map_(name, id)
|
151 |
+
fol = Folium(value=map, height=height, visible=False, elem_id='map-fol')
|
152 |
+
return fol
|
153 |
+
|
154 |
+
def map_js():
|
155 |
+
return """
|
156 |
+
(a, textBox) => {
|
157 |
+
const iframeMap = document.getElementById('map-fol').getElementsByTagName('iframe')[0];
|
158 |
+
const latlng = iframeMap.contentWindow.state_data;
|
159 |
+
if (!latlng) { return; }
|
160 |
+
textBox = `${latlng.lat},${latlng.lng}`;
|
161 |
+
document.getElementById('coords-tbox').getElementsByTagName('textarea')[0].value = textBox;
|
162 |
+
var a = countryCoder.iso1A2Code([latlng.lng, latlng.lat]);
|
163 |
+
if (!a) { a = 'nan'; }
|
164 |
+
return [a, `${latlng.lat},${latlng.lng},${a}`];
|
165 |
+
}
|
166 |
+
"""
|
167 |
+
|
168 |
+
def haversine(lat1, lon1, lat2, lon2):
|
169 |
+
if (lat1 is None) or (lon1 is None) or (lat2 is None) or (lon2 is None):
|
170 |
+
return 0
|
171 |
+
R = 6371 # radius of the earth in km
|
172 |
+
dLat = radians(lat2 - lat1)
|
173 |
+
dLon = radians(lon2 - lon1)
|
174 |
+
a = (
|
175 |
+
sin(dLat / 2.0) ** 2
|
176 |
+
+ cos(radians(lat1)) * cos(radians(lat2)) * sin(dLon / 2.0) ** 2
|
177 |
+
)
|
178 |
+
c = 2 * asin(sqrt(a))
|
179 |
+
distance = R * c
|
180 |
+
return distance
|
181 |
+
|
182 |
+
def geoscore(d):
|
183 |
+
return 5000 * exp(-d / 1492.7)
|
184 |
+
|
185 |
+
def compute_scores(csv_file):
|
186 |
+
df = pd.read_csv(csv_file)
|
187 |
+
if 'accuracy_country' not in df.columns:
|
188 |
+
print('Computing scores... (this may take a while)')
|
189 |
+
geocoders = rg.search([(row.true_lat, row.true_lon) for row in df.itertuples(name='Pandas')])
|
190 |
+
df['city'] = [geocoder['name'] for geocoder in geocoders]
|
191 |
+
df['area'] = [geocoder['admin2'] for geocoder in geocoders]
|
192 |
+
df['region'] = [geocoder['admin1'] for geocoder in geocoders]
|
193 |
+
df['country'] = [geocoder['cc'] for geocoder in geocoders]
|
194 |
+
|
195 |
+
df['city_val'] = df['city'].apply(lambda x: 0 if pd.isna(x) or x == 'nan' else 1)
|
196 |
+
df['area_val'] = df['area'].apply(lambda x: 0 if pd.isna(x) or x == 'nan' else 1)
|
197 |
+
df['region_val'] = df['region'].apply(lambda x: 0 if pd.isna(x) or x == 'nan' else 1)
|
198 |
+
df['country_val'] = df['country'].apply(lambda x: 0 if pd.isna(x) or x == 'nan' else 1)
|
199 |
+
|
200 |
+
df['distance'] = df.apply(lambda row: haversine(row['true_lat'], row['true_lon'], row['pred_lat'], row['pred_lon']), axis=1)
|
201 |
+
df['score'] = df.apply(lambda row: geoscore(row['distance']), axis=1)
|
202 |
+
df['distance_base'] = df.apply(lambda row: haversine(row['true_lat'], row['true_lon'], row['pred_lat_base'], row['pred_lon_base']), axis=1)
|
203 |
+
df['score_base'] = df.apply(lambda row: geoscore(row['distance_base']), axis=1)
|
204 |
+
|
205 |
+
print('Computing geocoding accuracy (base)...')
|
206 |
+
geocoders_base = rg.search([(row.pred_lat_base, row.pred_lon_base) for row in df.itertuples(name='Pandas')])
|
207 |
+
df['pred_city_base'] = [geocoder['name'] for geocoder in geocoders_base]
|
208 |
+
df['pred_area_base'] = [geocoder['admin2'] for geocoder in geocoders_base]
|
209 |
+
df['pred_region_base'] = [geocoder['admin1'] for geocoder in geocoders_base]
|
210 |
+
df['pred_country_base'] = [geocoder['cc'] for geocoder in geocoders_base]
|
211 |
+
|
212 |
+
df['city_hit_base'] = [df['city'].iloc[i] != 'nan' and df['pred_city_base'].iloc[i] == df['city'].iloc[i] for i in range(len(df))]
|
213 |
+
df['area_hit_base'] = [df['area'].iloc[i] != 'nan' and df['pred_area_base'].iloc[i] == df['area'].iloc[i] for i in range(len(df))]
|
214 |
+
df['region_hit_base'] = [df['region'].iloc[i] != 'nan' and df['pred_region_base'].iloc[i] == df['region'].iloc[i] for i in range(len(df))]
|
215 |
+
df['country_hit_base'] = [df['country'].iloc[i] != 'nan' and df['pred_country_base'].iloc[i] == df['country'].iloc[i] for i in range(len(df))]
|
216 |
+
|
217 |
+
df['accuracy_city_base'] = [(0 if df['city_val'].iloc[:i].sum() == 0 else df['city_hit_base'].iloc[:i].sum()/df['city_val'].iloc[:i].sum())*100 for i in range(len(df))]
|
218 |
+
df['accuracy_area_base'] = [(0 if df['area_val'].iloc[:i].sum() == 0 else df['area_hit_base'].iloc[:i].sum()/df['area_val'].iloc[:i].sum())*100 for i in range(len(df))]
|
219 |
+
df['accuracy_region_base'] = [(0 if df['region_val'].iloc[:i].sum() == 0 else df['region_hit_base'].iloc[:i].sum()/df['region_val'].iloc[:i].sum())*100 for i in range(len(df))]
|
220 |
+
df['accuracy_country_base'] = [(0 if df['country_val'].iloc[:i].sum() == 0 else df['country_hit_base'].iloc[:i].sum()/df['country_val'].iloc[:i].sum())*100 for i in range(len(df))]
|
221 |
+
|
222 |
+
print('Computing geocoding accuracy (best)...')
|
223 |
+
geocoders = rg.search([(row.pred_lat, row.pred_lon) for row in df.itertuples()])
|
224 |
+
df['pred_city'] = [geocoder['name'] for geocoder in geocoders]
|
225 |
+
df['pred_area'] = [geocoder['admin2'] for geocoder in geocoders]
|
226 |
+
df['pred_region'] = [geocoder['admin1'] for geocoder in geocoders]
|
227 |
+
df['pred_country'] = [geocoder['cc'] for geocoder in geocoders]
|
228 |
+
|
229 |
+
df['city_hit'] = [df['city'].iloc[i] != 'nan' and df['pred_city'].iloc[i] == df['city'].iloc[i] for i in range(len(df))]
|
230 |
+
df['area_hit'] = [df['area'].iloc[i] != 'nan' and df['pred_area'].iloc[i] == df['area'].iloc[i] for i in range(len(df))]
|
231 |
+
df['region_hit'] = [df['region'].iloc[i] != 'nan' and df['pred_region'].iloc[i] == df['region'].iloc[i] for i in range(len(df))]
|
232 |
+
df['country_hit'] = [df['country'].iloc[i] != 'nan' and df['pred_country'].iloc[i] == df['country'].iloc[i] for i in range(len(df))]
|
233 |
+
|
234 |
+
df['accuracy_city'] = [(0 if df['city_val'].iloc[:i].sum() == 0 else df['city_hit'].iloc[:i].sum()/df['city_val'].iloc[:i].sum())*100 for i in range(len(df))]
|
235 |
+
df['accuracy_area'] = [(0 if df['area_val'].iloc[:i].sum() == 0 else df['area_hit'].iloc[:i].sum()/df['area_val'].iloc[:i].sum())*100 for i in range(len(df))]
|
236 |
+
df['accuracy_region'] = [(0 if df['region_val'].iloc[:i].sum() == 0 else df['region_hit'].iloc[:i].sum()/df['region_val'].iloc[:i].sum())*100 for i in range(len(df))]
|
237 |
+
df['accuracy_country'] = [(0 if df['country_val'].iloc[:i].sum() == 0 else df['country_hit'].iloc[:i].sum()/df['country_val'].iloc[:i].sum())*100 for i in range(len(df))]
|
238 |
+
df.to_csv(csv_file, index=False)
|
239 |
+
|
240 |
+
|
241 |
+
if __name__ == "__main__":
|
242 |
+
JSON_DATASET_DIR = 'results'
|
243 |
+
scheduler = CommitScheduler(
|
244 |
+
repo_id="osv5m/humeval",
|
245 |
+
repo_type="dataset",
|
246 |
+
folder_path=JSON_DATASET_DIR,
|
247 |
+
path_in_repo=f"raw_data",
|
248 |
+
every=2
|
249 |
+
)
|
250 |
+
|
251 |
+
|
252 |
+
class Engine(object):
|
253 |
+
def __init__(self, image_folder, csv_file, mpl=True):
|
254 |
+
self.image_folder = image_folder
|
255 |
+
self.csv_file = csv_file
|
256 |
+
self.load_images_and_coordinates(csv_file)
|
257 |
+
|
258 |
+
# Initialize the score and distance lists
|
259 |
+
self.index = 0
|
260 |
+
self.stats = defaultdict(list)
|
261 |
+
|
262 |
+
# Create the figure and canvas only once
|
263 |
+
self.fig = plt.Figure(figsize=(10, 6))
|
264 |
+
self.mpl = mpl
|
265 |
+
if mpl:
|
266 |
+
self.ax = self.fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
|
267 |
+
|
268 |
+
self.tag = str(uuid4()) + datetime.datetime.now().strftime("__%Y_%m_%d_%H_%M_%S")
|
269 |
+
|
270 |
+
def load_images_and_coordinates(self, csv_file):
|
271 |
+
# Load the CSV
|
272 |
+
df = pd.read_csv(csv_file)
|
273 |
+
|
274 |
+
# Get the image filenames and their coordinates
|
275 |
+
self.images = [os.path.join(self.image_folder, f"{img_path}.jpg") for img_path in df['id'].tolist()[:]]
|
276 |
+
self.coordinates = df[['true_lon', 'true_lat']].values.tolist()[:]
|
277 |
+
|
278 |
+
# compute the admins
|
279 |
+
self.df = df
|
280 |
+
self.admins = self.df[['city', 'area', 'region', 'country']].values.tolist()[:]
|
281 |
+
self.preds = self.df[['pred_lon', 'pred_lat']].values.tolist()[:]
|
282 |
+
|
283 |
+
def isfinal(self):
|
284 |
+
return self.index == len(self.images)-1
|
285 |
+
|
286 |
+
def load_image(self):
|
287 |
+
if self.index > len(self.images)-1:
|
288 |
+
self.master.update_idletasks()
|
289 |
+
self.finish()
|
290 |
+
|
291 |
+
self.set_clock()
|
292 |
+
return self.images[self.index], '### ' + str(self.index + 1) + '/' + str(len(self.images))
|
293 |
+
|
294 |
+
def get_figure(self):
|
295 |
+
if self.mpl:
|
296 |
+
img_buf = io.BytesIO()
|
297 |
+
self.fig.savefig(img_buf, format='png', bbox_inches='tight', pad_inches=0, dpi=300)
|
298 |
+
pil = Image.open(img_buf)
|
299 |
+
self.width, self.height = pil.size
|
300 |
+
return pil
|
301 |
+
else:
|
302 |
+
pred_lon, pred_lat, true_lon, true_lat, click_lon, click_lat = self.info
|
303 |
+
map = Map(location=BASE_LOCATION, zoom_start=1)
|
304 |
+
map._name, map._id = 'visu', '1'
|
305 |
+
|
306 |
+
icon_star = BeautifyIcon(
|
307 |
+
icon='star',
|
308 |
+
inner_icon_style='color:red;font-size:30px;',
|
309 |
+
background_color='transparent',
|
310 |
+
border_color='transparent',
|
311 |
+
)
|
312 |
+
feature_group = FeatureGroup(name='Ground Truth')
|
313 |
+
Marker(
|
314 |
+
location=[true_lat, true_lon],
|
315 |
+
popup="True location",
|
316 |
+
icon=icon_star,
|
317 |
+
).add_to(feature_group)
|
318 |
+
map.add_child(feature_group)
|
319 |
+
|
320 |
+
icon_square = BeautifyIcon(
|
321 |
+
icon_shape='rectangle-dot',
|
322 |
+
border_color='green',
|
323 |
+
border_width=10,
|
324 |
+
)
|
325 |
+
feature_group_best = FeatureGroup(name='Best Model')
|
326 |
+
Marker(
|
327 |
+
location=[pred_lat, pred_lon],
|
328 |
+
popup="Best Model",
|
329 |
+
icon=icon_square,
|
330 |
+
).add_to(feature_group_best)
|
331 |
+
GeodesicPolyLine([[true_lat, true_lon], [pred_lat, pred_lon]], color='green').add_to(feature_group_best)
|
332 |
+
map.add_child(feature_group_best)
|
333 |
+
|
334 |
+
icon_circle = BeautifyIcon(
|
335 |
+
icon_shape='circle-dot',
|
336 |
+
border_color='blue',
|
337 |
+
border_width=10,
|
338 |
+
)
|
339 |
+
feature_group_user = FeatureGroup(name='User')
|
340 |
+
Marker(
|
341 |
+
location=[click_lat, click_lon],
|
342 |
+
popup="Human",
|
343 |
+
icon=icon_circle,
|
344 |
+
).add_to(feature_group_user)
|
345 |
+
GeodesicPolyLine([[true_lat, true_lon], [click_lat, click_lon]], color='blue').add_to(feature_group_user)
|
346 |
+
map.add_child(feature_group_user)
|
347 |
+
|
348 |
+
map.add_child(LayerControl())
|
349 |
+
|
350 |
+
return map
|
351 |
+
|
352 |
+
def set_clock(self):
|
353 |
+
self.time = time.time()
|
354 |
+
|
355 |
+
def get_clock(self):
|
356 |
+
return time.time() - self.time
|
357 |
+
|
358 |
+
def mpl_style(self, pred_lon, pred_lat, true_lon, true_lat, click_lon, click_lat):
|
359 |
+
if self.mpl:
|
360 |
+
self.ax.clear()
|
361 |
+
self.ax.set_global()
|
362 |
+
self.ax.stock_img()
|
363 |
+
self.ax.add_feature(cfeature.COASTLINE)
|
364 |
+
self.ax.add_feature(cfeature.BORDERS, linestyle=':')
|
365 |
+
|
366 |
+
self.ax.plot(pred_lon, pred_lat, 'gv', transform=ccrs.Geodetic(), label='model')
|
367 |
+
self.ax.plot([true_lon, pred_lon], [true_lat, pred_lat], color='green', linewidth=1, transform=ccrs.Geodetic())
|
368 |
+
self.ax.plot(click_lon, click_lat, 'bo', transform=ccrs.Geodetic(), label='user')
|
369 |
+
self.ax.plot([true_lon, click_lon], [true_lat, click_lat], color='blue', linewidth=1, transform=ccrs.Geodetic())
|
370 |
+
self.ax.plot(true_lon, true_lat, 'rx', transform=ccrs.Geodetic(), label='g.t.')
|
371 |
+
legend = self.ax.legend(ncol=3, loc='lower center') #, bbox_to_anchor=(0.5, -0.15), borderaxespad=0.
|
372 |
+
legend.get_frame().set_alpha(None)
|
373 |
+
self.fig.canvas.draw()
|
374 |
+
else:
|
375 |
+
self.info = [pred_lon, pred_lat, true_lon, true_lat, click_lon, click_lat]
|
376 |
+
|
377 |
+
|
378 |
+
def click(self, click_lon, click_lat, country):
|
379 |
+
time_elapsed = self.get_clock()
|
380 |
+
self.stats['times'].append(time_elapsed)
|
381 |
+
|
382 |
+
# convert click_lon, click_lat to lat, lon (given that you have the borders of the image)
|
383 |
+
# click_lon and click_lat is in pixels
|
384 |
+
# lon and lat is in degrees
|
385 |
+
self.stats['clicked_locations'].append((click_lat, click_lon))
|
386 |
+
true_lon, true_lat = self.coordinates[self.index]
|
387 |
+
pred_lon, pred_lat = self.preds[self.index]
|
388 |
+
self.mpl_style(pred_lon, pred_lat, true_lon, true_lat, click_lon, click_lat)
|
389 |
+
|
390 |
+
distance = haversine(true_lat, true_lon, click_lat, click_lon)
|
391 |
+
score = geoscore(distance)
|
392 |
+
self.stats['scores'].append(score)
|
393 |
+
self.stats['distances'].append(distance)
|
394 |
+
self.stats['country'].append(int(self.admins[self.index][3] != 'nan' and country == self.admins[self.index][3]))
|
395 |
+
|
396 |
+
df = pd.DataFrame([self.get_model_average(who) for who in ['user', 'best', 'base']], columns=['who', 'GeoScore', 'Distance', 'Accuracy (country)']).round(2)
|
397 |
+
result_text = (f"### GeoScore: {score:.0f}, distance: {distance:.0f} km")
|
398 |
+
|
399 |
+
self.cache(self.index+1, score, distance, (click_lat, click_lon), time_elapsed)
|
400 |
+
return self.get_figure(), result_text, df
|
401 |
+
|
402 |
+
def next_image(self):
|
403 |
+
# Go to the next image
|
404 |
+
self.index += 1
|
405 |
+
return self.load_image()
|
406 |
+
|
407 |
+
def get_model_average(self, which, all=False):
|
408 |
+
aux, i = [], self.index+1
|
409 |
+
if which == 'user':
|
410 |
+
avg_score = sum(self.stats['scores']) / len(self.stats['scores']) if self.stats['scores'] else 0
|
411 |
+
avg_distance = sum(self.stats['distances']) / len(self.stats['distances']) if self.stats['distances'] else 0
|
412 |
+
avg_country_accuracy = (0 if self.df['country_val'].iloc[:i].sum() == 0 else sum(self.stats['country'])/self.df['country_val'].iloc[:i].sum())*100
|
413 |
+
if all:
|
414 |
+
avg_city_accuracy = (0 if self.df['city_val'].iloc[:i].sum() == 0 else sum(self.stats['city'])/self.df['city_val'].iloc[:i].sum())*100
|
415 |
+
avg_area_accuracy = (0 if self.df['area_val'].iloc[:i].sum() == 0 else sum(self.stats['area'])/self.df['area_val'].iloc[:i].sum())*100
|
416 |
+
avg_region_accuracy = (0 if self.df['region_val'].iloc[:i].sum() == 0 else sum(self.stats['region'])/self.df['region_val'].iloc[:i].sum())*100
|
417 |
+
aux = [avg_city_accuracy, avg_area_accuracy, avg_region_accuracy]
|
418 |
+
elif which == 'base':
|
419 |
+
avg_score = np.mean(self.df[['score_base']].iloc[:i])
|
420 |
+
avg_distance = np.mean(self.df[['distance_base']].iloc[:i])
|
421 |
+
avg_country_accuracy = self.df['accuracy_country_base'].iloc[i]
|
422 |
+
if all:
|
423 |
+
aux = [self.df['accuracy_city_base'].iloc[i], self.df['accuracy_area_base'].iloc[i], self.df['accuracy_region_base'].iloc[i]]
|
424 |
+
elif which == 'best':
|
425 |
+
avg_score = np.mean(self.df[['score']].iloc[:i])
|
426 |
+
avg_distance = np.mean(self.df[['distance']].iloc[:i])
|
427 |
+
avg_country_accuracy = self.df['accuracy_country'].iloc[i]
|
428 |
+
if all:
|
429 |
+
aux = [self.df['accuracy_city_base'].iloc[i], self.df['accuracy_area_base'].iloc[i], self.df['accuracy_region_base'].iloc[i]]
|
430 |
+
return [which, avg_score, avg_distance, avg_country_accuracy] + aux
|
431 |
+
|
432 |
+
def update_average_display(self):
|
433 |
+
# Calculate the average values
|
434 |
+
avg_score = sum(self.stats['scores']) / len(self.stats['scores']) if self.stats['scores'] else 0
|
435 |
+
avg_distance = sum(self.stats['distances']) / len(self.stats['distances']) if self.stats['distances'] else 0
|
436 |
+
|
437 |
+
# Update the text box
|
438 |
+
return f"GeoScore: {avg_score:.0f}, Distance: {avg_distance:.0f} km"
|
439 |
+
|
440 |
+
def finish(self):
|
441 |
+
clicks = rg.search(self.stats['clicked_locations'])
|
442 |
+
self.stats['city'] = [(int(self.admins[self.index][0] != 'nan' and click['name'] == self.admins[self.index][0])) for click in clicks]
|
443 |
+
self.stats['area'] = [(int(self.admins[self.index][1] != 'nan' and click['admin2'] == self.admins[self.index][1])) for click in clicks]
|
444 |
+
self.stats['region'] = [(int(self.admins[self.index][2] != 'nan' and click['admin1'] == self.admins[self.index][2])) for click in clicks]
|
445 |
+
|
446 |
+
df = pd.DataFrame([self.get_model_average(who, True) for who in ['user', 'best', 'base']], columns=['who', 'GeoScore', 'Distance', 'Accuracy (country)', 'Accuracy (city)', 'Accuracy (area)', 'Accuracy (region)'])
|
447 |
+
return df
|
448 |
+
|
449 |
+
# Function to save the game state
|
450 |
+
def cache(self, index, score, distance, location, time_elapsed):
|
451 |
+
with scheduler.lock:
|
452 |
+
os.makedirs(join(JSON_DATASET_DIR, self.tag), exist_ok=True)
|
453 |
+
with open(join(JSON_DATASET_DIR, self.tag, f'{index}.json'), 'w') as f:
|
454 |
+
json.dump({"lat": location[0], "lon": location[1], "time": time_elapsed, "user": self.tag}, f)
|
455 |
+
f.write('\n')
|
456 |
+
|
457 |
+
|
458 |
+
if __name__ == "__main__":
|
459 |
+
# login with the key from secret
|
460 |
+
if 'csv' in os.environ:
|
461 |
+
csv_str = os.environ['csv']
|
462 |
+
with open(CSV_FILE, 'w') as f:
|
463 |
+
f.write(csv_str)
|
464 |
+
|
465 |
+
compute_scores(CSV_FILE)
|
466 |
+
import gradio as gr
|
467 |
+
def click(state, coords):
|
468 |
+
if coords == '-1' or state['clicked']:
|
469 |
+
return gr.update(), gr.update(), gr.update(), gr.update()
|
470 |
+
lat, lon, country = coords.split(',')
|
471 |
+
state['clicked'] = True
|
472 |
+
image, text, df = state['engine'].click(float(lon), float(lat), country)
|
473 |
+
df = df.sort_values(by='GeoScore', ascending=False)
|
474 |
+
return gr.update(visible=False), gr.update(value=image, visible=True), gr.update(value=text, visible=True), gr.update(value=df, visible=True)
|
475 |
+
|
476 |
+
def exit_(state):
|
477 |
+
if state['engine'].index > 0:
|
478 |
+
df = state['engine'].finish()
|
479 |
+
return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value='', visible=True), gr.update(visible=False), gr.update(visible=False), gr.update(value=df, visible=True), gr.update(value="-1", visible=False), gr.update(value="<h1 style='margin-top: 4em;'> Your stats on OSV-5M🌍 </h1>", visible=True), gr.update(value="<h3 style='margin-top: 1em;'>Thanks for playing ❤️</h3>", visible=True), gr.update(visible=False)
|
480 |
+
else:
|
481 |
+
return gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
482 |
+
|
483 |
+
def next_(state):
|
484 |
+
if state['clicked']:
|
485 |
+
if state['engine'].isfinal():
|
486 |
+
return exit_(state)
|
487 |
+
else:
|
488 |
+
image, text = state['engine'].next_image()
|
489 |
+
state['clicked'] = False
|
490 |
+
return gr.update(value=make_map_(), visible=True), gr.update(visible=False), gr.update(value=image), gr.update(value=text), gr.update(visible=False), gr.update(), gr.update(visible=False), gr.update(value="-1"), gr.update(), gr.update(), gr.update()
|
491 |
+
else:
|
492 |
+
return gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
|
493 |
+
|
494 |
+
def start(state):
|
495 |
+
# create a unique random temporary name under CACHE_DIR
|
496 |
+
# generate random hex and make sure it doesn't exist under CACHE_DIR
|
497 |
+
state['engine'] = Engine(IMAGE_FOLDER, CSV_FILE, MPL)
|
498 |
+
state['clicked'] = False
|
499 |
+
image, text = state['engine'].load_image()
|
500 |
+
|
501 |
+
return (
|
502 |
+
gr.update(visible=True),
|
503 |
+
gr.update(visible=False),
|
504 |
+
gr.update(value=image, visible=True),
|
505 |
+
gr.update(value=text, visible=True),
|
506 |
+
gr.update(visible=True),
|
507 |
+
gr.update(visible=True),
|
508 |
+
gr.update(value="<h1>OSV-5M (plonk)</h1>"),
|
509 |
+
gr.update(visible=False),
|
510 |
+
gr.update(visible=False),
|
511 |
+
gr.update(value="-1"),
|
512 |
+
gr.update(visible=True),
|
513 |
+
)
|
514 |
+
|
515 |
+
with gr.Blocks(css=css, head=space_js) as demo:
|
516 |
+
state = gr.State({})
|
517 |
+
rules = gr.Markdown(RULES, visible=True)
|
518 |
+
|
519 |
+
exit_button = gr.Button("Exit", visible=False, elem_id='exit_btn')
|
520 |
+
start_button = gr.Button("Start", visible=True)
|
521 |
+
with gr.Row():
|
522 |
+
map_ = make_map(height=512)
|
523 |
+
if MPL:
|
524 |
+
results = gr.Image(label='Results', visible=False)
|
525 |
+
else:
|
526 |
+
results = Folium(height=512, visible=False)
|
527 |
+
image_ = gr.Image(label='Image', visible=False, height=512)
|
528 |
+
|
529 |
+
with gr.Row():
|
530 |
+
text = gr.Markdown("", visible=False)
|
531 |
+
text_count = gr.Markdown("", visible=False)
|
532 |
+
|
533 |
+
with gr.Row():
|
534 |
+
select_button = gr.Button("Select", elem_id='latlon_btn', visible=False)
|
535 |
+
next_button = gr.Button("Next", visible=False, elem_id='next')
|
536 |
+
perf = gr.Dataframe(value=None, visible=False)
|
537 |
+
text_end = gr.Markdown("", visible=False)
|
538 |
+
|
539 |
+
coords = gr.Textbox(value="-1", label="Latitude, Longitude", visible=False, elem_id='coords-tbox')
|
540 |
+
start_button.click(start, inputs=[state], outputs=[map_, results, image_, text_count, text, next_button, rules, state, start_button, coords, select_button])
|
541 |
+
select_button.click(click, inputs=[state, coords], outputs=[map_, results, text, perf], js=map_js())
|
542 |
+
next_button.click(next_, inputs=[state], outputs=[map_, results, image_, text_count, text, next_button, perf, coords, rules, text_end, select_button])
|
543 |
+
exit_button.click(exit_, inputs=[state], outputs=[map_, results, image_, text_count, text, next_button, perf, coords, rules, text_end, select_button])
|
544 |
+
|
545 |
+
demo.launch(allowed_paths=["custom.ttf"], debug=True)
|
custom.ttf
ADDED
Binary file (97.8 kB). View file
|
|
images/1117948158689819.jpg
ADDED
images/1131929937219152.jpg
ADDED
images/132981045530287.jpg
ADDED
images/1373307973052511.jpg
ADDED
images/175745731085288.jpg
ADDED
images/1906611242827635.jpg
ADDED
images/2569566893353047.jpg
ADDED
images/262995582382853.jpg
ADDED
images/2688573051442365.jpg
ADDED
images/2815113128739763.jpg
ADDED
images/2926954904217999.jpg
ADDED
images/296619375337754.jpg
ADDED
images/2967574993525954.jpg
ADDED
images/297765091851450.jpg
ADDED
images/298997258922815.jpg
ADDED
images/312137333614311.jpg
ADDED
images/314603156759488.jpg
ADDED
images/316183296688571.jpg
ADDED
images/320045362820818.jpg
ADDED
images/3295650104022182.jpg
ADDED
images/3741052562672207.jpg
ADDED
images/374123653951353.jpg
ADDED
images/393835759099320.jpg
ADDED
images/4065883540137904.jpg
ADDED
images/474703873606185.jpg
ADDED
images/477541399978996.jpg
ADDED
images/479790613361193.jpg
ADDED
images/4887875507910938.jpg
ADDED
images/4898606693529789.jpg
ADDED
images/495204901603170.jpg
ADDED
images/503058357484613.jpg
ADDED
images/509010086792207.jpg
ADDED
images/517681129360654.jpg
ADDED
images/521919388810193.jpg
ADDED
images/524652062251031.jpg
ADDED
images/529442031384910.jpg
ADDED
images/537681453897872.jpg
ADDED
images/546314506564628.jpg
ADDED
images/688692811847809.jpg
ADDED
images/732681614433401.jpg
ADDED
images/743171673023530.jpg
ADDED
images/744683329529751.jpg
ADDED
images/797108350987275.jpg
ADDED
images/803654143860113.jpg
ADDED
images/810457353224255.jpg
ADDED
images/827026111505364.jpg
ADDED
images/900447860688305.jpg
ADDED