lsnn-app / main.py
TejasChauhan's picture
Update main.py
0e7f1b3 verified
Raw
History Blame Contribute Delete
3.85 kB
import io
import torch
import numpy as np
from PIL import Image
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import Response
from torchvision import transforms
import matplotlib
matplotlib.use('Agg') # Use non-interactive backend for server
import matplotlib.pyplot as plt
from matplotlib.patches import Patch
# Import architecture, colors, and utils from your model.py
from model import (
LSNN,
IMAGENET_MEAN,
IMAGENET_STD,
DISPLAY_COLORS,
LEGEND_ENTRIES,
predict_tta
)
app = FastAPI(title="Semantic Change Detection API")
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = None
preprocess = transforms.Compose([
transforms.Resize((256, 256)),
transforms.ToTensor(),
transforms.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),
])
def class_map_to_rgb(cls_map):
rgb = np.zeros((*cls_map.shape, 3), dtype=np.uint8)
for cls, col in DISPLAY_COLORS.items():
rgb[cls_map == cls] = col
return rgb
def make_legend_patches():
return [Patch(facecolor=np.array(c) / 255, label=l) for c, l in LEGEND_ENTRIES]
@app.get("/health")
async def health_check():
return {
"status": "ok",
"model_loaded": model is not None,
"device": str(device),
}
@app.on_event("startup")
async def load_model():
global model
print("Loading LSNN model...")
model = LSNN(hidden=96).to(device)
ckpt = torch.load("best_lsnn.pth", map_location=device)
if "model" in ckpt:
model.load_state_dict(ckpt["model"])
else:
model.load_state_dict(ckpt)
model.eval()
print("Model loaded!")
@app.post("/predict")
async def predict_change(img1: UploadFile = File(...), img2: UploadFile = File(...)):
# 1. Read and prep images
raw_img1 = Image.open(io.BytesIO(await img1.read())).convert("RGB")
raw_img2 = Image.open(io.BytesIO(await img2.read())).convert("RGB")
# Resize raw images for plotting so they match the 256x256 prediction map
disp_img1 = raw_img1.resize((256, 256))
disp_img2 = raw_img2.resize((256, 256))
# 2. Preprocess for the model
t1 = preprocess(raw_img1).unsqueeze(0).to(device)
t2 = preprocess(raw_img2).unsqueeze(0).to(device)
# 3. Inference
with torch.no_grad():
sem_logits = predict_tta(model, t1, t2)
pred_mask = torch.argmax(sem_logits, dim=1).squeeze(0).cpu().numpy()
# 4. Process predictions
change_pct = 100.0 * (pred_mask != 0).sum() / pred_mask.size
pred_rgb = class_map_to_rgb(pred_mask)
# 5. Generate Matplotlib Figure
fig = plt.figure(figsize=(15, 6))
gs = fig.add_gridspec(2, 3, height_ratios=[5, 1], hspace=0.1, wspace=0.1)
# Old Image
ax1 = fig.add_subplot(gs[0, 0])
ax1.imshow(disp_img1)
ax1.set_title("Old Image", fontweight='bold')
ax1.axis('off')
# New Image
ax2 = fig.add_subplot(gs[0, 1])
ax2.imshow(disp_img2)
ax2.set_title("New Image", fontweight='bold')
ax2.axis('off')
# Prediction
ax3 = fig.add_subplot(gs[0, 2])
ax3.imshow(pred_rgb)
ax3.set_title("Prediction", fontweight='bold')
ax3.axis('off')
# Legend
ax_lg = fig.add_subplot(gs[1, :])
ax_lg.axis('off')
ax_lg.legend(handles=make_legend_patches(), loc='center', ncol=7, fontsize=10, frameon=False)
# Percentage Text
fig.text(0.5, 0.05, f"Change Detected: {change_pct:.2f}%",
ha='center', fontsize=14, fontweight='bold', color='red')
# 6. Save plot to buffer and return
buf = io.BytesIO()
plt.savefig(buf, format="png", bbox_inches='tight', dpi=150)
plt.close(fig)
buf.seek(0)
return Response(content=buf.getvalue(), media_type="image/png")