ziffir commited on
Commit
4dcd0c4
·
verified ·
1 Parent(s): 5e97261

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +408 -132
app.py CHANGED
@@ -1,145 +1,421 @@
1
- import gradio as gr
 
2
  import torch
 
3
  from transformers import AutoImageProcessor, AutoModel
 
4
  from PIL import Image
5
  import numpy as np
6
- import rasterio
7
- from rasterio.warp import reproject, Resampling
8
- from rasterio.crs import CRS
9
- from rasterio.warp import transform_geom
10
- import shapely.geometry
11
- import utm
12
- import requests
 
13
  from io import BytesIO
14
- import os
15
- from huggingface_hub import spaces
16
-
17
- # GPU
18
- device = 'cuda' if torch.cuda.is_available() else 'cpu'
19
- print(f"GPU: {device}")
20
-
21
- # DINOv2
22
- processor = AutoImageProcessor.from_pretrained("facebook/dinov2-base")
23
- model = AutoModel.from_pretrained("facebook/dinov2-base").to(device).eval()
24
-
25
- # S2-NAIP TILE MAPPING
26
- def latlon_to_tile(lat, lon):
27
- src_crs = CRS.from_epsg(4326)
28
- src_point = shapely.geometry.Point(lon, lat)
29
- _, _, zone, _ = utm.from_latlon(lat, lon)
30
- epsg = 32600 + zone
31
- dst_crs = CRS.from_epsg(epsg)
32
- dst_point = transform_geom(src_crs, dst_crs, src_point)
33
- dst_point = shapely.geometry.shape(dst_point)
34
- col = int(dst_point.x / 1.25)
35
- row = int(dst_point.y / -1.25)
36
- tile = f"{epsg}_{col//512}_{row//512}"
37
- tar = f"{epsg}_{col//512//32}_{row//512//32}"
38
- return tile, tar, epsg
39
-
40
- # GERÇEK S2-NAIP GÖRÜNTÜ ÇEK
41
- def fetch_sentinel2_tile(tile_id):
42
- base = "https://huggingface.co/datasets/allenai/s2-naip/resolve/main/sentinel2"
43
- url = f"{base}/{tile_id}_8.tif"
44
- try:
45
- r = requests.get(url, timeout=10)
46
- if r.status_code == 200:
47
- bio = BytesIO(r.content)
48
- with rasterio.open(bio) as src:
49
- img = src.read([1,2,3]) # B04, B03, B02
50
- img = np.clip(img / 3000.0 * 255, 0, 255).astype(np.uint8)
51
- img = img.transpose(1,2,0)
52
- transform = src.transform
53
- crs = src.crs
54
- return Image.fromarray(img), transform, crs
55
- except:
56
- pass
57
- return None, None, None
58
 
59
- @spaces.GPU
60
- def georeference(image, location):
61
- if image is None:
62
- return None, None, None, "Görüntü yükleyin!"
63
 
