martedì 24 febbraio 2026

AsteroidOS TicWatch Pro 2018 wf12096

Linux al polso.. dopo quasi 8 anni riprovo AsteroidOs e devo dire che non e' niente male




Per sincronizzare con il telefono si usa la app che AsteroidOs Syncsi trova su F-Droid. Al momento di fare l'onboarding la app rimaneva bloccata per i permessi di notifica...si deve cercare il menu della foto sottostante ed abilitare esplicitamente a meno


 

dati di Plastic Litter Project

Il progetto prevede il posizionamento di target di plastica in mare ed il successivo telerilevamento  

I target hanno una superficie di 4 metri quadrati in polipropilene e polietilene 

Per ritrovare la posizione all'interno dell'immagine Sentinel 2 ho cerchiato di rosso il golfo (Golfo di Gera Grecia)

 

Le coordinate sono Lat 39 02' 21'' Lon 26 31' 28''

I dati vengono distribuiti sia in formato L1C che atmosfericamente corretto tramite ACOLITE (modalita' water) in formato netcdf

Da satellite il bersaglio, nonostante sia decisamente piu' piccolo di un pixel, risulta influenzare la firma di almeno 9 pixel con quello centrale piu' puro 



Dato L1C

I dati i Acolite sono divisi in rhot (top of atmosphere), rhos (riflettanza), e rhow (water leaving reflectance) 

rhos target 1

rhos target 2

 


 

 

 

 

 

 

 

SpectralFormer

 Sulla base del post precedente ho provato SpectralFormer  (https://ieeexplore.ieee.org/document/9627165) sempre sui dati di Pavia

 


 I risultati non sono ottimali 



import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
import numpy as np
import scipy.io as sio
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, cohen_kappa_score, accuracy_score
import matplotlib.pyplot as plt
import os

# ==========================================
# Configuration
# ==========================================
class Config:
data_path = './'
img_mat = 'PaviaU.mat'
gt_mat = 'PaviaU_gt.mat'
# Hyperparameters
patch_size = 9 # Spatial patch size (9x9)
pca_components = 30 # Reduce 103 bands to 30
num_classes = 9
train_ratio = 0.01 # 1% training data (standard for HSI)
# Model Hyperparameters
embed_dim = 64 # Embedding dimension
num_heads = 4
num_layers = 3 # Number of Transformer blocks
mlp_ratio = 2.0
dropout = 0.1
# Training
batch_size = 64
epochs = 50
learning_rate = 0.001
weight_decay = 1e-4
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

cfg = Config()

# ==========================================
# Data Loading & Preprocessing
# ==========================================
def load_pavia_data(cfg):
"""Loads Pavia University data and applies PCA."""
img_path = os.path.join(cfg.data_path, cfg.img_mat)
gt_path = os.path.join(cfg.data_path, cfg.gt_mat)
# Load .mat files
data = sio.loadmat(img_path)['paviaU']
gt = sio.loadmat(gt_path)['paviaU_gt']
# Apply PCA to reduce spectral dimensionality
h, w, b = data.shape
data_2d = data.reshape(-1, b)
pca = PCA(n_components=cfg.pca_components, whiten=True)
data_pca = pca.fit_transform(data_2d)
data_pca = data_pca.reshape(h, w, cfg.pca_components)
return data_pca, gt

def create_patches(data, gt, patch_size):
"""Creates 3D patches and handles padding."""
h, w, b = data.shape
pad = patch_size // 2
# Pad the image and ground truth
data_padded = np.pad(data, ((pad, pad), (pad, pad), (0, 0)), mode='reflect')
gt_padded = np.pad(gt, ((pad, pad), (pad, pad)), mode='constant', constant_values=0)
patches = []
labels = []
# Iterate over valid pixels (where gt > 0)
y_indices, x_indices = np.where(gt > 0)
for y, x in zip(y_indices, x_indices):
# Extract patch (centered at y, x in original coords -> y+pad, x+pad in padded)
patch = data_padded[y:y+patch_size, x:x+patch_size, :]
patches.append(patch)
labels.append(gt[y, x] - 1) # Labels in Pavia are 1-9, convert to 0-8
return np.array(patches), np.array(labels)

class HSIDataset(Dataset):
def __init__(self, patches, labels):
# Permute to (N, C, H, W) format for Conv2d
self.patches = torch.FloatTensor(patches).permute(0, 3, 1, 2)
self.labels = torch.LongTensor(labels)
def __len__(self):
return len(self.labels)
def __getitem__(self, idx):
return self.patches[idx], self.labels[idx]

# ==========================================
# SpectralFormer Model (FIXED)
# ==========================================
class MSA(nn.Module):
"""Multi-Head Self-Attention"""
def __init__(self, dim, num_heads, dropout=0.1):
super().__init__()
assert dim % num_heads == 0, "embed_dim must be divisible by num_heads"
self.num_heads = num_heads
self.scale = (dim // num_heads) ** -0.5
self.qkv = nn.Linear(dim, dim * 3, bias=True)
self.attn_drop = nn.Dropout(dropout)
self.proj = nn.Linear(dim, dim)
self.proj_drop = nn.Dropout(dropout)

def forward(self, x):
B, N, C = x.shape
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
q, k, v = qkv[0], qkv[1], qkv[2]
attn = (q @ k.transpose(-2, -1)) * self.scale
attn = attn.softmax(dim=-1)
attn = self.attn_drop(attn)
x = (attn @ v).transpose(1, 2).reshape(B, N, C)
x = self.proj(x)
x = self.proj_drop(x)
return x


class TransformerBlock(nn.Module):
"""Standard Transformer Block with LayerNorm"""
def __init__(self, dim, num_heads, mlp_ratio=2.0, dropout=0.1):
super().__init__()
self.norm1 = nn.LayerNorm(dim)
self.attn = MSA(dim, num_heads, dropout)
self.norm2 = nn.LayerNorm(dim)
mlp_hidden_dim = int(dim * mlp_ratio)
self.mlp = nn.Sequential(
nn.Linear(dim, mlp_hidden_dim),
nn.GELU(),
nn.Dropout(dropout),
nn.Linear(mlp_hidden_dim, dim),
nn.Dropout(dropout)
)

def forward(self, x):
x = x + self.attn(self.norm1(x))
x = x + self.mlp(self.norm2(x))
return x


class SpectralFormer(nn.Module):
"""
SpectralFormer Implementation (FIXED VERSION).
Key fixes:
1. Uses BatchNorm2d after Conv2d (not LayerNorm) to match (B,C,H,W) format
2. Applies LayerNorm only after flattening to (B, N, C) sequence format
3. Proper positional encoding initialization
"""
def __init__(self, cfg):
super().__init__()
self.patch_size = cfg.patch_size
self.embed_dim = cfg.embed_dim
self.num_layers = cfg.num_layers
# 1. Patch Embedding: 3D patch -> embedded token
# Input: (B, pca_components, patch_size, patch_size)
# Output: (B, embed_dim, 1, 1) since kernel_size=patch_size
self.patch_embed = nn.Sequential(
nn.Conv2d(cfg.pca_components, cfg.embed_dim,
kernel_size=cfg.patch_size, stride=cfg.patch_size),
nn.BatchNorm2d(cfg.embed_dim), # ✓ FIXED: BatchNorm for (B,C,H,W)
nn.GELU()
)
# 2. Positional Encoding: learnable, properly initialized
# Sequence length = 1 (single token per patch)
self.pos_embed = nn.Parameter(torch.randn(1, 1, cfg.embed_dim) * 0.02)
# 3. Transformer Blocks
self.blocks = nn.ModuleList([
TransformerBlock(cfg.embed_dim, cfg.num_heads, cfg.mlp_ratio, cfg.dropout)
for _ in range(cfg.num_layers)
])
# 4. Classification Head
self.norm = nn.LayerNorm(cfg.embed_dim) # ✓ LayerNorm works here: input is (B, 1, embed_dim)
self.head = nn.Linear(cfg.embed_dim, cfg.num_classes)
# Weight initialization
self.apply(self._init_weights)

def _init_weights(self, m):
if isinstance(m, nn.Linear):
torch.nn.init.xavier_uniform_(m.weight)
if m.bias is not None:
nn.init.constant_(m.bias, 0)
elif isinstance(m, (nn.LayerNorm, nn.BatchNorm2d)):
nn.init.constant_(m.bias, 0)
nn.init.constant_(m.weight, 1.0)
elif isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')

def forward(self, x):
# x shape: (B, pca_components, patch_size, patch_size)
B = x.shape[0]
# Embed patches: (B, C, 9, 9) -> (B, embed_dim, 1, 1)
x = self.patch_embed(x)
# Flatten to sequence format for Transformer: (B, N, embed_dim)
# N = 1 since spatial dims collapsed to 1x1
x = x.flatten(2).transpose(1, 2) # (B, 1, embed_dim)
# Add Positional Encoding
x = x + self.pos_embed
# Transformer blocks
for blk in self.blocks:
x = blk(x)
# Classification
x = self.norm(x) # (B, 1, embed_dim)
x = x.mean(dim=1) # (B, embed_dim) - global average pooling
x = self.head(x) # (B, num_classes)
return x

# ==========================================
# Training & Evaluation Functions
# ==========================================
def train_model(model, train_loader, val_loader, cfg):
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=cfg.learning_rate, weight_decay=cfg.weight_decay)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)
best_acc = 0.0
train_losses = []
val_accs = []
print(f"Starting Training on {cfg.device}...")
for epoch in range(cfg.epochs):
model.train()
total_loss = 0
for patches, labels in train_loader:
patches, labels = patches.to(cfg.device), labels.to(cfg.device)
optimizer.zero_grad()
outputs = model(patches)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
total_loss += loss.item()
scheduler.step()
avg_loss = total_loss / len(train_loader)
train_losses.append(avg_loss)
# Validation
model.eval()
correct = 0
total = 0
with torch.no_grad():
for patches, labels in val_loader:
patches, labels = patches.to(cfg.device), labels.to(cfg.device)
outputs = model(patches)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
val_acc = 100 * correct / total
val_accs.append(val_acc)
if val_acc > best_acc:
best_acc = val_acc
torch.save(model.state_dict(), 'best_spectralformer.pth')
if (epoch + 1) % 5 == 0:
print(f"Epoch [{epoch+1}/{cfg.epochs}], Loss: {avg_loss:.4f}, Val Acc: {val_acc:.2f}%")
return train_losses, val_accs


def evaluate_model(model, test_loader, cfg):
model.load_state_dict(torch.load('best_spectralformer.pth', map_location=cfg.device))
model.eval()
all_preds = []
all_labels = []
with torch.no_grad():
for patches, labels in test_loader:
patches = patches.to(cfg.device)
outputs = model(patches)
_, predicted = torch.max(outputs.data, 1)
all_preds.extend(predicted.cpu().numpy())
all_labels.extend(labels.numpy())
all_preds = np.array(all_preds)
all_labels = np.array(all_labels)
# Metrics
oa = accuracy_score(all_labels, all_preds)
kappa = cohen_kappa_score(all_labels, all_preds)
# Average Accuracy (per class)
cm = confusion_matrix(all_labels, all_preds)
# Handle division by zero for classes with no samples
class_acc = np.diag(cm) / np.maximum(np.sum(cm, axis=1), 1)
aa = np.mean(class_acc)
print("\n--- Evaluation Results ---")
print(f"Overall Accuracy (OA): {oa * 100:.2f}%")
print(f"Average Accuracy (AA): {aa * 100:.2f}%")
print(f"Kappa Coefficient: {kappa:.4f}")
print("Confusion Matrix:\n", cm)
# Per-class accuracy
print("\nPer-class Accuracy:")
for i, acc in enumerate(class_acc):
print(f" Class {i+1}: {acc * 100:.2f}%")
return oa, aa, kappa


# ==========================================
# Main Execution
# ==========================================
def main():
# Set random seeds for reproducibility
torch.manual_seed(42)
np.random.seed(42)
# 1. Load Data
print("Loading Data...")
data, gt = load_pavia_data(cfg)
# 2. Create Patches
print("Creating Patches...")
patches, labels = create_patches(data, gt, cfg.patch_size)
# 3. Split Data (Train/Val/Test)
print("Splitting Data...")
X_train, X_temp, y_train, y_temp = train_test_split(
patches, labels, train_size=cfg.train_ratio, stratify=labels, random_state=42
)
X_val, X_test, y_val, y_test = train_test_split(
X_temp, y_temp, test_size=0.5, stratify=y_temp, random_state=42
)
print(f"Train: {len(y_train)}, Val: {len(y_val)}, Test: {len(y_test)}")
# 4. Dataloaders
train_dataset = HSIDataset(X_train, y_train)
val_dataset = HSIDataset(X_val, y_val)
test_dataset = HSIDataset(X_test, y_test)
train_loader = DataLoader(train_dataset, batch_size=cfg.batch_size, shuffle=True, num_workers=0)
val_loader = DataLoader(val_dataset, batch_size=cfg.batch_size, shuffle=False, num_workers=0)
test_loader = DataLoader(test_dataset, batch_size=cfg.batch_size, shuffle=False, num_workers=0)
# 5. Initialize Model
print("Initializing Model...")
model = SpectralFormer(cfg).to(cfg.device)
print(f"Model Parameters: {sum(p.numel() for p in model.parameters()):,}")
# 6. Train
print("\n" + "="*50)
train_losses, val_accs = train_model(model, train_loader, val_loader, cfg)
# 7. Evaluate
print("\n" + "="*50)
evaluate_model(model, test_loader, cfg)
# 8. Plot Loss/Acc
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.plot(train_losses, label='Train Loss', color='blue')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss')
plt.grid(True, alpha=0.3)
plt.subplot(1, 2, 2)
plt.plot(val_accs, label='Val Accuracy', color='green')
plt.xlabel('Epoch')
plt.ylabel('Accuracy (%)')
plt.title('Validation Accuracy')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('training_curve.png', dpi=300, bbox_inches='tight')
print("\nTraining curve saved to training_curve.png")
plt.close()

if __name__ == '__main__':
# Create data directory if not exists
os.makedirs(cfg.data_path, exist_ok=True)
main()


lunedì 23 febbraio 2026

Segformer (Indian Pines, Pavia)

 Sulla base di quanto letto sul dataset SpectralWaste e codice allegato ho voluto provare SegFormer su due dataset pubblici e classici per testare algoritmi come Indian Pines e Pavia

Indian Pines ha un risoluzione di 220 bande con una risoluzione a terra di 30 m e 145x145 pixel. E' quindi un dataset molto piccolo che necessita ed data augmentation e inoltre e' anche sbilanciato in quanto la classe Background e' numericamente molto piu' numerosa delle altre classi

Pavia ha una risoluzione spettrale di 103 bande con una risoluzione a terra di 1.3 m pixel e 610x340 pixel 

I dataset possono essere scaricati da qui 

 

 Pavia

 


 


 

 

 

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
import random
from scipy.io import loadmat
from torch.utils.data import Dataset, DataLoader
from transformers import SegformerForSemanticSegmentation
from sklearn.metrics import cohen_kappa_score, accuracy_score, classification_report, confusion_matrix
import seaborn as sns

# --- CONFIGURATION FOR INDIAN PINES ---
CONFIG = {
"dataset": "IndianPines",
"data_path": "Indian_pines_corrected.mat",
"gt_path": "Indian_pines_gt.mat",
"in_channels": 200, # Standard for corrected Indian Pines
"num_classes": 17, # 16 classes + 1 background (0)
"window_size": 32, # Smaller window for 145x145 image
"stride": 4, # Smaller stride to increase patch count
"train_ratio": 0.2, # 20% training, 80% testing
"batch_size": 16,
"epochs": 80, # Increased epochs for convergence
"lr": 1e-4
}

# 1. DATASET WITH AUTOMATIC KEY DETECTION
class HSIDataset(Dataset):
def __init__(self, cfg, is_train=True, augment=True):
raw_data = loadmat(cfg["data_path"])
raw_gt = loadmat(cfg["gt_path"])
# Auto-detect keys (ignores metadata keys starting with __)
data_key = [k for k in raw_data.keys() if not k.startswith('__')][0]
gt_key = [k for k in raw_gt.keys() if not k.startswith('__')][0]
data = raw_data[data_key].astype(np.float32)
gt = raw_gt[gt_key].astype(np.int64)
# Normalization (Min-Max)
data = (data - np.min(data)) / (np.max(data) - np.min(data))
self.data = np.transpose(data, (2, 0, 1)) # [C, H, W]
# Create Train/Test Split logic
labeled_indices = np.where(gt > 0)
num_labeled = len(labeled_indices[0])
indices = np.arange(num_labeled)
np.random.seed(42)
np.random.shuffle(indices)
train_count = int(num_labeled * cfg["train_ratio"])
train_idx = indices[:train_count]
test_idx = indices[train_count:]
split_gt = np.zeros_like(gt)
if is_train:
split_gt[labeled_indices[0][train_idx], labeled_indices[1][train_idx]] = gt[labeled_indices[0][train_idx], labeled_indices[1][train_idx]]
else:
split_gt[labeled_indices[0][test_idx], labeled_indices[1][test_idx]] = gt[labeled_indices[0][test_idx], labeled_indices[1][test_idx]]
self.gt = split_gt
self.augment = augment and is_train
# Patch Generation
self.patches, self.labels = [], []
c, h, w = self.data.shape
for i in range(0, h - cfg["window_size"] + 1, cfg["stride"]):
for j in range(0, w - cfg["window_size"] + 1, cfg["stride"]):
patch_gt = self.gt[i:i+cfg["window_size"], j:j+cfg["window_size"]]
if np.sum(patch_gt > 0) > 0: # Only keep if patch has labels for this split
self.patches.append(self.data[:, i:i+cfg["window_size"], j:j+cfg["window_size"]])
self.labels.append(patch_gt)
self.patches = np.array(self.patches)
self.labels = np.array(self.labels)

def __len__(self): return len(self.patches)

def __getitem__(self, idx):
patch, label = self.patches[idx], self.labels[idx]
if self.augment:
if random.random() > 0.5:
patch = np.flip(patch, axis=2).copy()
label = np.flip(label, axis=1).copy()
if random.random() > 0.5:
patch = np.flip(patch, axis=1).copy()
label = np.flip(label, axis=0).copy()
return torch.from_numpy(patch), torch.from_numpy(label)

# 2. MODEL DEFINITION
class SegFormerHSI(nn.Module):
def __init__(self, in_ch, num_cl):
super().__init__()
# Reducer compresses spectral dimension to 3 channels for SegFormer input
self.reducer = nn.Conv2d(in_ch, 3, kernel_size=1)
self.model = SegformerForSemanticSegmentation.from_pretrained(
"nvidia/mit-b0",
num_labels=num_cl,
ignore_mismatched_sizes=True
)

def forward(self, x):
x = self.reducer(x)
out = self.model(x)
# Upsample logits to original patch size
return nn.functional.interpolate(out.logits, size=x.shape[-2:], mode="bilinear", align_corners=False)

# 3. VISUALIZATION FUNCTIONS
def plot_results(train_gt, full_gt, pred_map):
error_display = np.full(full_gt.shape + (3,), 1.0)
mask = full_gt > 0
error_display[mask] = [1, 0, 0] # Red for errors
error_display[mask & (full_gt == pred_map)] = [0, 1, 0] # Green for correct

fig, ax = plt.subplots(1, 3, figsize=(18, 6))
# Train Mask
train_view = train_gt.astype(float)
train_view[train_view == 0] = np.nan
ax[0].imshow(train_view, cmap='nipy_spectral')
ax[0].set_title("Training Pixels (20%)")
# Prediction
ax[1].imshow(pred_map, cmap='nipy_spectral')
ax[1].set_title("Full Prediction Map")
# Error Map
ax[2].imshow(error_display)
ax[2].set_title("Error Map (Green=Correct)")
for a in ax: a.axis('off')
plt.show()

# 4. MAIN EXECUTION
def main():
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
train_ds = HSIDataset(CONFIG, is_train=True)
test_ds = HSIDataset(CONFIG, is_train=False, augment=False)
train_loader = DataLoader(train_ds, batch_size=CONFIG["batch_size"], shuffle=True)
model = SegFormerHSI(CONFIG["in_channels"], CONFIG["num_classes"]).to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr=CONFIG["lr"])
criterion = nn.CrossEntropyLoss(ignore_index=0) # Ignore background class

