Files
remote-sensing/run_prediction_new.py
T

307 lines
12 KiB
Python
Executable File

"""
Updated run_prediction function for api_server.py
Uses FeatureExtractor for consistent feature extraction
"""
async def run_prediction(config: PredictionConfig):
"""Chạy prediction process - Sử dụng FeatureExtractor để đồng bộ với training"""
global prediction_status
try:
prediction_status["progress"] = "Đang import thư viện..."
# Import required libraries
import numpy as np
import xarray as xr
from datetime import datetime as dt
import hashlib
from feature_extractor import get_feature_extractor
# Validate bbox
if (config.min_lon < -180 or config.max_lon > 180 or
config.min_lat < -90 or config.max_lat > 90):
raise ValueError(f"Bbox không hợp lệ: ({config.min_lon}, {config.min_lat}, {config.max_lon}, {config.max_lat}). "
f"Phải trong phạm vi (-180, -90, 180, 90)")
prediction_status["progress"] = "Đang load model..."
# Load model using ModelManager
model_manager = get_model_manager()
model, label_encoder, model_metadata = model_manager.load_model(config.model_filename)
# Get feature_mode from metadata (default to 'simple' if not specified)
feature_mode = model_metadata.get("feature_mode", "simple")
required_features = model_metadata.get("features", [])
n_features_expected = model_metadata.get("n_features", len(required_features))
prediction_status["progress"] = f"Model: {model_metadata.get('model_type', 'unknown')}, mode={feature_mode}, features={n_features_expected}"
# Initialize FeatureExtractor with same mode as training
extractor = get_feature_extractor(mode=feature_mode)
# Check if it's a CNN model (PyTorch)
is_cnn_model = hasattr(model, '__class__') and 'CNN' in model.__class__.__name__
if is_cnn_model:
prediction_status["progress"] = "Phát hiện PyTorch CNN model..."
try:
import torch
except ImportError:
raise ImportError("PyTorch required for CNN models. Install: pip install torch")
# Initialize common variables
bbox = [config.min_lon, config.min_lat, config.max_lon, config.max_lat]
time_range = f"{config.start_date}/{config.end_date}"
# ============ LOAD SENTINEL-2 DATA ============
prediction_status["progress"] = "Đang kết nối Microsoft Planetary Computer..."
import pystac_client
import planetary_computer
from odc.stac import load
catalog = pystac_client.Client.open(
"https://planetarycomputer.microsoft.com/api/stac/v1",
modifier=planetary_computer.sign_inplace,
)
prediction_status["progress"] = "Đang tải dữ liệu Sentinel-2..."
s2_search = catalog.search(
collections=["sentinel-2-l2a"],
bbox=bbox,
datetime=time_range,
query={"eo:cloud_cover": {"lt": config.cloud_cover}}
)
s2_items = list(s2_search.items())
if not s2_items:
raise ValueError("Không tìm thấy dữ liệu Sentinel-2 cho khu vực và thời gian này")
s2_items = s2_items[:config.max_scenes]
prediction_status["progress"] = f"Đang xử lý {len(s2_items)} scenes Sentinel-2..."
# Load different bands based on feature mode
if feature_mode == 'simple':
bands_to_load = ["B04", "B08", "SCL"]
else: # temporal or extended
bands_to_load = ["B02", "B03", "B04", "B08", "B11", "SCL"]
s2_data = load(
s2_items,
bbox=bbox,
bands=bands_to_load,
chunks={"time": 1, "x": 2048, "y": 2048},
groupby="solar_day",
resolution=config.resolution
).compute()
prediction_status["progress"] = "Đã load Sentinel-2 data"
# ============ LOAD SENTINEL-1 DATA (RADAR) ============
prediction_status["progress"] = "Đang tải dữ liệu Sentinel-1 (Radar)..."
use_radar = False
vh_data = None
vv_data = None
try:
s1_search = catalog.search(
collections=["sentinel-1-rtc"],
bbox=bbox,
datetime=time_range,
)
s1_items = list(s1_search.items())
if s1_items:
s1_items = s1_items[:config.max_scenes]
s1_data = load(
s1_items,
bbox=bbox,
bands=["vh", "vv"],
chunks={"time": 1, "x": 2048, "y": 2048},
groupby="solar_day",
resolution=config.resolution
).compute()
# Convert to dB
vh_data = 10 * np.log10(s1_data['vh'].where(s1_data['vh'] > 0))
vv_data = 10 * np.log10(s1_data['vv'].where(s1_data['vv'] > 0))
use_radar = True
prediction_status["progress"] = f"Đã load Sentinel-1 data ({len(s1_items)} scenes)"
else:
prediction_status["progress"] = "Không có dữ liệu Sentinel-1, bỏ qua radar features"
except Exception as e:
prediction_status["progress"] = f"Lỗi load Sentinel-1: {str(e)}, bỏ qua radar features"
# ============ APPLY CLOUD MASK ============
prediction_status["progress"] = "Đang xử lý mây..."
if "SCL" in s2_data:
scl = s2_data["SCL"]
# SCL values: 3=cloud shadow, 8=cloud medium, 9=cloud high, 10=cirrus
cloud_mask = (scl == 3) | (scl == 8) | (scl == 9) | (scl == 10)
for band in s2_data.data_vars:
if band != "SCL":
s2_data[band] = s2_data[band].where(~cloud_mask)
# ============ EXTRACT FEATURES ============
prediction_status["progress"] = f"Đang trích xuất features (mode={feature_mode})..."
if feature_mode == 'simple':
# Calculate NDVI for simple mode
nir = s2_data["B08"].astype('float32')
red = s2_data["B04"].astype('float32')
ndvi = (nir - red) / (nir + red + 1e-8)
# Fill NaN
ndvi_filled = ndvi.ffill(dim='time').bfill(dim='time')
# Extract features using FeatureExtractor
features = extractor.extract(
ndvi_data=ndvi_filled,
vh_data=vh_data,
vv_data=vv_data
)
else:
# temporal or extended mode
# Fill NaN values in spectral bands
for band in ["B02", "B03", "B04", "B08", "B11"]:
if band in s2_data:
s2_data[band] = s2_data[band].ffill(dim='time').bfill(dim='time')
# Extract features using FeatureExtractor
features = extractor.extract(
s2_data=s2_data,
vh_data=vh_data,
vv_data=vv_data
)
# Handle NaN values
features = np.nan_to_num(features, nan=0.0)
prediction_status["progress"] = f"Đã extract {features.shape[1]} features cho {features.shape[0]} pixels"
# ============ PREDICT ============
prediction_status["progress"] = "Đang dự đoán..."
# Make prediction
if is_cnn_model:
predictions = model.predict(features)
else:
predictions = model.predict(features)
# Decode labels if label_encoder exists
if label_encoder is not None:
try:
predictions = label_encoder.inverse_transform(predictions.astype(int))
except:
pass
# Reshape to original shape
if feature_mode == 'simple' and 'B08' in s2_data:
# Use B08 to get shape
y_size = len(s2_data.y)
x_size = len(s2_data.x)
else:
y_size = len(s2_data.y)
x_size = len(s2_data.x)
pred_shape = (y_size, x_size)
predictions_2d = predictions.reshape(pred_shape)
# ============ CREATE OUTPUT ============
prediction_status["progress"] = "Đang tạo bản đồ phân loại..."
# Create output xarray
prediction_da = xr.DataArray(
predictions_2d,
coords={
"y": s2_data.y,
"x": s2_data.x
},
dims=["y", "x"],
name="classification"
)
# Save output
output_dir = Path("predictions")
output_dir.mkdir(exist_ok=True)
timestamp = dt.now().strftime("%Y%m%d_%H%M%S")
output_file = output_dir / f"prediction_{timestamp}.tif"
prediction_status["progress"] = "Đang lưu kết quả GeoTIFF..."
# Set CRS and save as GeoTIFF
if hasattr(s2_data, 'rio') and s2_data.rio.crs is not None:
prediction_da.rio.write_crs(s2_data.rio.crs, inplace=True)
else:
prediction_da.rio.write_crs("EPSG:4326", inplace=True)
prediction_da.rio.to_raster(str(output_file), driver="GTiff")
# Generate PNG preview
prediction_status["progress"] = "Đang tạo PNG preview..."
png_file = output_dir / f"prediction_{timestamp}.png"
try:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(12, 10), dpi=150)
im = ax.imshow(predictions_2d, cmap='tab20', interpolation='nearest')
ax.set_title(f'Prediction Result - {timestamp}', fontsize=14, fontweight='bold')
ax.set_xlabel('X (pixels)', fontsize=10)
ax.set_ylabel('Y (pixels)', fontsize=10)
cbar = plt.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
cbar.set_label('Class', rotation=270, labelpad=15)
ax.grid(True, alpha=0.3, linestyle='--', linewidth=0.5)
plt.tight_layout()
plt.savefig(str(png_file), dpi=150, bbox_inches='tight')
plt.close(fig)
print(f"[PNG PREVIEW] Created: {png_file}")
except Exception as e:
print(f"[PNG PREVIEW ERROR] Failed to create PNG: {e}")
png_file = None
# Get unique classes
unique_classes = np.unique(predictions_2d)
unique_classes = unique_classes[~np.isnan(unique_classes)].tolist()
prediction_status["is_predicting"] = False
prediction_status["progress"] = "Hoàn thành! Đang tạo báo cáo..."
prediction_status["output_file"] = str(output_file)
prediction_status["result"] = {
"output_file": str(output_file),
"png_file": str(png_file) if png_file else None,
"shape": list(pred_shape),
"unique_classes": unique_classes,
"bbox": bbox,
"time_range": time_range,
"n_features": features.shape[1],
"feature_mode": feature_mode,
"used_radar": use_radar,
"model_used": config.model_filename
}
# Auto generate prediction report
try:
report_path, _ = generate_prediction_report(prediction_status["result"])
prediction_status["result"]["report_path"] = report_path
prediction_status["result"]["report_filename"] = Path(report_path).name
prediction_status["progress"] = "Hoàn thành! Báo cáo đã được tạo."
print(f"[PREDICTION REPORT] Generated: {report_path}")
except Exception as e:
print(f"[PREDICTION REPORT ERROR] Failed to generate report: {e}")
prediction_status["progress"] = "Hoàn thành! (Không thể tạo báo cáo)"
prediction_status["end_time"] = dt.now().isoformat()
except Exception as e:
prediction_status["is_predicting"] = False
prediction_status["error"] = str(e)
prediction_status["progress"] = f"Lỗi: {str(e)}"
prediction_status["end_time"] = dt.now().isoformat()
import traceback
print(f"[PREDICTION ERROR] {str(e)}")
print(traceback.format_exc())