import json from pathlib import Path import streamlit as st import streamlit.components.v1 as components import numpy as np from PIL import Image from src.utils import load_metrics_json MODEL_TYPES = ['cnn', 'transfer', 'vit'] # Search order for weights / metrics (best-trained snapshot first). ARTIFACT_DIR_NAMES = ['real_eval_fixed', 'real_eval_current', 'artifacts'] def page_style(): return """ """ def html_header(): return """
Tri-Netra
A modern MRI brain tumor dashboard with compact model comparison, upload-driven inference, and performance metrics for CNN, Transfer, and Vision Transformer models.
Oncology AI Model comparison
""" def _resolve_artifact_dirs(primary): """Return the ordered list of dirs we will probe for weights/metrics. The Streamlit sidebar lets the user override the primary directory, but if the user-provided one has no weights, we fall back to the shipped real_eval_* snapshots (the only ones that exist in a fresh clone). """ primary = Path(primary) dirs = [primary] repo_root = Path(__file__).resolve().parent for name in ARTIFACT_DIR_NAMES: candidate = repo_root / name if candidate.exists() and candidate not in dirs: dirs.append(candidate) return dirs def load_model_weights(model_type, artifacts_dir): """Find best_weights.weights.h5 (or any *.weights.h5) for the given model. Probes the user-provided artifacts_dir first, then falls back to real_eval_fixed/, real_eval_current/, and artifacts/ so the dashboard works out of the box with the snapshot weights that ship with this repo. """ for base in _resolve_artifact_dirs(artifacts_dir): model_dir = base / model_type if not model_dir.exists(): continue for candidate in ( model_dir / 'best_weights.weights.h5', model_dir / 'best_weights.h5', ): if candidate.exists(): return candidate for candidate in model_dir.glob('*.weights.h5'): return candidate return None def load_model_metrics(model_type, artifacts_dir): """Find _evaluation_metrics.json across the configured artifact dirs.""" for base in _resolve_artifact_dirs(artifacts_dir): path = base / f'{model_type}_evaluation_metrics.json' if path.exists(): return load_metrics_json(path) return None def metric_summary(metrics): summary = { 'accuracy': None, 'precision': None, 'recall': None, 'f1_score': None, 'roc_auc': None, } if not metrics: return summary summary['roc_auc'] = metrics.get('roc_auc') report = metrics.get('classification_report', {}) if isinstance(report, dict): weighted = report.get('weighted avg', report.get('weighted_avg', {})) summary['accuracy'] = report.get('accuracy') summary['precision'] = weighted.get('precision') summary['recall'] = weighted.get('recall') summary['f1_score'] = weighted.get('f1-score', weighted.get('f1_score')) return summary def available_models(artifacts_dir): return [m for m in MODEL_TYPES if load_model_weights(m, artifacts_dir) or load_model_metrics(m, artifacts_dir)] def run_prediction(model_type, weight_path, image): from src.models import get_model model = get_model(model_type) model.load_weights(weight_path) x = np.asarray(image.resize((224, 224)), dtype=np.float32) / 255.0 score = model.predict(np.expand_dims(x, axis=0), verbose=0)[0][0] return score def render_metric_card(title, value, detail=''): return f"""
{title}
{value}
{detail}
""" def render_comparison_table(table_data): if not table_data: return '
No model comparison metrics available yet.
' rows = ''.join( f"{row['model'].upper()}{row['Accuracy']:.3f}{row['Precision']:.3f}{row['Recall']:.3f}{row['F1 Score']:.3f}{row['ROC AUC']:.3f}" for row in table_data ) return f"""
Model comparison results
{rows}
Model Accuracy Precision Recall F1 Score ROC AUC
""" def build_comparison_rows(artifacts_dir): rows = [] for model_type in MODEL_TYPES: metrics = load_model_metrics(model_type, artifacts_dir) if metrics: summary = metric_summary(metrics) rows.append({ 'model': model_type, 'Accuracy': summary['accuracy'] or 0.0, 'Precision': summary['precision'] or 0.0, 'Recall': summary['recall'] or 0.0, 'F1 Score': summary['f1_score'] or 0.0, 'ROC AUC': summary['roc_auc'] or 0.0, }) return rows def main(): st.set_page_config(page_title='Tri-Netra', layout='wide') st.markdown(page_style(), unsafe_allow_html=True) components.html(html_header(), height=180) # Display the brand logo at the top of the sidebar logo_path = Path(__file__).resolve().parent / "Dashboard_Images" / "logo.png" if logo_path.exists(): st.sidebar.image(str(logo_path), use_column_width=True) artifacts_dir = Path(st.sidebar.text_input('Artifacts directory', 'artifacts')) available = available_models(artifacts_dir) st.sidebar.markdown('---') st.sidebar.write('Available models:') for model_name in MODEL_TYPES: status = '✅' if model_name in available else '❌' st.sidebar.write(f'{status} {model_name.upper()}') st.sidebar.markdown('---') uploaded_file = st.sidebar.file_uploader('Upload MRI image', type=['jpg', 'jpeg', 'png']) metrics_rows = build_comparison_rows(artifacts_dir) best_model = max(metrics_rows, key=lambda item: item['Accuracy'] or 0) if metrics_rows else None left, right = st.columns([2, 1], gap='large') with left: st.markdown("
Performance Overview
", unsafe_allow_html=True) metric_cards = [] if best_model: metric_cards.append(render_metric_card('Top model', best_model['model'].upper(), 'Highest accuracy available')) metric_cards.append(render_metric_card('Accuracy', f"{best_model['Accuracy']:.3f}")) metric_cards.append(render_metric_card('Precision', f"{best_model['Precision']:.3f}")) metric_cards.append(render_metric_card('Recall', f"{best_model['Recall']:.3f}")) metric_cards.append(render_metric_card('F1 Score', f"{best_model['F1 Score']:.3f}")) else: metric_cards.append(render_metric_card('Ready to visualize', 'No models yet', 'Add metrics or weight files to ./artifacts')) st.markdown('
' + ''.join(metric_cards) + '
', unsafe_allow_html=True) st.markdown("
Model Comparison
", unsafe_allow_html=True) st.components.v1.html(render_comparison_table(metrics_rows), height=320) with right: st.markdown("
Image Prediction
", unsafe_allow_html=True) if uploaded_file is None: st.markdown("
Upload an MRI image
Drop a PNG or JPG scan to compare predictions across models.
", unsafe_allow_html=True) else: image = Image.open(uploaded_file).convert('RGB') st.image(image, caption='Uploaded MRI image', use_column_width=True) if available: predictions = [] for model_name in available: weight_path = load_model_weights(model_name, artifacts_dir) if weight_path: score = run_prediction(model_name, weight_path, image) predictions.append((model_name.upper(), score)) if predictions: cards = '' for name, score in predictions: label = 'TUMOR' if score >= 0.5 else 'NO TUMOR' cards += f"
{name}
{score:.4f}
{label}
" st.markdown('
' + cards + '
', unsafe_allow_html=True) else: st.warning('No trained weights found to run predictions.') else: st.warning('No available models found. Add model artifacts under ./artifacts.') st.markdown("
Tri-Netra — AI-powered MRI brain tumor analysis by Anannya Vyas.
", unsafe_allow_html=True) if __name__ == '__main__': main()