print(f"Dataset: Indian Pines | Training Patches: {len(train_ds)}")

# Training
model.train()
for epoch in range(CONFIG["epochs"]):
total_loss = 0
for imgs, masks in train_loader:
imgs, masks = imgs.to(device), masks.to(device)
optimizer.zero_grad()
outputs = model(imgs)
loss = criterion(outputs, masks)
loss.backward()
optimizer.step()
total_loss += loss.item()
if epoch % 10 == 0:
print(f"Epoch {epoch} | Loss: {total_loss/len(train_loader):.4f}")

# Evaluation
model.eval()
with torch.no_grad():
full_img = torch.from_numpy(test_ds.data).unsqueeze(0).to(device)
pred_map = torch.argmax(model(full_img), dim=1).squeeze(0).cpu().numpy()
# Stats on Test Set
mask = test_ds.gt > 0
y_true = test_ds.gt[mask]
y_pred = pred_map[mask]
print(f"\n--- INDIAN PINES RESULTS ---")
print(f"Overall Accuracy: {accuracy_score(y_true, y_pred):.4f}")
print(f"Kappa: {cohen_kappa_score(y_true, y_pred):.4f}")
# Final Visualizations
raw_gt = loadmat(CONFIG["gt_path"])
gt_key = [k for k in raw_gt.keys() if not k.startswith('__')][0]
full_gt = raw_gt[gt_key].astype(np.int64)
plot_results(train_ds.gt, full_gt, pred_map)

