import streamlit as st import torch import torch.nn as nn from torchvision import models, transforms from PIL import Image import numpy as np # ── Page configuration ────────────────────────────────── st.set_page_config( page_title="DermaScan AI", page_icon="🔬", layout="wide", initial_sidebar_state="expanded" ) # ── Custom CSS styling ────────────────────────────────── st.markdown(""" """, unsafe_allow_html=True) # ── Load model ────────────────────────────────────────── @st.cache_resource def load_model(): classes = np.load('classes.npy', allow_pickle=True) num_classes = len(classes) model = models.resnet50(weights=None) num_features = model.fc.in_features model.fc = nn.Sequential( nn.Linear(num_features, 1024), nn.ReLU(), nn.Dropout(0.2), nn.Linear(1024, 512), nn.ReLU(), nn.Dropout(0.1), nn.Linear(512, 128), nn.ReLU(), nn.Linear(128, num_classes) ) model.load_state_dict(torch.load('skin_lesion_resnet50_best.pth', map_location='cpu')) model.eval() return model, classes def preprocess(image): transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) return transform(image).unsqueeze(0) def confidence_pill(conf): if conf >= 0.80: return f'{conf*100:.1f}% High Confidence' elif conf >= 0.60: return f'{conf*100:.1f}% Moderate' else: return f'{conf*100:.1f}% Low Confidence' # ── Hero ───────────────────────────────────────────────── st.markdown("""
🔬

DermaScan AI

Deep learning–powered dermoscopy analysis  ·  ResNet50 classification engine

⚡ Live Inference
""", unsafe_allow_html=True) # ── Sidebar ────────────────────────────────────────────── with st.sidebar: st.markdown('', unsafe_allow_html=True) st.markdown(""" """, unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.markdown('', unsafe_allow_html=True) st.markdown("""
ArchitectureResNet50
FrameworkPyTorch
Input Size224 × 224 px
TaskMulti-class
NormalizationImageNet
""", unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) st.markdown('', unsafe_allow_html=True) st.markdown("""
1. Upload a dermoscopy image (JPG / PNG)
2. Wait for model inference to complete
3. Review ranked predictions and confidence scores
4. Consult a dermatologist for clinical decisions
""", unsafe_allow_html=True) # ── Upload ──────────────────────────────────────────────── st.markdown("""
📂 Image Upload
""", unsafe_allow_html=True) uploaded_file = st.file_uploader( "Drop a dermoscopy image here, or click to browse", type=["jpg", "jpeg", "png"], help="Supported: JPG, JPEG, PNG" ) # ── Results ──────────────────────────────────────────────── if uploaded_file: image = Image.open(uploaded_file).convert("RGB") w, h = image.size col1, col2 = st.columns([1, 1], gap="large") with col1: st.markdown("""
🖼 Input Image
""", unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) st.image(image, use_column_width=True) st.markdown(f"""
{w} × {h}
Resolution
{uploaded_file.name.split('.')[-1].upper()}
Format
{uploaded_file.size // 1024} KB
File Size
RGB
Color Mode
""", unsafe_allow_html=True) with col2: st.markdown("""
🎯 Analysis Results
""", unsafe_allow_html=True) with st.spinner("Running model inference…"): model, classes = load_model() tensor = preprocess(image) with torch.no_grad(): outputs = model(tensor) probs = torch.softmax(outputs, dim=1)[0] top_prob, top_idx = torch.topk(probs, 3) top_prediction = classes[top_idx[0]] top_confidence = float(top_prob[0]) # Primary diagnosis st.markdown(f"""
Primary Diagnosis
{top_prediction}
{confidence_pill(top_confidence)}
""", unsafe_allow_html=True) # Top-3 predictions st.markdown("""
📊 Ranked Predictions
""", unsafe_allow_html=True) rank_labels = ["1st", "2nd", "3rd"] for i, (prob, idx) in enumerate(zip(top_prob, top_idx)): conf = float(prob) bar_width = int(conf * 100) st.markdown(f"""
{rank_labels[i]}   {classes[idx]}
{conf*100:.1f}%
""", unsafe_allow_html=True) # ── Full results table ── st.markdown("
", unsafe_allow_html=True) st.markdown("""
📋 Classification Summary
""", unsafe_allow_html=True) results_data = { "Rank": [f"#{i+1}" for i in range(len(top_prob))], "Diagnosis": [classes[idx] for idx in top_idx], "Confidence": [f"{prob*100:.2f}%" for prob in top_prob], "Status": [ "✅ Primary" if i == 0 else ("⚠️ Alternate" if i == 1 else "ℹ️ Low prob") for i in range(len(top_prob)) ] } st.dataframe(results_data, use_container_width=True, hide_index=True) # ── Clinical note ── st.markdown("""
⚕️ Clinical Disclaimer

This AI classification is intended for informational and research purposes only. It does not constitute a medical diagnosis. Always consult a board-certified dermatologist for clinical evaluation, diagnosis, and treatment recommendations.

""", unsafe_allow_html=True) else: st.markdown("""
🔬

No Image Uploaded

Upload a dermoscopy image above to begin skin lesion classification

""", unsafe_allow_html=True)