Spaces:
Running on CPU Upgrade

osv5m commited on
Commit
efbd782
1 Parent(s): 52c2ff8

Upload 55 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. README.md +42 -0
  2. app.py +545 -0
  3. custom.ttf +0 -0
  4. images/1117948158689819.jpg +0 -0
  5. images/1131929937219152.jpg +0 -0
  6. images/132981045530287.jpg +0 -0
  7. images/1373307973052511.jpg +0 -0
  8. images/175745731085288.jpg +0 -0
  9. images/1906611242827635.jpg +0 -0
  10. images/2569566893353047.jpg +0 -0
  11. images/262995582382853.jpg +0 -0
  12. images/2688573051442365.jpg +0 -0
  13. images/2815113128739763.jpg +0 -0
  14. images/2926954904217999.jpg +0 -0
  15. images/296619375337754.jpg +0 -0
  16. images/2967574993525954.jpg +0 -0
  17. images/297765091851450.jpg +0 -0
  18. images/298997258922815.jpg +0 -0
  19. images/312137333614311.jpg +0 -0
  20. images/314603156759488.jpg +0 -0
  21. images/316183296688571.jpg +0 -0
  22. images/320045362820818.jpg +0 -0
  23. images/3295650104022182.jpg +0 -0
  24. images/3741052562672207.jpg +0 -0
  25. images/374123653951353.jpg +0 -0
  26. images/393835759099320.jpg +0 -0
  27. images/4065883540137904.jpg +0 -0
  28. images/474703873606185.jpg +0 -0
  29. images/477541399978996.jpg +0 -0
  30. images/479790613361193.jpg +0 -0
  31. images/4887875507910938.jpg +0 -0
  32. images/4898606693529789.jpg +0 -0
  33. images/495204901603170.jpg +0 -0
  34. images/503058357484613.jpg +0 -0
  35. images/509010086792207.jpg +0 -0
  36. images/517681129360654.jpg +0 -0
  37. images/521919388810193.jpg +0 -0
  38. images/524652062251031.jpg +0 -0
  39. images/529442031384910.jpg +0 -0
  40. images/537681453897872.jpg +0 -0
  41. images/546314506564628.jpg +0 -0
  42. images/688692811847809.jpg +0 -0
  43. images/732681614433401.jpg +0 -0
  44. images/743171673023530.jpg +0 -0
  45. images/744683329529751.jpg +0 -0
  46. images/797108350987275.jpg +0 -0
  47. images/803654143860113.jpg +0 -0
  48. images/810457353224255.jpg +0 -0
  49. images/827026111505364.jpg +0 -0
  50. 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