if __name__ == "__main__":
main()


 

Indian Pines


 

 


 

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
import random
from scipy.io import loadmat
from torch.utils.data import Dataset, DataLoader
from transformers import SegformerForSemanticSegmentation
from sklearn.metrics import cohen_kappa_score, accuracy_score, classification_report

from sklearn.metrics import confusion_matrix
import seaborn as sns

def plot_results_summary(train_gt, full_gt, pred_map, dataset_name):
# Prepare Error Map: 1 for correct, 0 for error
# Only evaluate where ground truth exists (full_gt > 0)
error_map = np.zeros_like(full_gt, dtype=float)
mask = full_gt > 0
error_map[mask] = (full_gt[mask] == pred_map[mask]).astype(float)
# For visualization, we make mistakes Red (0) and correct pixels Green (1)
# Background stays White/Transparent
error_display = np.full(full_gt.shape + (3,), 1.0) # White background
error_display[full_gt > 0] = [1, 0, 0] # Default Red (Error)
error_display[(full_gt > 0) & (full_gt == pred_map)] = [0, 1, 0] # Green (Correct)

plt.figure(figsize=(18, 6))
# 1. Training Mask
plt.subplot(1, 3, 1)
train_view = train_gt.astype(float)
train_view[train_view == 0] = np.nan
plt.imshow(train_view, cmap='nipy_spectral')
plt.title(f"Training Mask (Used pixels)")
plt.axis('off')
# 2. Prediction
plt.subplot(1, 3, 2)
plt.imshow(pred_map, cmap='nipy_spectral')
plt.title(f"Model Prediction (Full Map)")
plt.axis('off')
# 3. Error Map (Green = Correct, Red = Wrong)
plt.subplot(1, 3, 3)
plt.imshow(error_display)
plt.title(f"Error Map (Green=Correct, Red=Error)")
plt.axis('off')
plt.tight_layout()
plt.show()

