File size: 8,882 Bytes
25a71e0 b427d16 25a71e0 b427d16 25a71e0 b427d16 25a71e0 b427d16 25a71e0 b427d16 25a71e0 b427d16 25a71e0 b427d16 25a71e0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
import streamlit as st
import cv2
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
def render_css():
"""Injects custom CSS for a professional medical dashboard optimized for horizontal screens."""
st.markdown("""
<style>
.main {
max-width: 100% !important;
padding: 1rem 2rem;
}
.block-container {
max-width: 100% !important;
padding-left: 2rem !important;
padding-right: 2rem !important;
}
h1, h2, h3 {
font-family: 'Segoe UI', sans-serif;
font-weight: 600;
}
div[data-testid="stMetric"] {
padding: 18px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
img {
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
width: 100%;
height: auto;
display: block;
image-rendering: auto;
}
.stImage > div {
width: 100%;
max-width: 100%;
}
.report-box {
padding: 24px;
border-radius: 12px;
border-left: 6px solid #dc3545;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.stTabs [data-baseweb="tab-list"] {
gap: 8px;
}
.stTabs [data-baseweb="tab"] {
padding: 12px 24px;
border-radius: 8px;
}
.dataframe {
font-size: 0.95rem !important;
}
</style>
<script>
const meta = window.parent?.document?.querySelector('meta[http-equiv="Permissions-Policy"]');
if (meta) {
meta.setAttribute('content', 'geolocation=(), microphone=()');
}
</script>
""", unsafe_allow_html=True)
def render_header(app_name, version):
st.title(app_name)
st.caption(f"Klinik Karar Destek Sistemi | Sürüm: {version}")
st.markdown("---")
def apply_heatmap_overlay(img_rgb, heatmap_float, colormap=cv2.COLORMAP_JET, alpha=0.6):
if np.max(heatmap_float) > 0:
heatmap_float = heatmap_float / np.max(heatmap_float)
heatmap_uint8 = np.uint8(255 * heatmap_float)
heatmap_colored = cv2.applyColorMap(heatmap_uint8, colormap)
heatmap_colored = cv2.cvtColor(heatmap_colored, cv2.COLOR_BGR2RGB)
if heatmap_colored.shape[:2] != img_rgb.shape[:2]:
heatmap_colored = cv2.resize(heatmap_colored, (img_rgb.shape[1], img_rgb.shape[0]))
overlay = cv2.addWeighted(img_rgb, 1-alpha, heatmap_colored, alpha, 0)
return overlay
def render_classification_panel(img_rgb, diagnosis, cls_conf, seg_conf, gradcam_map):
st.subheader("1. Tanı ve Model Güven Analizi")
col_diag, col_orig, col_xai = st.columns([1.2, 1.4, 1.4])
with col_diag:
# Dynamic Styling based on Diagnosis
color = "#dc3545" if "Benign" not in diagnosis else "#28a745"
st.markdown(f"""
<div style="padding: 24px; border-radius: 12px; border-left: 6px solid {color}; box-shadow: 0 4px 12px rgba(0,0,0,0.1);">
<h3 style="margin:0; color: {color} !important; font-size: 1.8rem;">{diagnosis}</h3>
<p style="margin-top: 12px;">Yapay Zeka Nihai Kararı</p>
</div>
""", unsafe_allow_html=True)
st.markdown("#### Güvenilirlik Metrikleri")
c1, c2 = st.columns(2)
c1.metric("Teşhis Güveni", f"%{cls_conf*100:.1f}", help="ResNet50 modelinin sınıflandırma kesinliği.")
c2.metric("Segmentasyon Güveni", f"%{seg_conf*100:.1f}", help="CIA-Net modelinin hücre tespit kesinliği (Ortalama Piksel Olasılığı).")
if cls_conf < 0.70:
st.warning("Düşük güven skoru. Lütfen manuel inceleme yapınız.")
with col_orig:
st.image(img_rgb, caption="Orijinal Görüntü", use_column_width=True)
with col_xai:
overlay = apply_heatmap_overlay(img_rgb, gradcam_map, alpha=0.5)
st.image(overlay, caption="Yapay Zeka Odak Alanları (Grad-CAM)", use_column_width=True)
def render_segmentation_panel(img_rgb, nuc_map, uncertainty_map, instance_mask, stats):
st.markdown("---")
st.subheader("2. Hücresel Morfoloji ve Biyolojik Analiz")
tab_seg, tab_unc, tab_data, tab_plots = st.tabs([
"Segmentasyon",
"Belirsizlik (Uncertainty)",
"Kantitatif Veriler",
"Dağılım Grafikleri"
])
with tab_seg:
c1, c2 = st.columns(2)
with c1:
nuc_colored = apply_heatmap_overlay(img_rgb, nuc_map, colormap=cv2.COLORMAP_OCEAN, alpha=0.6)
st.image(nuc_colored, caption="Nükleus Olasılık Haritası (AI Çıktısı)", use_column_width=True)
with c2:
mask_rgb = np.zeros_like(img_rgb)
mask_rgb[instance_mask > 0] = [0, 255, 0] # Green
overlay = cv2.addWeighted(img_rgb, 0.7, mask_rgb, 0.3, 0)
st.image(overlay, caption="Ayrıştırılmış Hücreler (Watershed)", use_column_width=True)
with tab_unc:
c1, c2 = st.columns([1, 2])
with c1:
st.info("""
**Nasıl Okunmalı?**
* **Siyah/Koyu Alanlar:** Modelin kararından %100 emin olduğu bölgeler.
* **Parlak/Sarı Alanlar:** Modelin kararsız kaldığı ("Burası hücre mi değil mi?") bölgeler.
Sarı alanların çokluğu, görüntünün kalitesiz veya dokunun karmaşık olduğunu gösterir.
""")
with c2:
unc_colored = apply_heatmap_overlay(img_rgb, uncertainty_map, colormap=cv2.COLORMAP_INFERNO, alpha=0.7)
st.image(unc_colored, caption="Model Entropi (Belirsizlik) Haritası", use_column_width=True)
with tab_data:
if not stats.empty:
m1, m2, m3, m4 = st.columns(4)
m1.metric("Toplam Hücre", f"{len(stats)}")
m2.metric("Ort. Alan", f"{stats['Area'].mean():.1f} px")
m3.metric("Düzensizlik", f"{1 - stats['Circularity'].mean():.2f}", help="0'a yaklaştıkça hücreler daha yuvarlak (sağlıklı) demektir.")
m4.metric("Varyasyon", f"{stats['Area'].std():.1f}", help="Yüksek varyasyon (Anizonükleoz) kanser belirtisi olabilir.")
st.dataframe(
stats.style.background_gradient(cmap='Reds', subset=['Area'])
.format("{:.2f}"),
width="stretch"
)
else:
st.warning("Hücre tespit edilemedi.")
with tab_plots:
if not stats.empty:
# HD Graphics Settings - High DPI
plt.style.use('seaborn-v0_8-whitegrid')
sns.set_context("notebook", font_scale=1.3)
sns.set_palette("husl")
c1, c2 = st.columns(2)
with c1:
fig, ax = plt.subplots(figsize=(10, 6), dpi=150)
sns.histplot(stats['Area'], kde=True, ax=ax, color='#3498db', fill=True, alpha=0.7, linewidth=2)
ax.set_title("Hücre Boyut Dağılımı (Histogram)", fontsize=16, fontweight='bold', pad=20)
ax.set_xlabel("Alan (Piksel)", fontsize=13, fontweight='600')
ax.set_ylabel("Frekans", fontsize=13, fontweight='600')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.grid(True, alpha=0.3, linestyle='--')
plt.tight_layout()
st.pyplot(fig)
plt.close()
with c2:
fig, ax = plt.subplots(figsize=(10, 6), dpi=150)
scatter = sns.scatterplot(data=stats, x='Area', y='Circularity', hue='Solidity',
ax=ax, palette='viridis', s=100, alpha=0.8, edgecolor='white', linewidth=1.5)
ax.set_title("Boyut vs. Şekil Düzensizliği", fontsize=16, fontweight='bold', pad=20)
ax.set_xlabel("Alan (Piksel)", fontsize=13, fontweight='600')
ax.set_ylabel("Dairesellik", fontsize=13, fontweight='600')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.grid(True, alpha=0.3, linestyle='--')
ax.legend(title='Solidity', title_fontsize=11, fontsize=10, loc='best', frameon=True,
fancybox=True, shadow=True, framealpha=0.95)
plt.tight_layout()
st.pyplot(fig)
plt.close() |