64
- # KONUM → LAT/LON
65
- locations = {
66
- "seattle": (47.6062, -122.3321),
67
- "whiskeytown": (40.5838, -122.5692),
68
- "los angeles": (34.0522, -118.2437),
69
- "new york": (40.7128, -74.0060),
70
- "san francisco": (37.7749, -122.4194)
71
- }
72
- loc_key = next((k for k in locations if k in location.lower()), "seattle")
73
- lat, lon = locations[loc_key]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
- # TILE BUL
76
- tile_id, tar_id, epsg = latlon_to_tile(lat, lon)
77
- print(f"Tile: {tile_id}")
78
-
79
- # GERÇEK S2 GÖRÜNTÜ ÇEK
80
- ref_img, ref_transform, ref_crs = fetch_sentinel2_tile(tile_id)
81
- if ref_img is None:
82
- # DEMO: Rastgele referans
83
- ref = np.random.randint(50, 200, (64, 64, 3), dtype=np.uint8)
84
- ref_img = Image.fromarray(ref)
85
- ref_transform = rasterio.Affine(10, 0, lon*111000, 0, -10, lat*111000)
86
- ref_crs = f"EPSG:{epsg}"
87
-
88
- # DINOv2 EŞLEŞTİRME
89
- inputs = processor(images=[Image.fromarray(image), ref_img], return_tensors="pt").to(device)
90
- with torch.no_grad():
91
- feats = model(**inputs).last_hidden_state[:, 0]
92
- sim = torch.cosine_similarity(feats[0], feats[1], dim=0).item()
93
-
94
- # HOMOGRAFI (Demo: sabit)
95
- H = np.array([[1.0, 0.0, 30], [0.0, 1.0, 20], [0.0, 0.0, 1.0]])
96
-
97
- # WARP
98
- h, w = image.shape[:2]
99
- output_tif = "georef_output.tif"
100
- profile = {
101
- 'driver': 'GTiff', 'height': h, 'width': w, 'count': 3, 'dtype': 'uint8',
102
- 'crs': ref_crs, 'transform': ref_transform
103
- }
104
- warped = np.stack([image[:,:,i] for i in range(3)])
105
- with rasterio.open(output_tif, 'w', **profile) as dst:
106
- dst.write(warped)
107
-
108
- # GCP
109
- points_file = "gcp.points"
110
- with open(points_file, 'w') as f:
111
- f.write("mapX,mapY,pixelX,pixelY,enable\n")
112
- for px, py in [(0,0), (w-1,0), (w-1,h-1), (0,h-1)]:
113
- mx, my = ref_transform * (px, py)
114
- f.write(f"{mx:.2f},{my:.2f},{px},{py},1\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
- return (
117
- output_tif,
118
- points_file,
119
- ref_img,
120
- f"**BAŞARILI!**\n"
121
- f"**Konum:** {loc_key.title()}\n"
122
- f"**Tile:** `{tile_id}`\n"
123
- f"**Eşleşme:** {sim:.1%}\n"
124
- f"**Cihaz:** {device}"
125
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
- # GRADIO UI
128
- with gr.Blocks() as demo:
129
- gr.Markdown("# AI Georeferencer – S2-NAIP")
130
- gr.Markdown("**ABD geneli gerçek uydu verisi!**")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
- with gr.Row():
133
- with gr.Column():
134
- gr.Image(label="Harita", type="numpy")
135
- gr.Textbox(label="Konum", placeholder="seattle, whiskeytown, la, nyc", value="seattle")
136
- gr.Button("Jeoreferansla").click(
137
- georeference,
138
- [gr.State(), gr.State()],
139
- [gr.File(), gr.File(), gr.Image(), gr.Markdown()]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  )
141
- with gr.Column():
142
- gr.Image(label="Sentinel-2 Referans")
143
- gr.Markdown()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
- demo.launch()
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import os
3
  import torch
4
+ import torch.nn as nn
5
  from transformers import AutoImageProcessor, AutoModel
6
+ import torchvision.transforms as transforms
7
  from PIL import Image
8
  import numpy as np
9
+ import gradio as gr
10
+ import tempfile
11
+ import json
12
+ from datetime import datetime
13
+ import logging
14
+ from datasets import load_dataset
15
+ import matplotlib.pyplot as plt
16
+ import folium
17
  from io import BytesIO
18
+ import base64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
+ # Logging konfigürasyonu
21
+ logging.basicConfig(level=logging.INFO)
22
+ logger = logging.getLogger(__name__)
 
23
 
24
+ class S2NAIPGeoModel(nn.Module):
25
+ """S2-NAIP Dataset için Jeo-Referanslama Modeli"""
26
+
27
+ def __init__(self):
28
+ super(S2NAIPGeoModel, self).__init__()
29
+
30
+ # DINOv2 backbone
31
+ self.dinov2 = AutoModel.from_pretrained("facebook/dinov2-base")
32
+
33
+ # Regresyon başı - koordinat tahmini için
34
+ self.regressor = nn.Sequential(
35
+ nn.Linear(768, 512),
36
+ nn.ReLU(),
37
+ nn.Dropout(0.1),
38
+ nn.Linear(512, 256),
39
+ nn.ReLU(),
40
+ nn.Dropout(0.1),
41
+ nn.Linear(256, 128),
42
+ nn.ReLU(),
43
+ nn.Linear(128, 2) # enlem, boylam
44
+ )
45
+
46
+ def forward(self, pixel_values):
47
+ # Görüntü özelliklerini çıkar
48
+ features = self.dinov2(pixel_values=pixel_values).last_hidden_state
49
+ features = features.mean(dim=1) # Global average pooling
50
+
51
+ # Koordinat tahmini
52
+ coordinates = self.regressor(features)
53
+ return coordinates
54
 
55
+ class S2NAIPGeoSystem:
56
+ """S2-NAIP Jeo-Referanslama Sistemi"""
57
+
58
+ def __init__(self, model_path=None):
59
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
60
+ logger.info(f"Cihaz: {self.device}")
61
+
62
+ # Model ve processor
63
+ self.processor = AutoImageProcessor.from_pretrained("facebook/dinov2-base")
64
+ self.model = S2NAIPGeoModel().to(self.device)
65
+
66
+ # Transformations
67
+ self.transform = transforms.Compose([
68
+ transforms.Resize((224, 224)),
69
+ transforms.ToTensor(),
70
+ transforms.Normalize(
71
+ mean=[0.485, 0.456, 0.406],
72
+ std=[0.229, 0.224, 0.225]
73
+ )
74
+ ])
75
+
76
+ # Model yükleme
77
+ if model_path and os.path.exists(model_path):
78
+ self.model.load_state_dict(torch.load(model_path, map_location=self.device))
79
+ logger.info("Model yüklendi")
80
+ else:
81
+ logger.info("Rastgele ağırlıklı model kullanılıyor")
82
+
83
+ self.model.eval()
84
+
85
+ # Dataset bilgileri
86
+ self.dataset_info = {
87
+ 'name': 'allenai/s2-naip',
88
+ 'description': 'Sentinel-2 ve NAIP görüntü çiftleri',
89
+ 'resolution': '10m (Sentinel-2), 1m (NAIP)'
90
+ }
91
+
92
+ def predict(self, image):
93
+ """Görüntüden koordinat tahmini"""
94
+ try:
95
+ # Görüntüyü yükle ve işle
96
+ if isinstance(image, str):
97
+ image = Image.open(image).convert('RGB')
98
+ elif isinstance(image, np.ndarray):
99
+ image = Image.fromarray(image.astype('uint8')).convert('RGB')
100
+
101
+ # Transform uygula
102
+ processed_image = self.transform(image).unsqueeze(0).to(self.device)
103
+
104
+ with torch.no_grad():
105
+ coordinates = self.model(processed_image)
106
+ coords = coordinates.cpu().numpy()[0]
107
+
108
+ # Gerçek koordinat aralığına uygun hale getir
109
+ lat = float(coords[0] * 180 - 90) # -90 ile 90 arası
110
+ lon = float(coords[1] * 360 - 180) # -180 ile 180 arası
111
+
112
+ # Basit güven skoru (örnek)
113
+ confidence = max(0.0, min(1.0, 1.0 - abs(coords[0]) - abs(coords[1])))
114
+
115
+ result = {
116
+ 'latitude': lat,
117
+ 'longitude': lon,
118
+ 'confidence': float(confidence),
119
+ 'coordinates': [lat, lon],
120
+ 'timestamp': datetime.now().isoformat(),
121
+ 'dataset': self.dataset_info['name']
122
+ }
123
+
124
+ return result
125
+
126
+ except Exception as e:
127
+ logger.error(f"Tahmin hatası: {e}")
128
+ return {
129
+ 'error': str(e),
130
+ 'latitude': 0.0,
131
+ 'longitude': 0.0,
132
+ 'confidence': 0.0
133
+ }
134
+
135
+ def load_sample_data(self, num_samples=5):
136
+ """S2-NAIP datasetinden örnek veriler yükle"""
137
+ try:
138
+ dataset = load_dataset("allenai/s2-naip", split=f"train[:{num_samples}]")
139
+ samples = []
140
+
141
+ for i, item in enumerate(dataset):
142
+ sample = {
143
+ 'sentinel': item['sentinel'],
144
+ 'naip': item['naip'],
145
+ 'index': i,
146
+ 'has_coords': 'lat' in item and 'lon' in item
147
+ }
148
+
149
+ if sample['has_coords']:
150
+ sample['lat'] = item['lat']
151
+ sample['lon'] = item['lon']
152
+
153
+ samples.append(sample)
154
+
155
+ logger.info(f"{len(samples)} örnek yüklendi")
156
+ return samples
157
+
158
+ except Exception as e:
159
+ logger.error(f"Dataset yükleme hatası: {e}")
160
+ return []
161
 
162
+ class GeoVisualizer:
163
+ """Jeo-görselleştirme sınıfı"""
164
+
165
+ def __init__(self):
166
+ self.map_style = 'openstreetmap'
167
+
168
+ def create_map(self, predictions, center=(39, 35), zoom=4):
169
+ """Folium haritası oluştur"""
170
+ try:
171
+ m = folium.Map(location=center, zoom_start=zoom, tiles=self.map_style)
172
+
173
+ for i, pred in enumerate(predictions):
174
+ if 'error' in pred:
175
+ continue
176
+
177
+ lat, lon = pred['latitude'], pred['longitude']
178
+ confidence = pred.get('confidence', 0.5)
179
+
180
+ # Güven skoruna göre renk
181
+ color = 'red' if confidence < 0.3 else 'orange' if confidence < 0.7 else 'green'
182
+
183
+ popup_text = f"""
184
+ <b>Tahmin {i+1}</b><br>
185
+ <b>Koordinatlar:</b> {lat:.4f}, {lon:.4f}<br>
186
+ <b>Güven:</b> {confidence:.2%}
187
+ """
188
+
189
+ folium.Marker(
190
+ [lat, lon],
191
+ popup=folium.Popup(popup_text, max_width=300),
192
+ tooltip=f"Güven: {confidence:.2%}",
193
+ icon=folium.Icon(color=color, icon='info-sign')
194
+ ).add_to(m)
195
+
196
+ # Haritayı HTML olarak kaydet
197
+ with tempfile.NamedTemporaryFile(suffix='.html', delete=False) as tmp:
198
+ m.save(tmp.name)
199
+ return tmp.name
200
+
201
+ except Exception as e:
202
+ logger.error(f"Harita oluşturma hatası: {e}")
203
+ return None
204
+
205
+ def plot_prediction_comparison(self, predictions):
206
+ """Tahmin karşılaştırması grafiği"""
207
+ try:
208
+ fig, ax = plt.subplots(figsize=(10, 6))
209
+
210
+ latitudes = [p['latitude'] for p in predictions if 'error' not in p]
211
+ longitudes = [p['longitude'] for p in predictions if 'error' not in p]
212
+ confidences = [p.get('confidence', 0) for p in predictions if 'error' not in p]
213
+
214
+ scatter = ax.scatter(longitudes, latitudes, c=confidences,
215
+ cmap='viridis', s=100, alpha=0.7)
216
+
217
+ ax.set_xlabel('Boylam')
218
+ ax.set_ylabel('Enlem')
219
+ ax.set_title('Koordinat Tahminleri ve Güven Skorları')
220
+ ax.grid(True, alpha=0.3)
221
+
222
+ # Renk barı ekle
223
+ plt.colorbar(scatter, ax=ax, label='Güven Skoru')
224
+
225
+ # Geçici dosyaya kaydet
226
+ with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
227
+ plt.savefig(tmp.name, dpi=150, bbox_inches='tight')
228
+ plt.close()
229
+ return tmp.name
230
+
231
+ except Exception as e:
232
+ logger.error(f"Grafik oluşturma hatası: {e}")
233
+ return None
234
 
235
+ # Ana uygulama sınıfı
236
+ class S2NAIPGeoApp:
237
+ def __init__(self):
238
+ self.geo_system = S2NAIPGeoSystem()
239
+ self.visualizer = GeoVisualizer()
240
+ self.predictions_history = []
241
+
242
+ # Örnek verileri yükle
243
+ self.sample_data = self.geo_system.load_sample_data(3)
244
+ logger.info("S2-NAIP Jeo-Referanslama Uygulaması başlatıldı")
245
+
246
+ def process_image(self, image):
247
+ """Tek görüntü işleme"""
248
+ result = self.geo_system.predict(image)
249
+
250
+ if 'error' not in result:
251
+ self.predictions_history.append(result)
252
+
253
+ # Harita oluştur
254
+ map_path = self.visualizer.create_map([result])
255
+
256
+ return result, map_path
257
+ else:
258
+ return result, None
259
+
260
+ def process_batch(self, files):
261
+ """Toplu işleme"""
262
+ results = []
263
+
264
+ for file in files:
265
+ try:
266
+ result = self.geo_system.predict(file.name)
267
+ result['filename'] = os.path.basename(file.name)
268
+ results.append(result)
269
+ except Exception as e:
270
+ results.append({
271
+ 'filename': os.path.basename(file.name),
272
+ 'error': str(e)
273
+ })
274
+
275
+ # Başarılı tahminler
276
+ successful = [r for r in results if 'error' not in r]
277
+
278
+ if successful:
279
+ map_path = self.visualizer.create_map(successful)
280
+ plot_path = self.visualizer.plot_prediction_comparison(successful)
281
+ else:
282
+ map_path = None
283
+ plot_path = None
284
+
285
+ batch_result = {
286
+ 'results': results,
287
+ 'summary': {
288
+ 'toplam_gorsel': len(files),
289
+ 'basarili_tahmin': len(successful),
290
+ 'basarisiz_tahmin': len(results) - len(successful),
291
+ 'ortalama_guven': np.mean([r.get('confidence', 0) for r in successful]) if successful else 0
292
+ }
293
+ }
294
+
295
+ self.predictions_history.extend(successful)
296
+
297
+ return batch_result, map_path, plot_path
298
+
299
+ def get_sample_images(self):
300
+ """Örnek görüntüleri getir"""
301
+ samples = []
302
+ for sample in self.sample_data:
303
+ samples.append({
304
+ 'sentinel': sample['sentinel'],
305
+ 'naip': sample['naip'],
306
+ 'coordinates': f"Enlem: {sample.get('lat', 'Bilinmiyor')}, Boylam: {sample.get('lon', 'Bilinmiyor')}"
307
+ })
308
+ return samples
309
 
310
+ # Gradio arayüzü oluştur
311
+ def create_interface():
312
+ app = S2NAIPGeoApp()
313
+
314
+ with gr.Blocks(title="🌍 S2-NAIP Jeo-Referanslama Sistemi", theme=gr.themes.Soft()) as demo:
315
+ gr.Markdown("""
316
+ # 🌍 S2-NAIP Jeo-Referanslama Sistemi
317
+ **Sentinel-2 ve NAIP görüntüleri için AI destekli koordinat tahmini**
318
+
319
+ Bu sistem, `allenai/s2-naip` dataseti kullanılarak geliştirilmiştir.
320
+ """)
321
+
322
+ with gr.Tab("📍 Tek Görüntü Analizi"):
323
+ with gr.Row():
324
+ with gr.Column():
325
+ image_input = gr.Image(
326
+ type="filepath",
327
+ label="Uydu Görüntüsü Yükle",
328
+ height=300
329
+ )
330
+ predict_btn = gr.Button("Koordinatları Tahmin Et", variant="primary")
331
+
332
+ with gr.Column():
333
+ output_json = gr.JSON(label="Tahmin Sonuçları")
334
+ output_map = gr.HTML(label="Harita Görünümü")
335
+
336
+ # Örnekler
337
+ gr.Markdown("### Örnek Görüntüler")
338
+ with gr.Row():
339
+ for i, sample in enumerate(app.sample_data):
340
+ with gr.Column():
341
+ gr.Image(
342
+ value=sample['sentinel'],
343
+ label=f"Sentinel-2 Örnek {i+1}",
344
+ height=150
345
+ )
346
+ gr.Textbox(
347
+ value=f"Koordinatlar: {sample.get('lat', 'Bilinmiyor')}, {sample.get('lon', 'Bilinmiyor')}",
348
+ label="Gerçek Koordinatlar"
349
+ )
350
+
351
+ predict_btn.click(
352
+ fn=app.process_image,
353
+ inputs=image_input,
354
+ outputs=[output_json, output_map]
355
+ )
356
+
357
+ with gr.Tab("📊 Toplu İşleme"):
358
+ with gr.Row():
359
+ with gr.Column():
360
+ batch_files = gr.File(
361
+ file_count="multiple",
362
+ file_types=[".jpg", ".jpeg", ".png", ".tiff"],
363
+ label="Toplu Görüntü Yükle"
364
+ )
365
+ batch_btn = gr.Button("Toplu İşle", variant="primary")
366
+
367
+ with gr.Column():
368
+ batch_output = gr.JSON(label="Toplu Sonuçlar")
369
+ batch_map = gr.HTML(label="Toplu Harita")
370
+ batch_plot = gr.Image(label="Karşılaştırma Grafiği")
371
+
372
+ batch_btn.click(
373
+ fn=app.process_batch,
374
+ inputs=batch_files,
375
+ outputs=[batch_output, batch_map, batch_plot]
376
  )
377
+
378
+ with gr.Tab("ℹ️ Dataset Bilgisi"):
379
+ gr.Markdown("""
380
+ ## S2-NAIP Dataset Hakkında
381
+
382
+ **Dataset:** `allenai/s2-naip`
383
+
384
+ **Açıklama:**
385
+ - Sentinel-2 (10m çözünürlük) ve NAIP (1m çözünürlük) görüntü çiftleri
386
+ - ABD genelinde çeşitli lokasyonlar
387
+ - Her örnek için koordinat bilgisi içerir
388
+
389
+ **Özellikler:**
390
+ - Multi-spektral Sentinel-2 görüntüleri
391
+ - Yüksek çözünürlüklü NAIP görüntüleri
392
+ - Coğrafi koordinat metadata'sı
393
+
394
+ **Kullanım:** Bu dataset, uydu görüntülerinden koordinat tahmini için kullanılmaktadır.
395
+ """)
396
+
397
+ # Dataset istatistikleri
398
+ gr.Markdown("""
399
+ ### Model Bilgisi
400
+ - **Temel Model:** DINOv2 Base
401
+ - **Input Çözünürlük:** 224x224
402
+ - **Çıktı:** Enlem, Boylam koordinatları
403
+ - **Güven Skoru:** Tahmin kalitesi göstergesi
404
+ """)
405
+
406
+ # Uyarı
407
+ gr.Markdown("""
408
+ ---
409
+ ⚠️ **Not:** Bu demo için rastgele ağırlıklar kullanılmaktadır. Gerçek tahminler için modelin eğitilmesi gerekmektedir.
410
+ """)
411
+
412
+ return demo
413
 
414
+ if __name__ == "__main__":
415
+ # Gradio arayüzünü başlat
416
+ demo = create_interface()
417
+ demo.launch(
418
+ server_name="0.0.0.0",
419
+ server_port=7860,
420
+ share=False
421
+ )