def plot_train_vs_prediction(train_gt, pred_map, dataset_name):
# Set background (0) to NaN for better visualization (appears white/empty)
train_display = train_gt.astype(float)
train_display[train_display == 0] = np.nan
pred_display = pred_map.astype(float)
# Optional: mask prediction with where labels actually exist in reality
# pred_display[dataset.gt == 0] = np.nan

plt.figure(figsize=(14, 7))
# Left: Training Mask
plt.subplot(1, 2, 1)
plt.imshow(train_display, cmap='nipy_spectral')
plt.title(f"{dataset_name}: Training Pixels (20%)")
plt.axis('off')
# Right: Model Prediction
plt.subplot(1, 2, 2)
plt.imshow(pred_display, cmap='nipy_spectral')
plt.title(f"{dataset_name}: SegFormer Full Prediction")
plt.axis('off')
plt.tight_layout()
plt.show()

def plot_confusion_matrix(y_true, y_pred, dataset_name):
cm = confusion_matrix(y_true, y_pred)
# Normalize by row (true labels)
cm_norm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
plt.figure(figsize=(10, 8))
sns.heatmap(cm_norm, annot=True, fmt=".2f", cmap="Blues")
plt.title(f"Normalized Confusion Matrix: {dataset_name}")
plt.ylabel("True Class")
plt.xlabel("Predicted Class")
plt.show()

