import gradio as gr import torch from torchvision import transforms from model import UNet from PIL import Image import numpy as np # --- Configuration --- MODES = [ "MP+Tr+BCE", "MP+Tr+Dice", "StrConv+Tr+BCE", "StrConv+Ups+Dice" ] def get_config(mode_name): if mode_name == "MP+Tr+BCE": return {"downsample": "maxpool", "upsample": "transpose", "filename": "unet_MP_Tr_BCE.pth"} elif mode_name == "MP+Tr+Dice": return {"downsample": "maxpool", "upsample": "transpose", "filename": "unet_MP_Tr_Dice.pth"} elif mode_name == "StrConv+Tr+BCE": return {"downsample": "strided", "upsample": "transpose", "filename": "unet_StrConv_Tr_BCE.pth"} elif mode_name == "StrConv+Ups+Dice": return {"downsample": "strided", "upsample": "upsample", "filename": "unet_StrConv_Ups_Dice.pth"} else: raise ValueError(f"Unknown mode: {mode_name}") # --- Load Models Once (Optional optimization, but safer to load on demand if memory is tight) --- # For immediate responsiveness, let's load them on demand inside the functions or keep them cached. # Given it's a demo, loading 4 models might be heavy on CPU/RAM if hosted on free tier. # But let's try to run them sequentially. device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') def run_inference_single_model(model, image_tensor): with torch.no_grad(): output = model(image_tensor) output = torch.sigmoid(output) output = output.squeeze().cpu().numpy() mask = (output > 0.5).astype(np.float32) return mask def predict_all(image): if image is None: return [None] * 4 # Preprocess transform = transforms.Compose([ transforms.Resize((128, 128)), transforms.ToTensor() ]) input_tensor = transform(image).unsqueeze(0).to(device) # Create overlay # Transform image to numpy for blending. We need it in [0,1] or [0,255] img_resized = image.resize((128, 128)) img_np = np.array(img_resized) results = [] for mode in MODES: config = get_config(mode) # Initialize model structure model = UNet(n_channels=3, n_classes=1, downsample_mode=config['downsample'], upsample_mode=config['upsample']).to(device) # Load weights try: model.load_state_dict(torch.load(config['filename'], map_location=device)) model.eval() mask = run_inference_single_model(model, input_tensor) # Create Overlay # Mask is (128, 128). We want to make it Red where mask is 1. # Convert mask to RGBA overlay = np.zeros_like(img_np) # Set Red channel to 255 where mask is 1 overlay[:,:,0] = mask * 255 # overlay[:,:,1] = 0 # overlay[:,:,2] = 0 # Blend: 0.7 * Original + 0.3 * Red Overlay (where mask is present) # Actually simplest is just simpler blending # Where mask == 1, add red tint # Let's perform a weighted add using OpenCV logic manually blended = img_np.copy() # Indices where mask is active idx = (mask > 0) # Increase Red component, decrease Green/Blue to make it pop blended[idx, 0] = np.clip(blended[idx, 0] * 0.5 + 255 * 0.5, 0, 255) blended[idx, 1] = blended[idx, 1] * 0.5 blended[idx, 2] = blended[idx, 2] * 0.5 results.append(blended) except Exception as e: print(f"Error for {mode}: {e}") results.append(img_np) # Return original image on error return results # --- Gradio App --- title = "UNet Oxford-IIIT Pet Separation - Model Comparison" description = "Compare the segmentation results of 4 different UNet configurations on the same input image." iface = gr.Interface( fn=predict_all, inputs=gr.Image(type="pil", label="Input Image"), outputs=[ gr.Image(label="MP + Tr + BCE"), gr.Image(label="MP + Tr + Dice"), gr.Image(label="StrConv + Tr + BCE"), gr.Image(label="StrConv + Ups + Dice") ], title=title, description=description ) if __name__ == "__main__": iface.launch()