landvex: Fixar och tester klara för alla komponenter
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"statistics": {
|
||||
"mean_difference": 1.9875,
|
||||
"max_difference": 120.0,
|
||||
"std_difference": 14.63129672141195,
|
||||
"changed_pixels": 3000,
|
||||
"total_pixels": 160000,
|
||||
"change_percentage": 1.875
|
||||
},
|
||||
"changes": [
|
||||
{
|
||||
"x": 150,
|
||||
"y": 150,
|
||||
"width": 50,
|
||||
"height": 50,
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"x": 200,
|
||||
"y": 150,
|
||||
"width": 50,
|
||||
"height": 50,
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"x": 150,
|
||||
"y": 200,
|
||||
"width": 50,
|
||||
"height": 50,
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"x": 200,
|
||||
"y": 200,
|
||||
"width": 50,
|
||||
"height": 50,
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"x": 100,
|
||||
"y": 300,
|
||||
"width": 50,
|
||||
"height": 50,
|
||||
"severity": "high"
|
||||
}
|
||||
],
|
||||
"analysis_time": "2026-07-04T14:20:57.919816"
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
"""
|
||||
RIVP Pilot 1 - Road Change Detection
|
||||
Analyserar förändringar på vägar med satellitbilder
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
class RoadChangeDetector:
|
||||
"""Detekterar förändringar på vägar med Sentinel-2 bilder"""
|
||||
|
||||
def __init__(self, road_name, bbox):
|
||||
self.road_name = road_name
|
||||
self.bbox = bbox
|
||||
self.observations = []
|
||||
|
||||
def analyze_images(self, image_before, image_after):
|
||||
"""
|
||||
Jämför två satellitbilder och identifierar förändringar
|
||||
|
||||
I verkligheten: NDVI-diff, spektral analys, ML-modell
|
||||
Här: Simulerad analys baserad på kända mönster
|
||||
"""
|
||||
|
||||
changes = []
|
||||
|
||||
# Simulera detektion baserat på datum och vägtyp
|
||||
# I verkligheten: pixel-för-pixel jämförelse
|
||||
|
||||
if self.road_name == "E4":
|
||||
# E4 är en hårt trafikerad motorväg
|
||||
changes = [
|
||||
{
|
||||
"type": "pothole",
|
||||
"location": {"lat": 59.85, "lon": 17.65},
|
||||
"confidence": 0.82,
|
||||
"size_m2": 12,
|
||||
"detected_date": image_after["date"],
|
||||
"severity": "medium"
|
||||
},
|
||||
{
|
||||
"type": "construction",
|
||||
"location": {"lat": 59.88, "lon": 17.72},
|
||||
"confidence": 0.95,
|
||||
"size_m2": 2500,
|
||||
"detected_date": image_after["date"],
|
||||
"severity": "high"
|
||||
}
|
||||
]
|
||||
elif self.road_name == "Länsväg 272":
|
||||
# Mindre väg, mer variation
|
||||
changes = [
|
||||
{
|
||||
"type": "surface_damage",
|
||||
"location": {"lat": 59.92, "lon": 17.55},
|
||||
"confidence": 0.78,
|
||||
"size_m2": 45,
|
||||
"detected_date": image_after["date"],
|
||||
"severity": "low"
|
||||
}
|
||||
]
|
||||
|
||||
return changes
|
||||
|
||||
def calculate_ndvi(self, image):
|
||||
"""Beräkna NDVI (Normaliserad Differens Vegetations Index)"""
|
||||
# I verkligheten: (NIR - Red) / (NIR + Red)
|
||||
# Här: Simulerat värde
|
||||
return 0.45
|
||||
|
||||
def detect_road_surface_changes(self, before, after):
|
||||
"""Detektera förändringar i vägytan"""
|
||||
|
||||
# I verkligheten:
|
||||
# 1. Extrahera vägmask från bild
|
||||
# 2. Jämför spektral signatur före/efter
|
||||
# 3. Klassificera förändringstyp
|
||||
|
||||
changes = self.analyze_images(before, after)
|
||||
|
||||
# Beräkna konfidens
|
||||
for change in changes:
|
||||
# Konfidens baserad på:
|
||||
# - Bildkvalitet (molnighet)
|
||||
# - Förändringsstorlek
|
||||
# - Spektral tydlighet
|
||||
|
||||
base_confidence = change["confidence"]
|
||||
|
||||
# Justera för molnighet
|
||||
cloud_factor = 1.0 - (after.get("cloud_cover", 0) / 100)
|
||||
|
||||
# Justera för storlek (större = lättare att se)
|
||||
size_factor = min(1.0, change["size_m2"] / 100)
|
||||
|
||||
change["adjusted_confidence"] = base_confidence * cloud_factor * (0.5 + 0.5 * size_factor)
|
||||
|
||||
return changes
|
||||
|
||||
class RIVPPilot1:
|
||||
"""RIVP Pilot 1 - Infrastructure Monitoring"""
|
||||
|
||||
def __init__(self):
|
||||
self.roads = [
|
||||
{
|
||||
"name": "E4",
|
||||
"bbox": "17.5,59.8,17.8,60.0",
|
||||
"length_km": 45,
|
||||
"type": "motorway"
|
||||
},
|
||||
{
|
||||
"name": "Länsväg 272",
|
||||
"bbox": "17.4,59.9,17.7,60.1",
|
||||
"length_km": 23,
|
||||
"type": "county_road"
|
||||
}
|
||||
]
|
||||
|
||||
self.detector = RoadChangeDetector("", "")
|
||||
|
||||
def run_analysis(self):
|
||||
"""Kör komplett analys för alla vägar"""
|
||||
|
||||
results = {
|
||||
"pilot": "RIVP-1",
|
||||
"date": datetime.now().isoformat(),
|
||||
"roads_analyzed": len(self.roads),
|
||||
"total_changes": 0,
|
||||
"changes_by_type": {},
|
||||
"roads": []
|
||||
}
|
||||
|
||||
for road in self.roads:
|
||||
print(f"\nAnalyserar: {road['name']}")
|
||||
print(f" Typ: {road['type']}")
|
||||
print(f" Längd: {road['length_km']} km")
|
||||
|
||||
# Simulera bilder före/efter
|
||||
image_before = {
|
||||
"date": "2026-06-01",
|
||||
"cloud_cover": 10
|
||||
}
|
||||
image_after = {
|
||||
"date": "2026-07-01",
|
||||
"cloud_cover": 15
|
||||
}
|
||||
|
||||
# Detektera förändringar
|
||||
self.detector.road_name = road["name"]
|
||||
self.detector.bbox = road["bbox"]
|
||||
|
||||
changes = self.detector.detect_road_surface_changes(image_before, image_after)
|
||||
|
||||
road_result = {
|
||||
"name": road["name"],
|
||||
"changes_detected": len(changes),
|
||||
"changes": changes
|
||||
}
|
||||
|
||||
results["roads"].append(road_result)
|
||||
results["total_changes"] += len(changes)
|
||||
|
||||
# Räkna per typ
|
||||
for change in changes:
|
||||
change_type = change["type"]
|
||||
if change_type not in results["changes_by_type"]:
|
||||
results["changes_by_type"][change_type] = 0
|
||||
results["changes_by_type"][change_type] += 1
|
||||
|
||||
print(f" → {change_type}: {change['severity']} (confidence: {change['adjusted_confidence']:.2f})")
|
||||
|
||||
return results
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("RIVP Pilot 1 - Road Change Detection")
|
||||
print("=" * 60)
|
||||
|
||||
pilot = RIVPPilot1()
|
||||
results = pilot.run_analysis()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("SAMMANFATTNING")
|
||||
print("=" * 60)
|
||||
print(f"Vägar analyserade: {results['roads_analyzed']}")
|
||||
print(f"Totala förändringar: {results['total_changes']}")
|
||||
print(f"\nFörändringar per typ:")
|
||||
for change_type, count in results["changes_by_type"].items():
|
||||
print(f" {change_type}: {count}")
|
||||
|
||||
# Spara resultat
|
||||
output_file = "/home/bernt/.openclaw/workspace/rivp-pilot-1/results.json"
|
||||
with open(output_file, "w") as f:
|
||||
json.dump(results, f, indent=2)
|
||||
|
||||
print(f"\nResultat sparade: {output_file}")
|
||||
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Hämtar verkliga Sentinel-2 bilder från Copernicus Data Space
|
||||
"""
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# Uppsala area bounding box
|
||||
BBOX = {
|
||||
"min_lon": 17.4,
|
||||
"min_lat": 59.8,
|
||||
"max_lon": 17.8,
|
||||
"max_lat": 60.1
|
||||
}
|
||||
|
||||
# Copernicus Data Space API
|
||||
COPERNICUS_URL = "https://catalogue.dataspace.copernicus.eu/odata/v1/Products"
|
||||
|
||||
def search_sentinel_images():
|
||||
"""Sök efter Sentinel-2 bilder för Uppsala-området"""
|
||||
|
||||
# Bygg sökquery
|
||||
params = {
|
||||
"$filter": f"Collection/Name eq 'SENTINEL-2' and OData.CSC.Intersects(area=geography'SRID=4326;POLYGON(({BBOX['min_lon']} {BBOX['min_lat']}, {BBOX['max_lon']} {BBOX['min_lat']}, {BBOX['max_lon']} {BBOX['max_lat']}, {BBOX['min_lon']} {BBOX['max_lat']}, {BBOX['min_lon']} {BBOX['min_lat']}))')",
|
||||
"$orderby": "ContentDate/Start desc",
|
||||
"$top": 5,
|
||||
"$skip": 0
|
||||
}
|
||||
|
||||
print(f"[{datetime.now().isoformat()}] Searching Sentinel-2 images...")
|
||||
print(f"Area: Uppsala ({BBOX['min_lon']}, {BBOX['min_lat']}, {BBOX['max_lon']}, {BBOX['max_lat']})")
|
||||
|
||||
try:
|
||||
response = requests.get(COPERNICUS_URL, params=params, timeout=30)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
products = data.get('value', [])
|
||||
|
||||
print(f"Found {len(products)} products")
|
||||
|
||||
for i, product in enumerate(products[:3]):
|
||||
print(f"\nProduct {i+1}:")
|
||||
print(f" ID: {product.get('Id')}")
|
||||
print(f" Name: {product.get('Name')}")
|
||||
print(f" Date: {product.get('ContentDate', {}).get('Start')}")
|
||||
print(f" Cloud Cover: {product.get('CloudCover', 'N/A')}%")
|
||||
print(f" Size: {product.get('ContentLength', 0) / (1024*1024):.1f} MB")
|
||||
|
||||
# Spara produktinfo
|
||||
with open(f'satellite_product_{i+1}.json', 'w') as f:
|
||||
json.dump(product, f, indent=2)
|
||||
|
||||
return products
|
||||
else:
|
||||
print(f"Error: HTTP {response.status_code}")
|
||||
print(response.text[:500])
|
||||
return []
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {str(e)}")
|
||||
return []
|
||||
|
||||
def download_quicklook(product_id, filename):
|
||||
"""Ladda ner quicklook (förhandsvisning)"""
|
||||
url = f"{COPERNICUS_URL}({product_id})/Products(Quicklook)"
|
||||
|
||||
print(f"\nDownloading quicklook for {product_id}...")
|
||||
|
||||
try:
|
||||
response = requests.get(url, timeout=30)
|
||||
if response.status_code == 200:
|
||||
with open(filename, 'wb') as f:
|
||||
f.write(response.content)
|
||||
print(f"Saved to {filename} ({len(response.content)} bytes)")
|
||||
return True
|
||||
else:
|
||||
print(f"Error: HTTP {response.status_code}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Error: {str(e)}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("="*60)
|
||||
print("SENTINEL-2 SATELLITE IMAGE FETCH")
|
||||
print("="*60)
|
||||
|
||||
products = search_sentinel_images()
|
||||
|
||||
if products:
|
||||
print("\n" + "="*60)
|
||||
print("DOWNLOADING QUICKLOOKS")
|
||||
print("="*60)
|
||||
|
||||
for i, product in enumerate(products[:2]):
|
||||
product_id = product.get('Id')
|
||||
if product_id:
|
||||
download_quicklook(product_id, f"quicklook_{i+1}.jpg")
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("DONE")
|
||||
print("="*60)
|
||||
@@ -0,0 +1,81 @@
|
||||
"""
|
||||
RIVP Pilot 1 - Sentinel-2 Image Fetcher
|
||||
Hämtar satellitbilder för E4/Länsväg 272, Uppsala
|
||||
"""
|
||||
import urllib.request
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
# Bounding box för E4/Länsväg 272, Uppsala
|
||||
# Ungefärlig: 59.8-60.0N, 17.5-17.8E
|
||||
BBOX = "17.5,59.8,17.8,60.0"
|
||||
|
||||
def fetch_sentinel_catalog():
|
||||
"""Hämta katalog över tillgängliga Sentinel-2 bilder"""
|
||||
|
||||
# Copernicus Data Space API
|
||||
url = "https://catalogue.dataspace.copernicus.eu/odata/v1/Products"
|
||||
|
||||
# Query för Sentinel-2 L2A (nivå 2A = atmospheriskt korrigerad)
|
||||
params = {
|
||||
"$filter": f"Collection/Name eq 'SENTINEL-2' and OData.CSC.Intersects(area=geography'SRID=4326;POLYGON(({BBOX.replace(',', ' ')}))')",
|
||||
"$orderby": "ContentDate/Start desc",
|
||||
"$top": 10
|
||||
}
|
||||
|
||||
print("Söker efter Sentinel-2 bilder...")
|
||||
print(f"Område: {BBOX}")
|
||||
|
||||
# För demo: skapa mock-data som representerar vad vi skulle få
|
||||
mock_catalog = {
|
||||
"products": [
|
||||
{
|
||||
"id": "S2A_T33VWG_20260701T104021",
|
||||
"date": "2026-07-01",
|
||||
"cloud_cover": 15,
|
||||
"tile": "33VWG",
|
||||
"size_mb": 850
|
||||
},
|
||||
{
|
||||
"id": "S2B_T33VWG_20260628T103529",
|
||||
"date": "2026-06-28",
|
||||
"cloud_cover": 8,
|
||||
"tile": "33VWG",
|
||||
"size_mb": 820
|
||||
},
|
||||
{
|
||||
"id": "S2A_T33VWG_20260625T104021",
|
||||
"date": "2026-06-25",
|
||||
"cloud_cover": 22,
|
||||
"tile": "33VWG",
|
||||
"size_mb": 900
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return mock_catalog
|
||||
|
||||
def download_quicklook(product_id):
|
||||
"""Hämta quicklook (förhandsvisning) av bild"""
|
||||
|
||||
# I verkligheten: hämta från Copernicus Data Space
|
||||
# För demo: skapa info om var bilden finns
|
||||
|
||||
quicklook_url = f"https://catalogue.dataspace.copernicus.eu/odata/v1/Products({product_id})/Products(Quicklook)"
|
||||
|
||||
return {
|
||||
"product_id": product_id,
|
||||
"quicklook_url": quicklook_url,
|
||||
"status": "available"
|
||||
}
|
||||
|
||||
if __name__ == "__main__":
|
||||
catalog = fetch_sentinel_catalog()
|
||||
|
||||
print(f"\nHittade {len(catalog['products'])} bilder:")
|
||||
for p in catalog["products"]:
|
||||
print(f" {p['date']} - {p['id']} - Moln: {p['cloud_cover']}%")
|
||||
|
||||
print("\nRIVP Pilot 1 - Sentinel-2 data identifierad.")
|
||||
print("Nästa steg: Ladda ner och analysera bilder.")
|
||||
@@ -0,0 +1,125 @@
|
||||
import sqlite3
|
||||
import random
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
conn = sqlite3.connect('rivp.db')
|
||||
c = conn.cursor()
|
||||
|
||||
# Hämta alla vägar - kolla kolumnnamn först
|
||||
c.execute("PRAGMA table_info(roads)")
|
||||
columns = c.fetchall()
|
||||
print("Columns:", [col[1] for col in columns])
|
||||
|
||||
c.execute("SELECT id, name, length_km, bbox, type, county FROM roads")
|
||||
roads = c.fetchall()
|
||||
|
||||
print(f"Generating observations for {len(roads)} roads...")
|
||||
|
||||
observation_types = ['pothole', 'surface_damage', 'crack', 'construction', 'vegetation', 'flooding', 'ice_damage']
|
||||
severities = ['low', 'medium', 'high', 'critical']
|
||||
sources = ['satellite', 'quixzoom', 'manual', 'sensor']
|
||||
|
||||
observation_count = 0
|
||||
|
||||
for road in roads:
|
||||
road_id, name, length_km, bbox, road_type, county = road
|
||||
|
||||
# Antal observationer baserat på väglängd och typ
|
||||
if road_type == 'motorway':
|
||||
num_obs = int(length_km / 10) + random.randint(0, 3)
|
||||
else:
|
||||
num_obs = int(length_km / 15) + random.randint(0, 2)
|
||||
|
||||
for i in range(num_obs):
|
||||
# Generera koordinater inom bounding box
|
||||
bbox_parts = bbox.split(',')
|
||||
min_lon, min_lat, max_lon, max_lat = map(float, bbox_parts)
|
||||
|
||||
lat = random.uniform(min_lat, max_lat)
|
||||
lon = random.uniform(min_lon, max_lon)
|
||||
|
||||
# Observationstyp baserat på säsong
|
||||
month = random.randint(1, 12)
|
||||
if month in [11, 12, 1, 2, 3]:
|
||||
obs_type = random.choice(['pothole', 'ice_damage', 'surface_damage', 'crack'])
|
||||
elif month in [4, 5, 6]:
|
||||
obs_type = random.choice(['construction', 'pothole', 'surface_damage'])
|
||||
elif month in [7, 8]:
|
||||
obs_type = random.choice(['vegetation', 'construction', 'surface_damage'])
|
||||
else:
|
||||
obs_type = random.choice(['pothole', 'flooding', 'surface_damage', 'crack'])
|
||||
|
||||
# Konfidens baserat på källa
|
||||
source = random.choice(sources)
|
||||
if source == 'satellite':
|
||||
confidence = random.uniform(0.6, 0.9)
|
||||
elif source == 'quixzoom':
|
||||
confidence = random.uniform(0.75, 0.95)
|
||||
elif source == 'manual':
|
||||
confidence = random.uniform(0.85, 0.99)
|
||||
else:
|
||||
confidence = random.uniform(0.5, 0.8)
|
||||
|
||||
# Severity
|
||||
if obs_type in ['construction']:
|
||||
severity = random.choice(['medium', 'high'])
|
||||
elif obs_type in ['pothole', 'crack']:
|
||||
severity = random.choice(['low', 'medium', 'high'])
|
||||
elif obs_type in ['flooding', 'ice_damage']:
|
||||
severity = random.choice(['medium', 'high', 'critical'])
|
||||
else:
|
||||
severity = random.choice(['low', 'medium'])
|
||||
|
||||
# Storlek
|
||||
if obs_type == 'construction':
|
||||
size_m2 = random.uniform(500, 5000)
|
||||
elif obs_type == 'pothole':
|
||||
size_m2 = random.uniform(1, 20)
|
||||
elif obs_type == 'vegetation':
|
||||
size_m2 = random.uniform(50, 500)
|
||||
else:
|
||||
size_m2 = random.uniform(10, 200)
|
||||
|
||||
# Datum
|
||||
day = random.randint(1, 28)
|
||||
detected_date = f"2026-{month:02d}-{day:02d}"
|
||||
|
||||
# Verifierad?
|
||||
verified = 1 if confidence > 0.8 else 0
|
||||
|
||||
c.execute('''
|
||||
INSERT INTO observations
|
||||
(road_id, observation_type, latitude, longitude, confidence, severity, size_m2, detected_date, verified, source)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
''', (road_id, obs_type, lat, lon, confidence, severity, size_m2, detected_date, verified, source))
|
||||
|
||||
observation_count += 1
|
||||
|
||||
conn.commit()
|
||||
|
||||
# Räkna totala
|
||||
conn = sqlite3.connect('rivp.db')
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT COUNT(*) FROM observations")
|
||||
total = c.fetchone()[0]
|
||||
print(f"Total observations in database: {total}")
|
||||
|
||||
# Visa fördelning
|
||||
c.execute("SELECT observation_type, COUNT(*) FROM observations GROUP BY observation_type")
|
||||
print("\nBy type:")
|
||||
for row in c.fetchall():
|
||||
print(f" {row[0]}: {row[1]}")
|
||||
|
||||
c.execute("SELECT source, COUNT(*) FROM observations GROUP BY source")
|
||||
print("\nBy source:")
|
||||
for row in c.fetchall():
|
||||
print(f" {row[0]}: {row[1]}")
|
||||
|
||||
c.execute("SELECT severity, COUNT(*) FROM observations GROUP BY severity")
|
||||
print("\nBy severity:")
|
||||
for row in c.fetchall():
|
||||
print(f" {row[0]}: {row[1]}")
|
||||
|
||||
conn.close()
|
||||
|
||||
print(f"\nGenerated {observation_count} new observations")
|
||||
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Verklig förändringsdetektion med bildbehandling
|
||||
Använder numpy för att analysera bildskillnader
|
||||
"""
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
def analyze_image_difference(image_path1, image_path2):
|
||||
"""
|
||||
Analysera skillnader mellan två bilder
|
||||
Returnerar förändringskarta och statistik
|
||||
"""
|
||||
if not os.path.exists(image_path1) or not os.path.exists(image_path2):
|
||||
return None
|
||||
|
||||
# Ladda bilder
|
||||
img1 = Image.open(image_path1).convert('L') # Gråskala
|
||||
img2 = Image.open(image_path2).convert('L')
|
||||
|
||||
# Konvertera till numpy arrays
|
||||
arr1 = np.array(img1)
|
||||
arr2 = np.array(img2)
|
||||
|
||||
# Beräkna skillnad
|
||||
diff = np.abs(arr2.astype(float) - arr1.astype(float))
|
||||
|
||||
# Statistik
|
||||
stats = {
|
||||
"mean_difference": float(np.mean(diff)),
|
||||
"max_difference": float(np.max(diff)),
|
||||
"std_difference": float(np.std(diff)),
|
||||
"changed_pixels": int(np.sum(diff > 30)),
|
||||
"total_pixels": diff.size,
|
||||
"change_percentage": float(np.sum(diff > 30) / diff.size * 100)
|
||||
}
|
||||
|
||||
# Identifiera förändringsområden
|
||||
threshold = 50
|
||||
changes = []
|
||||
|
||||
# Hitta konturer av förändrade områden (förenklad)
|
||||
change_mask = diff > threshold
|
||||
|
||||
# Dela upp i grid och hitta aktiva celler
|
||||
h, w = change_mask.shape
|
||||
grid_size = 50
|
||||
|
||||
for i in range(0, h, grid_size):
|
||||
for j in range(0, w, grid_size):
|
||||
cell = change_mask[i:i+grid_size, j:j+grid_size]
|
||||
if np.sum(cell) > (grid_size * grid_size * 0.1): # 10% av cellen förändrad
|
||||
changes.append({
|
||||
"x": j,
|
||||
"y": i,
|
||||
"width": grid_size,
|
||||
"height": grid_size,
|
||||
"severity": "high" if np.sum(cell) > (grid_size * grid_size * 0.3) else "medium"
|
||||
})
|
||||
|
||||
return {
|
||||
"statistics": stats,
|
||||
"changes": changes[:20], # Begränsa till 20 förändringar
|
||||
"analysis_time": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
def generate_synthetic_comparison():
|
||||
"""
|
||||
Generera syntetiska före/efter-bilder för demo
|
||||
"""
|
||||
# Skapa två bilder med kända skillnader
|
||||
size = (400, 400)
|
||||
|
||||
# Bild 1: "Före"
|
||||
img1 = np.ones(size, dtype=np.uint8) * 128 # Grå bakgrund
|
||||
# Lägg till "väg"
|
||||
img1[180:220, :] = 80 # Mörkare väg
|
||||
|
||||
# Bild 2: "Efter" (med förändringar)
|
||||
img2 = img1.copy()
|
||||
# Lägg till "hål" i vägen
|
||||
img2[190:210, 150:250] = 200 # Ljust hål
|
||||
# Lägg till "skada"
|
||||
img2[300:320, 100:150] = 50 # Mörk skada
|
||||
|
||||
# Spara
|
||||
Image.fromarray(img1).save('synthetic_before.png')
|
||||
Image.fromarray(img2).save('synthetic_after.png')
|
||||
|
||||
print("Generated synthetic comparison images")
|
||||
return 'synthetic_before.png', 'synthetic_after.png'
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("="*60)
|
||||
print("REAL CHANGE DETECTION")
|
||||
print("="*60)
|
||||
|
||||
# Generera syntetiska bilder för demo
|
||||
before, after = generate_synthetic_comparison()
|
||||
|
||||
# Analysera skillnader
|
||||
result = analyze_image_difference(before, after)
|
||||
|
||||
if result:
|
||||
print("\nAnalysis Results:")
|
||||
print(f" Mean difference: {result['statistics']['mean_difference']:.2f}")
|
||||
print(f" Max difference: {result['statistics']['max_difference']:.2f}")
|
||||
print(f" Changed pixels: {result['statistics']['changed_pixels']:,}")
|
||||
print(f" Change percentage: {result['statistics']['change_percentage']:.2f}%")
|
||||
print(f"\nDetected {len(result['changes'])} change areas")
|
||||
|
||||
# Spara resultat
|
||||
with open('change_detection_result.json', 'w') as f:
|
||||
json.dump(result, f, indent=2)
|
||||
|
||||
print("\nResult saved to change_detection_result.json")
|
||||
else:
|
||||
print("Failed to analyze images")
|
||||
|
||||
print("="*60)
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"pilot": "RIVP-1",
|
||||
"date": "2026-07-04T13:31:25.731410",
|
||||
"roads_analyzed": 2,
|
||||
"total_changes": 3,
|
||||
"changes_by_type": {
|
||||
"pothole": 1,
|
||||
"construction": 1,
|
||||
"surface_damage": 1
|
||||
},
|
||||
"roads": [
|
||||
{
|
||||
"name": "E4",
|
||||
"changes_detected": 2,
|
||||
"changes": [
|
||||
{
|
||||
"type": "pothole",
|
||||
"location": {
|
||||
"lat": 59.85,
|
||||
"lon": 17.65
|
||||
},
|
||||
"confidence": 0.82,
|
||||
"size_m2": 12,
|
||||
"detected_date": "2026-07-01",
|
||||
"severity": "medium",
|
||||
"adjusted_confidence": 0.39032
|
||||
},
|
||||
{
|
||||
"type": "construction",
|
||||
"location": {
|
||||
"lat": 59.88,
|
||||
"lon": 17.72
|
||||
},
|
||||
"confidence": 0.95,
|
||||
"size_m2": 2500,
|
||||
"detected_date": "2026-07-01",
|
||||
"severity": "high",
|
||||
"adjusted_confidence": 0.8075
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "L\u00e4nsv\u00e4g 272",
|
||||
"changes_detected": 1,
|
||||
"changes": [
|
||||
{
|
||||
"type": "surface_damage",
|
||||
"location": {
|
||||
"lat": 59.92,
|
||||
"lon": 17.55
|
||||
},
|
||||
"confidence": 0.78,
|
||||
"size_m2": 45,
|
||||
"detected_date": "2026-07-01",
|
||||
"severity": "low",
|
||||
"adjusted_confidence": 0.480675
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"@odata.mediaContentType": "application/octet-stream",
|
||||
"Id": "3684c3a1-62b3-44e9-8255-9fe73546dbc3",
|
||||
"Name": "S2A_MSIL2A_20260701T101701_N0512_R065_T33VXG_20260701T170611.SAFE",
|
||||
"ContentType": "application/octet-stream",
|
||||
"ContentLength": 950614434,
|
||||
"OriginDate": "2026-07-01T17:59:11.000000Z",
|
||||
"PublicationDate": "2026-07-01T18:04:34.067210Z",
|
||||
"ModificationDate": "2026-07-01T18:05:56.266583Z",
|
||||
"Online": true,
|
||||
"EvictionDate": "9999-12-31T23:59:59.999999Z",
|
||||
"S3Path": "/eodata/Sentinel-2/MSI/L2A/2026/07/01/S2A_MSIL2A_20260701T101701_N0512_R065_T33VXG_20260701T170611.SAFE",
|
||||
"Checksum": [
|
||||
{
|
||||
"Value": "9734b1f6f6adb54d8420bcd9fb7ac174",
|
||||
"Algorithm": "MD5",
|
||||
"ChecksumDate": "2026-07-01T18:04:30.982833Z"
|
||||
},
|
||||
{
|
||||
"Value": "7f43533a4ada51fedec8b4a19cd8553278f28b6bd314db9657f308b6e2b0c333",
|
||||
"Algorithm": "BLAKE3",
|
||||
"ChecksumDate": "2026-07-01T18:04:32.941191Z"
|
||||
}
|
||||
],
|
||||
"ContentDate": {
|
||||
"Start": "2026-07-01T10:17:01.024000Z",
|
||||
"End": "2026-07-01T10:17:01.024000Z"
|
||||
},
|
||||
"Footprint": "geography'SRID=4326;POLYGON ((16.816281226545698 60.42407827988155, 16.763183117311936 59.438625858979684, 18.69570505815484 59.39819844853761, 18.80675171611126 60.382025432644134, 16.816281226545698 60.42407827988155))'",
|
||||
"GeoFootprint": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
16.816281226545698,
|
||||
60.42407827988155
|
||||
],
|
||||
[
|
||||
16.763183117311936,
|
||||
59.438625858979684
|
||||
],
|
||||
[
|
||||
18.69570505815484,
|
||||
59.39819844853761
|
||||
],
|
||||
[
|
||||
18.80675171611126,
|
||||
60.382025432644134
|
||||
],
|
||||
[
|
||||
16.816281226545698,
|
||||
60.42407827988155
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"@odata.mediaContentType": "application/octet-stream",
|
||||
"Id": "51d83970-6a28-46c9-99c1-c4b75824979a",
|
||||
"Name": "S2A_MSIL1C_20260701T101701_N0512_R065_T34VCM_20260701T153545.SAFE",
|
||||
"ContentType": "application/octet-stream",
|
||||
"ContentLength": 665159389,
|
||||
"OriginDate": "2026-07-01T17:03:32.000000Z",
|
||||
"PublicationDate": "2026-07-01T17:09:15.021891Z",
|
||||
"ModificationDate": "2026-07-01T17:12:05.609183Z",
|
||||
"Online": true,
|
||||
"EvictionDate": "9999-12-31T23:59:59.999999Z",
|
||||
"S3Path": "/eodata/Sentinel-2/MSI/L1C/2026/07/01/S2A_MSIL1C_20260701T101701_N0512_R065_T34VCM_20260701T153545.SAFE",
|
||||
"Checksum": [
|
||||
{
|
||||
"Value": "7a656572d2f2df9c7b56e311faab6d32",
|
||||
"Algorithm": "MD5",
|
||||
"ChecksumDate": "2026-07-01T17:09:11.845093Z"
|
||||
},
|
||||
{
|
||||
"Value": "f289c598b5e5ba35355287ce91136a73089bfba81fccd6779d6b0d9c868d27a6",
|
||||
"Algorithm": "BLAKE3",
|
||||
"ChecksumDate": "2026-07-01T17:09:13.218480Z"
|
||||
}
|
||||
],
|
||||
"ContentDate": {
|
||||
"Start": "2026-07-01T10:17:01.024000Z",
|
||||
"End": "2026-07-01T10:17:01.024000Z"
|
||||
},
|
||||
"Footprint": "geography'SRID=4326;POLYGON ((19.38192461835227 60.008734911083124, 19.36162448520634 60.42638503415989, 17.370638358989392 60.386985996095795, 17.4765375427145 59.402967480991144, 18.932854703941825 59.43150320328048, 18.969031861516253 59.47891253116559, 19.0774436459542 59.61980150469789, 19.186652320883013 59.76056091329817, 19.296860011181227 59.90117745706819, 19.38192461835227 60.008734911083124))'",
|
||||
"GeoFootprint": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
19.38192461835227,
|
||||
60.008734911083124
|
||||
],
|
||||
[
|
||||
19.36162448520634,
|
||||
60.42638503415989
|
||||
],
|
||||
[
|
||||
17.370638358989392,
|
||||
60.386985996095795
|
||||
],
|
||||
[
|
||||
17.4765375427145,
|
||||
59.402967480991144
|
||||
],
|
||||
[
|
||||
18.932854703941825,
|
||||
59.43150320328048
|
||||
],
|
||||
[
|
||||
18.969031861516253,
|
||||
59.47891253116559
|
||||
],
|
||||
[
|
||||
19.0774436459542,
|
||||
59.61980150469789
|
||||
],
|
||||
[
|
||||
19.186652320883013,
|
||||
59.76056091329817
|
||||
],
|
||||
[
|
||||
19.296860011181227,
|
||||
59.90117745706819
|
||||
],
|
||||
[
|
||||
19.38192461835227,
|
||||
60.008734911083124
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"@odata.mediaContentType": "application/octet-stream",
|
||||
"Id": "ac3f1cc2-890d-4823-b181-2e390cfbe1ad",
|
||||
"Name": "S2A_MSIL1C_20260701T101701_N0512_R065_T33VXG_20260701T153545.SAFE",
|
||||
"ContentType": "application/octet-stream",
|
||||
"ContentLength": 744494318,
|
||||
"OriginDate": "2026-07-01T17:02:40.000000Z",
|
||||
"PublicationDate": "2026-07-01T17:06:53.893148Z",
|
||||
"ModificationDate": "2026-07-01T17:07:26.034915Z",
|
||||
"Online": true,
|
||||
"EvictionDate": "9999-12-31T23:59:59.999999Z",
|
||||
"S3Path": "/eodata/Sentinel-2/MSI/L1C/2026/07/01/S2A_MSIL1C_20260701T101701_N0512_R065_T33VXG_20260701T153545.SAFE",
|
||||
"Checksum": [
|
||||
{
|
||||
"Value": "eebb10196271ee40e7a5b980f30e0a0a",
|
||||
"Algorithm": "MD5",
|
||||
"ChecksumDate": "2026-07-01T17:06:51.027873Z"
|
||||
},
|
||||
{
|
||||
"Value": "9e449a8a1fa144f9db9cc0cadf71a8f4ef70f1ca6bc917f4783b8785f8c48d49",
|
||||
"Algorithm": "BLAKE3",
|
||||
"ChecksumDate": "2026-07-01T17:06:52.171476Z"
|
||||
}
|
||||
],
|
||||
"ContentDate": {
|
||||
"Start": "2026-07-01T10:17:01.024000Z",
|
||||
"End": "2026-07-01T10:17:01.024000Z"
|
||||
},
|
||||
"Footprint": "geography'SRID=4326;POLYGON ((16.816281226545698 60.42407827988155, 16.763183117311936 59.438625858979684, 18.69570505815484 59.39819844853761, 18.80675171611126 60.382025432644134, 16.816281226545698 60.42407827988155))'",
|
||||
"GeoFootprint": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [
|
||||
[
|
||||
[
|
||||
16.816281226545698,
|
||||
60.42407827988155
|
||||
],
|
||||
[
|
||||
16.763183117311936,
|
||||
59.438625858979684
|
||||
],
|
||||
[
|
||||
18.69570505815484,
|
||||
59.39819844853761
|
||||
],
|
||||
[
|
||||
18.80675171611126,
|
||||
60.382025432644134
|
||||
],
|
||||
[
|
||||
16.816281226545698,
|
||||
60.42407827988155
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 998 B |
Reference in New Issue
Block a user