# --- CONFIGURATION ---
CONFIG = {
"dataset": "PaviaU",
"data_path": "PaviaU.mat",
"gt_path": "PaviaU_gt.mat",
"in_channels": 103,
"num_classes": 10,
"window_size": 64,
"stride": 8,
"train_ratio": 0.2, # Use 20% of pixels for training
"batch_size": 16,
"epochs": 60,
"lr": 1e-4
}

# 1. DATASET WITH SPATIAL SPLIT
class HSIDataset(Dataset):
def __init__(self, cfg, is_train=True, augment=True):
raw_data = loadmat(cfg["data_path"])
raw_gt = loadmat(cfg["gt_path"])
data_key = "paviaU" if cfg["dataset"] == "PaviaU" else "indian_pines_corrected"
gt_key = "paviaU_gt" if cfg["dataset"] == "PaviaU" else "indian_pines_gt"
data = raw_data[data_key].astype(np.float32)
gt = raw_gt[gt_key].astype(np.int64)
# Normalize
data = (data - np.min(data)) / (np.max(data) - np.min(data))
self.data = np.transpose(data, (2, 0, 1))
# Create Train/Test Mask
# Only split labeled pixels (gt > 0)
labeled_indices = np.where(gt > 0)
num_labeled = len(labeled_indices[0])
indices = np.arange(num_labeled)
np.random.seed(42)
np.random.shuffle(indices)
train_count = int(num_labeled * cfg["train_ratio"])
train_idx = indices[:train_count]
test_idx = indices[train_count:]
split_gt = np.zeros_like(gt)
if is_train:
split_gt[labeled_indices[0][train_idx], labeled_indices[1][train_idx]] = gt[labeled_indices[0][train_idx], labeled_indices[1][train_idx]]
else:
split_gt[labeled_indices[0][test_idx], labeled_indices[1][test_idx]] = gt[labeled_indices[0][test_idx], labeled_indices[1][test_idx]]
self.gt = split_gt
self.augment = augment and is_train
# Generate Patches
self.patches, self.labels = [], []
c, h, w = self.data.shape
for i in range(0, h - cfg["window_size"] + 1, cfg["stride"]):
for j in range(0, w - cfg["window_size"] + 1, cfg["stride"]):
patch_gt = self.gt[i:i+cfg["window_size"], j:j+cfg["window_size"]]
# Only keep patch if it contains labeled pixels for this split
if np.sum(patch_gt > 0) > 0:
self.patches.append(self.data[:, i:i+cfg["window_size"], j:j+cfg["window_size"]])
self.labels.append(patch_gt)
self.patches = np.array(self.patches)
self.labels = np.array(self.labels)

