Spaces:
Running on CPU Upgrade

osv5m commited on
Commit
1ddd64b
·
1 Parent(s): 6aa5374
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. README.md +4 -4
  2. app.py +262 -0
  3. requirements.txt +4 -0
  4. select.csv +51 -0
  5. select/104642768529339.jpg +0 -0
  6. select/1093560081172659.jpg +0 -0
  7. select/1132165610543859.jpg +0 -0
  8. select/1234836596948376.jpg +0 -0
  9. select/1389710078031039.jpg +0 -0
  10. select/1394764281000584.jpg +0 -0
  11. select/141905757860376.jpg +0 -0
  12. select/1441534879556156.jpg +0 -0
  13. select/152454737707008.jpg +0 -0
  14. select/160540152679305.jpg +0 -0
  15. select/1632995353756681.jpg +0 -0
  16. select/169961348374774.jpg +0 -0
  17. select/174371825084733.jpg +0 -0
  18. select/184068376916582.jpg +0 -0
  19. select/189829202981952.jpg +0 -0
  20. select/213668066965052.jpg +0 -0
  21. select/228333062384255.jpg +0 -0
  22. select/2295166667284628.jpg +0 -0
  23. select/2521081868187418.jpg +0 -0
  24. select/299535091653682.jpg +0 -0
  25. select/303007251265953.jpg +0 -0
  26. select/304682731408847.jpg +0 -0
  27. select/308415407537837.jpg +0 -0
  28. select/311028890545710.jpg +0 -0
  29. select/317939186591897.jpg +0 -0
  30. select/320105042867850.jpg +0 -0
  31. select/339675071071472.jpg +0 -0
  32. select/364473748335727.jpg +0 -0
  33. select/383548139454148.jpg +0 -0
  34. select/393732468965312.jpg +0 -0
  35. select/4000903146675702.jpg +0 -0
  36. select/4169751473085624.jpg +0 -0
  37. select/4304508472933745.jpg +0 -0
  38. select/468632131073831.jpg +0 -0
  39. select/477063033350685.jpg +0 -0
  40. select/491911438726571.jpg +0 -0
  41. select/492211698793885.jpg +0 -0
  42. select/503560481463361.jpg +0 -0
  43. select/520441642700354.jpg +0 -0
  44. select/5265020933627697.jpg +0 -0
  45. select/612686053145014.jpg +0 -0
  46. select/769547813760817.jpg +0 -0
  47. select/813929882849515.jpg +0 -0
  48. select/826435791283265.jpg +0 -0
  49. select/838797057047031.jpg +0 -0
  50. select/859387714930005.jpg +0 -0
README.md CHANGED
@@ -1,10 +1,10 @@
1
  ---
2
  title: Plonk
3
- emoji: 📚
4
- colorFrom: green
5
- colorTo: red
6
  sdk: gradio
7
- sdk_version: 4.1.2
8
  app_file: app.py
9
  pinned: false
