Dinusharg's picture
Update app.py
6517599 verified
Raw History Blame Contribute Delete
4.26 kB
import os
import numpy as np
import torch
import torch.nn.functional as F
import gradio as gr
from PIL import Image
from transformers import SegformerForSemanticSegmentation, SegformerImageProcessor
# ============================================================
# Config
# ============================================================
MODEL_ID = "Dinusharg/segformer_environment_1"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
# Class colors for your labels
PALETTE = {
0: (0, 0, 0), # background
1: (255, 0, 0), # footpath
2: (0, 255, 0), # grass
3: (128, 128, 128), # road
4: (0, 0, 255), # water
}
# Optional nicer class names for display
CLASS_NAMES = {
0: "background",
1: "footpath",
2: "grass",
3: "road",
4: "water",
}
# ============================================================
# Load model once at startup
# ============================================================
processor = SegformerImageProcessor.from_pretrained(MODEL_ID)
model = SegformerForSemanticSegmentation.from_pretrained(MODEL_ID).to(DEVICE)
model.eval()
def make_color_mask(pred: np.ndarray) -> np.ndarray:
"""
Convert class-id mask [H, W] into RGB color mask [H, W, 3].
"""
color_mask = np.zeros((pred.shape[0], pred.shape[1], 3), dtype=np.uint8)
for class_id, color in PALETTE.items():
color_mask[pred == class_id] = color
return color_mask
def make_overlay(image_np: np.ndarray, color_mask: np.ndarray, alpha: float = 0.5) -> np.ndarray:
"""
Blend original image with color mask.
"""
image_f = image_np.astype(np.float32)
mask_f = color_mask.astype(np.float32)
overlay = (image_f * (1.0 - alpha) + mask_f * alpha).clip(0, 255).astype(np.uint8)
return overlay
def make_class_summary(pred: np.ndarray) -> str:
"""
Summarize predicted pixel counts and percentages.
"""
total_pixels = pred.size
unique, counts = np.unique(pred, return_counts=True)
lines = []
for class_id, count in zip(unique.tolist(), counts.tolist()):
pct = 100.0 * count / total_pixels
class_name = CLASS_NAMES.get(class_id, f"class_{class_id}")
lines.append(f"- {class_id}: {class_name} — {count} pixels ({pct:.2f}%)")
return "\n".join(lines)
def predict(image: Image.Image):
"""
Gradio callback.
Input: PIL image
Outputs:
- segmentation mask
- overlay
- class summary
"""
if image is None:
return None, None, "No image provided."
image = image.convert("RGB")
original_np = np.array(image)
inputs = processor(images=image, return_tensors="pt")
inputs = {k: v.to(DEVICE) for k, v in inputs.items()}
with torch.no_grad():
outputs = model(**inputs)
upsampled_logits = F.interpolate(
outputs.logits,
size=image.size[::-1], # PIL gives (W, H), torch expects (H, W)
mode="bilinear",
align_corners=False
)
pred = upsampled_logits.argmax(dim=1)[0].detach().cpu().numpy()
color_mask = make_color_mask(pred)
overlay = make_overlay(original_np, color_mask, alpha=0.5)
summary = make_class_summary(pred)
return Image.fromarray(color_mask), Image.fromarray(overlay), summary
title = "SegFormer Semantic Segmentation Demo"
description = (
"Upload an outdoor scene image to generate a semantic segmentation mask and overlay. "
"Classes: background, footpath, grass, road, water."
)
article = """
### Notes
- The model is loaded from the Hugging Face Hub using `MODEL_ID`.
- To use your own model in this Space, set `MODEL_ID` in the Space settings or replace the default value in `app.py`.
- Later you can add your public Space link to the model README.
"""
demo = gr.Interface(
fn=predict,
inputs=gr.Image(type="pil", label="Upload Image"),
outputs=[
gr.Image(type="pil", label="Segmentation Mask"),
gr.Image(type="pil", label="Overlay"),
gr.Markdown(label="Class Summary"),
],
title=title,
description=description,
examples=[
# Add your sample images later, for example:
# ["assets/sample1.jpg"],
# ["assets/sample2.jpg"],
],
)
if __name__ == "__main__":
demo.launch()