def __len__(self): return len(self.patches)

def __getitem__(self, idx):
patch, label = self.patches[idx], self.labels[idx]
if self.augment:
if random.random() > 0.5: patch = np.flip(patch, axis=2).copy(); label = np.flip(label, axis=1).copy()
if random.random() > 0.5: patch = np.flip(patch, axis=1).copy(); label = np.flip(label, axis=0).copy()
return torch.from_numpy(patch), torch.from_numpy(label)

# 2. MODEL & METRICS (Same as before)
class SegFormerHSI(nn.Module):
def __init__(self, in_ch, num_cl):
super().__init__()
self.reducer = nn.Conv2d(in_ch, 3, kernel_size=1)
self.model = SegformerForSemanticSegmentation.from_pretrained(
"nvidia/mit-b0", num_labels=num_cl, ignore_mismatched_sizes=True
)

def forward(self, x):
x = self.reducer(x)
out = self.model(x)
return nn.functional.interpolate(out.logits, size=x.shape[-2:], mode="bilinear", align_corners=False)

# 3. MAIN TRAINING & VALIDATION
def main():
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Datasets
train_ds = HSIDataset(CONFIG, is_train=True)
test_ds = HSIDataset(CONFIG, is_train=False, augment=False)
train_loader = DataLoader(train_ds, batch_size=CONFIG["batch_size"], shuffle=True)
model = SegFormerHSI(CONFIG["in_channels"], CONFIG["num_classes"]).to(device)
optimizer = torch.optim.AdamW(model.parameters(), lr=CONFIG["lr"])
criterion = nn.CrossEntropyLoss(ignore_index=0)