10
  license: mit
 
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
app.py ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Requires gradio==3.44.0"""
2
+ import io
3
+ import os
4
+ import uuid
5
+ import matplotlib
6
+ import time
7
+ matplotlib.use('Agg')
8
+ from os.path import join
9
+ from PIL import Image
10
+ import pandas as pd
11
+ import reverse_geocoder as rg
12
+ import cartopy.crs as ccrs
13
+ import cartopy.feature as cfeature
14
+ import matplotlib.pyplot as plt
15
+ from math import radians, sin, cos, sqrt, asin, exp
16
+ from collections import defaultdict
17
+
18
+ IMAGE_FOLDER = './select'
19
+ CSV_FILE = './select.csv'
20
+ RESULTS_DIR = './results'
21
+ RULES = """# Plonk 🌍 🌎 🌏
22
+ ## Total time: 50 pictures ~ 5min
23
+ ### How it works:
24
+ - Click on the map 🗺️ (left) to indicate where do you think the image 🖼️ (right) was captured!
25
+ - Click next to move to the next image.
26
+ ⚠️ Your selection is final!
27
+ ### Click "start" to begin...
28
+ """
29
+
30
+ def haversine(lat1, lon1, lat2, lon2):
31
+ if (lat1 is None) or (lon1 is None) or (lat2 is None) or (lon2 is None):
32
+ return 0
33
+ R = 6371 # radius of the earth in km
34
+ dLat = radians(lat2 - lat1)
35
+ dLon = radians(lon2 - lon1)
36
+ a = (
37
+ sin(dLat / 2.0) ** 2
38
+ + cos(radians(lat1)) * cos(radians(lat2)) * sin(dLon / 2.0) ** 2
39
+ )
40
+ c = 2 * asin(sqrt(a))
41
+ distance = R * c
42
+ return distance
43
+
44
+ def geoscore(d):
45
+ return 5000 * exp(-d / 1492.7)
46
+
47
+
48
+ class Engine(object):
49
+ def __init__(self, image_folder, csv_file, cache_path):
50
+ self.image_folder = image_folder
51
+ self.load_images_and_coordinates(csv_file)
52
+ self.cache_path = cache_path
53
+
54
+ # Initialize the score and distance lists
55
+ self.index = 0
56
+ self.stats = defaultdict(list)
57
+
58
+ # Create the figure and canvas only once
59
+ self.fig = plt.Figure(figsize=(10, 6))
60
+ self.ax = self.fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
61
+ self.MIN_LON, self.MAX_LON, self.MIN_LAT, self.MAX_LAT = self.ax.get_extent()
62
+
63
+ def load_images_and_coordinates(self, csv_file):
64
+ # Load the CSV
65
+ df = pd.read_csv(csv_file)
66
+
67
+ # Get the image filenames and their coordinates
68
+ self.images = df['image_id'].tolist()[:]
69
+ self.coordinates = df[['longitude', 'latitude']].values.tolist()[:]
70
+ self.admins = df[['city', 'area', 'region', 'country']].values.tolist()[:]
71
+
72
+
73
+ def isfinal(self):
74
+ return self.index == len(self.images)-1
75
+
76
+ def load_image(self):
77
+ if self.index > len(self.images)-1:
78
+ self.master.update_idletasks()
79
+ self.finish()
80
+
81
+ self.ax.clear()
82
+ self.ax.set_global()
83
+ self.ax.stock_img()
84
+ self.ax.add_feature(cfeature.COASTLINE)
85
+ self.ax.add_feature(cfeature.BORDERS, linestyle=':')
86
+ self.fig.canvas.draw()
87
+ pil = self.get_figure()
88
+ self.set_clock()
89
+ return pil, os.path.join(self.image_folder, f"{self.images[self.index]}.jpg"), '### ' + str(self.index + 1) + '/' + str(len(self.images))
90
+
91
+ def get_figure(self):
92
+ img_buf = io.BytesIO()
93
+ self.fig.savefig(img_buf, format='png', bbox_inches='tight', pad_inches=0, dpi=300)
94
+ pil = Image.open(img_buf)
95
+ self.width, self.height = pil.size
96
+ return pil
97
+
98
+ def normalize_pixels(self, click_lon, click_lat):
99
+ return self.MIN_LON + click_lon * (self.MAX_LON-self.MIN_LON) / self.width, self.MIN_LAT + (self.height - click_lat+1) * (self.MAX_LAT-self.MIN_LAT) / self.height
100
+
101
+ def set_clock(self):
102
+ self.time = time.time()
103
+
104
+ def get_clock(self):
105
+ return time.time() - self.time
106
+
107
+ def click(self, click_lon, click_lat):
108
+ time_elapsed = self.get_clock()
109
+ self.stats['times'].append(time_elapsed)
110
+
111
+ # convert click_lon, click_lat to lat, lon (given that you have the borders of the image)
112
+ # click_lon and click_lat is in pixels
113
+ # lon and lat is in degrees
114
+ click_lon, click_lat = self.normalize_pixels(click_lon, click_lat)
115
+ self.stats['clicked_locations'].append((click_lat, click_lon))
116
+ true_lon, true_lat = self.coordinates[self.index]
117
+
118
+ self.ax.plot(click_lon, click_lat, 'bo', transform=ccrs.Geodetic())
119
+ self.ax.plot([true_lon, click_lon], [true_lat, click_lat], color='blue', linewidth=1, transform=ccrs.Geodetic())
120
+ self.ax.plot(true_lon, true_lat, 'rx', transform=ccrs.Geodetic())
121
+
122
+ distance = haversine(true_lat, true_lon, click_lat, click_lon)
123
+ score = geoscore(distance)
124
+ self.stats['scores'].append(score)
125
+ self.stats['distances'].append(distance)
126
+
127
+ average_text = self.update_average_display()
128
+ result_text = (f"### GeoScore: {score:.0f}, distance: {distance:.0f} km\n ")
129
+
130
+ self.cache(self.index+1, score, distance, (click_lat, click_lon), time_elapsed)
131
+ return self.get_figure(), result_text + average_text
132
+
133
+ def next_image(self):
134
+ # Go to the next image
135
+ self.index += 1
136
+ return self.load_image()
137
+
138
+ def update_average_display(self):
139
+ # Calculate the average values
140
+ avg_score = sum(self.stats['scores']) / len(self.stats['scores']) if self.stats['scores'] else 0
141
+ avg_distance = sum(self.stats['distances']) / len(self.stats['distances']) if self.stats['distances'] else 0
142
+
143
+ # Update the text box
144
+ return f"### Average GeoScore: {avg_score:.0f}, Average distance: {avg_distance:.0f} km"
145
+
146
+ def finish(self):
147
+ clicks = rg.search(self.stats['clicked_locations'])
148
+ clicked_admins = [[click['name'], click['admin2'], click['admin1'], click['cc']] for click in clicks]
149
+
150
+ correct = [0,0,0,0]
151
+ valid = [0,0,0,0]
152
+
153
+ for clicked_admin, true_admin in zip(clicked_admins, self.admins):
154
+ for i in range(4):
155
+ if true_admin[i]!= 'nan':
156
+ valid[i] += 1
157
+ if true_admin[i] == clicked_admin[i]:
158
+ correct[i] += 1
159
+
160
+ avg_city_accuracy = correct[0] / valid[0]
161
+ avg_area_accuracy = correct[1] / valid[1]
162
+ avg_region_accuracy = correct[2] / valid[2]
163
+ avg_country_accuracy = correct[3] / valid[3]
164
+
165
+ avg_score = sum(self.stats['scores']) / len(self.stats['scores']) if self.stats['scores'] else 0
166
+ avg_distance = sum(self.stats['distances']) / len(self.stats['distances']) if self.stats['distances'] else 0
167
+
168
+ final_results = (
169
+ f"Average GeoScore: {avg_score:.0f} \n" +
170
+ f"Average distance: {avg_distance:.0f} km \n" +
171
+ f"Country Acc: {100*avg_country_accuracy:.1f} \n" +
172
+ f"Region Acc: {100*avg_region_accuracy:.1f} \n" +
173
+ f"Area Acc: {100*avg_area_accuracy:.1f} \n" +
174
+ f"City Acc: {100*avg_city_accuracy:.1f}"
175
+ )
176
+
177
+ self.cache_final(final_results)
178
+
179
+ # Update the text box
180
+ return f"# Your stats 🌍\n" + final_results + f" \n# Thanks for playing ❤️"
181
+
182
+ # Function to save the game state
183
+ def cache(self, index, score, distance, location, time_elapsed):
184
+ if not os.path.exists(self.cache_path):
185
+ os.makedirs(self.cache_path)
186
+
187
+ with open(join(self.cache_path, str(index).zfill(2) + '.txt'), 'w') as f:
188
+ print(f"{score}, {distance}, {location[0]}, {location[1]}, {time_elapsed}", file=f)
189
+
190
+ # Function to save the game state
191
+ def cache_final(self, final_results):
192
+ times = ', '.join(map(str, self.stats['times']))
193
+ with open(join(self.cache_path, 'full.txt'), 'w') as f:
194
+ print(f"{final_results}" + '\n Times: ' + times, file=f)
195
+
196
+
197
+
198
+ if __name__ == "__main__":
199
+ import gradio as gr
200
+ def click(state, evt: gr.SelectData):
201
+ if state['clicked']:
202
+ return gr.update(), gr.update()
203
+ x, y = evt.index
204
+ state['clicked'] = True
205
+ image, text = state['engine'].click(x, y)
206
+ return gr.update(value=image), gr.update(value=text)
207
+
208
+ def next_(state):
209
+ if state['clicked']:
210
+ if state['engine'].isfinal():
211
+ text = state['engine'].finish()
212
+ return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(value=text), gr.update(visible=False)
213
+ else:
214
+ fig, image, text = state['engine'].next_image()
215
+ state['clicked'] = False
216
+ return gr.update(value=fig), gr.update(value=image), gr.update(value=text), gr.update(), gr.update()
217
+ else:
218
+ return gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
219
+
220
+ def start(state):
221
+ # create a unique random temporary name under CACHE_DIR
222
+ # generate random hex and make sure it doesn't exist under CACHE_DIR
223
+ while True:
224
+ path = str(uuid.uuid4().hex)
225
+ name = os.path.join(RESULTS_DIR, path)
226
+ if not os.path.exists(name):
227
+ break
228
+
229
+ state['engine'] = Engine(IMAGE_FOLDER, CSV_FILE, name)
230
+ state['clicked'] = False
231
+ fig, image, text = state['engine'].load_image()
232
+
233
+ return (
234
+ gr.update(value=fig, visible=True),
235
+ gr.update(value=image, visible=True),
236
+ gr.update(value=text, visible=True),
237
+ gr.update(visible=True),
238
+ gr.update(visible=True),
239
+ gr.update(visible=False),
240
+ gr.update(visible=False),
241
+ gr.update(visible=False),
242
+ gr.update(visible=False),
243
+ )
244
+
245
+ with gr.Blocks() as demo:
246
+ state = gr.State({})
247
+ rules = gr.Markdown(RULES, visible=True)
248
+
249
+ start_button = gr.Button("Start", visible=True)
250
+ with gr.Row():
251
+ map_ = gr.Image(label='Map', visible=False)
252
+ image_ = gr.Image(label='Image', visible=False)
253
+ with gr.Row():
254
+ text = gr.Markdown("", visible=False)
255
+ text_count = gr.Markdown("", visible=False)
256
+
257
+ next_button = gr.Button("Next", visible=False)
258
+ start_button.click(start, inputs=[state], outputs=[map_, image_, text_count, text, next_button, rules, state, start_button])
259
+ map_.select(click, inputs=[state], outputs=[map_, text])
260
+ next_button.click(next_, inputs=[state], outputs=[map_, image_, text_count, text, next_button])
261
+
262
+ demo.launch(share=True, debug=True)
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ matplotlib==3.7.1
2
+ pandas==2.0.1
3
+ reverse_geocoder==1.5.1
4
+ cartopy==0.22.0
select.csv ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ image_id,longitude,latitude,city,area,region,country
2
+ 860133474581133,117.73186795368,-31.94982838928,Cunderdin,Cunderdin,Western Australia,AU
3
+ 152454737707008,-115.46546196759182,51.171538690221375,Banff,,Alberta,CA
4
+ 393732468965312,18.97688915808808,69.11780259850913,Storsteinnes,Balsfjord,Troms,NO
5
+ 1132165610543859,-58.849666972752,-35.554807349705,San Miguel del Monte,,Buenos Aires,AR
6
+ 1394764281000584,147.03644442101,-43.241833344957,Cygnet,Huon Valley,Tasmania,AU
7
+ 4304508472933745,111.71332546557,2.1383249963969,Sibu,,Sarawak,MY
8
+ 184068376916582,99.957131581331,9.4777282094183,Ko Samui,,Surat Thani,TH
9
+ 311028890545710,118.28358048575,29.724935841625,Xintan,,Anhui Sheng,CN
10
+ 813929882849515,35.055043425688,-15.0184741953,Balaka,Balaka District,Southern Region,MW
11
+ 1093560081172659,-6.2240662119428,56.725251766279,Isle Of Mull,Argyll and Bute,Scotland,GB
12
+ 612686053145014,-96.16942670000188,18.993103300001184,Paso del Toro,,Veracruz,MX
13
+ 1632995353756681,-66.11192506887,18.135853801397,G. L. Garcia,,Cayey,PR
14
+ 317939186591897,55.555796187621,25.533114073286,Umm al Qaywayn,,Umm al Qaywayn,AE
15
+ 520441642700354,-119.31753516877,50.249882799029,Vernon,Regional District of North Okanagan,British Columbia,CA
16
+ 189829202981952,44.005405251222,41.599785003198,Tsalka,Tsalka,Kvemo Kartli,GE
17
+ 4000903146675702,50.579197062107,36.973620940803,Ramsar,,Mazandaran,IR
18
+ 1389710078031039,13.71181614806,47.399453831847,Schladming,Politischer Bezirk Liezen,Styria,AT
19
+ 949517792450129,9.7177091797716,4.046983959221,Douala,,Littoral,CM
20
+ 769547813760817,34.087808245679,1.0808757622682,Mbale,Mbale District,Eastern Region,UG
21
+ 104642768529339,16.450404476788645,68.31075667561743,Kjopsvik,Tysfjord,Nordland,NO
22
+ 303007251265953,-71.578509002669,-36.390450225908,Coihueco,Provincia de Nuble,Biobio,CL
23
+ 320105042867850,71.468575775224,51.122642222111,Astana,,Astana Qalasy,KZ
24
+ 468632131073831,29.218865526259,65.777072925388,Kuusamo,Koillismaa,Northern Ostrobothnia,FI
25
+ 503560481463361,67.86856499056314,47.49971675692054,Zhezqazghan,,Qaraghandy,KZ
26
+ 304682731408847,-104.84187971601,38.921916584821,Air Force Academy,El Paso County,Colorado,US
27
+ 492211698793885,15.243938905249,11.731445568385,Kousseri,,Far North,CM
28
+ 169961348374774,-66.338308918774,-17.444761411827,Sipe Sipe,,Cochabamba,BO
29
+ 141905757860376,-74.780652186738,10.934612493587,Soledad,,Atlantico,CO
30
+ 957589118406370,-48.46767735367,-8.0935339973733,Conceicao do Araguaia,Conceicao Do Araguaia,Para,BR
31
+ 2521081868187418,-0.40230996669347,32.614639189097,Ain Sefra,,Wilaya de Naama,DZ
32
+ 917445892443616,-1.1248279177789,52.937541598214,West Bridgford,Nottinghamshire,England,GB
33
+ 383548139454148,-1.7849243363171,48.039562839713,Chavagne,Departement d'Ille-et-Vilaine,Brittany,FR
34
+ 859387714930005,147.10522055011,-42.977124432213,Huonville,Huon Valley,Tasmania,AU
35
+ 2295166667284628,7.1359534780749,51.190279172602,Solingen,Regierungsbezirk Dusseldorf,North Rhine-Westphalia,DE
36
+ 826435791283265,-102.14648195121,31.99819818952,Midland,Midland County,Texas,US
37
+ 160540152679305,32.640044964906,54.194653471895,Stodolishche,,Smolensk,RU
38
+ 1441534879556156,131.9022521,43.093777400038,Vladivostok,,Primorskiy,RU
39
+ 213668066965052,-7.7833356565566,41.298426689978,Vila Real,Vila Real,Vila Real,PT
40
+ 491911438726571,62.642283844441,35.982936156486,Serhetabat,,Mary,TM
41
+ 228333062384255,20.03622352221,42.173352559956,Iballe,Rrethi i Pukes,Shkoder,AL
42
+ 339675071071472,18.935150981289,53.3141942187,Wabrzezno,Powiat wabrzeski,Kujawsko-Pomorskie,PL
43
+ 174371825084733,-54.40021860075962,-15.844177431680396,Poxoreo,Poxoreo,Mato Grosso,BR
44
+ 5265020933627697,81.92179231998388,41.36342468712621,Baicheng,,Xinjiang Uygur Zizhiqu,CN
45
+ 364473748335727,-55.731720607751,-27.91313764655,Apostoles,Departamento de Apostoles,Misiones,AR
46
+ 477063033350685,97.735136849091,16.797879002023,Hpa-an,,Kayin,MM
47
+ 1234836596948376,118.2591368156,29.761870407333,Xintan,,Anhui Sheng,CN
48
+ 308415407537837,-6.8798972749875,10.960432369588,Kolondieba,,Sikasso,ML
49
+ 4169751473085624,136.87180806629,50.250143059295,Amursk,,Khabarovsk Krai,RU
50
+ 838797057047031,1.4928102327598,43.618538732481,Balma,Departement de la Haute-Garonne,Midi-Pyrenees,FR
51
+ 299535091653682,171.22063076773,-44.390911705397,Timaru,Timaru District,Canterbury,NZ
select/104642768529339.jpg ADDED
select/1093560081172659.jpg ADDED
select/1132165610543859.jpg ADDED
select/1234836596948376.jpg ADDED
select/1389710078031039.jpg ADDED
select/1394764281000584.jpg ADDED
select/141905757860376.jpg ADDED
select/1441534879556156.jpg ADDED
select/152454737707008.jpg ADDED
select/160540152679305.jpg ADDED
select/1632995353756681.jpg ADDED
select/169961348374774.jpg ADDED
select/174371825084733.jpg ADDED
select/184068376916582.jpg ADDED
select/189829202981952.jpg ADDED
select/213668066965052.jpg ADDED
select/228333062384255.jpg ADDED
select/2295166667284628.jpg ADDED
select/2521081868187418.jpg ADDED
select/299535091653682.jpg ADDED
select/303007251265953.jpg ADDED
select/304682731408847.jpg ADDED
select/308415407537837.jpg ADDED
select/311028890545710.jpg ADDED
select/317939186591897.jpg ADDED
select/320105042867850.jpg ADDED
select/339675071071472.jpg ADDED
select/364473748335727.jpg ADDED
select/383548139454148.jpg ADDED
select/393732468965312.jpg ADDED
select/4000903146675702.jpg ADDED
select/4169751473085624.jpg ADDED
select/4304508472933745.jpg ADDED
select/468632131073831.jpg ADDED
select/477063033350685.jpg ADDED
select/491911438726571.jpg ADDED
select/492211698793885.jpg ADDED
select/503560481463361.jpg ADDED
select/520441642700354.jpg ADDED
select/5265020933627697.jpg ADDED
select/612686053145014.jpg ADDED
select/769547813760817.jpg ADDED
select/813929882849515.jpg ADDED
select/826435791283265.jpg ADDED
select/838797057047031.jpg ADDED
select/859387714930005.jpg ADDED