print(f"Train Patches: {len(train_ds)} | Test Patches: {len(test_ds)}")

# Train Loop
for epoch in range(CONFIG["epochs"]):
model.train()
for imgs, masks in train_loader:
imgs, masks = imgs.to(device), masks.to(device)
optimizer.zero_grad(); loss = criterion(model(imgs), masks); loss.backward(); optimizer.step()
if epoch % 10 == 0: print(f"Epoch {epoch} complete.")

# Evaluation on the TEST split only
model.eval()
with torch.no_grad():
full_img = torch.from_numpy(test_ds.data).unsqueeze(0).to(device)
pred_map = torch.argmax(model(full_img), dim=1).squeeze(0).cpu().numpy()
# Mask to only evaluate pixels assigned to TEST split
mask = test_ds.gt > 0
oa = accuracy_score(test_ds.gt[mask], pred_map[mask])
kappa = cohen_kappa_score(test_ds.gt[mask], pred_map[mask])
print(f"\n--- TEST SET RESULTS ---")
print(f"Overall Accuracy: {oa:.4f}")
print(f"Kappa: {kappa:.4f}")
print("\nClass-wise Report:")
print(classification_report(test_ds.gt[mask], pred_map[mask]))
plot_confusion_matrix(test_ds.gt[mask], pred_map[mask], CONFIG["dataset"])
plot_train_vs_prediction(train_ds.gt, pred_map, CONFIG["dataset"])

mat_gt = loadmat(CONFIG["gt_path"])
gt_key = "paviaU_gt" if CONFIG["dataset"] == "PaviaU" else "indian_pines_gt"
full_gt = mat_gt[gt_key].astype(np.int64)

# 2. Now call the plot function with the newly defined full_gt
print("Generating Results Plots...")
plot_results_summary(
train_gt=train_ds.gt,
full_gt=full_gt,
pred_map=pred_map,
dataset_name=CONFIG["dataset"]
)

if __name__ == "__main__":
main()


AsteroidOS TicWatch Pro 2018 wf12096

Linux al polso.. dopo quasi 8 anni riprovo AsteroidOs e devo dire che non e' niente male Per sincronizzare con il telefono si usa la ap...