File size: 11,322 Bytes
782e635
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
"""
=================================================================
DERMASCAN-AI β€” Professional Medical UI
Production Grade Healthcare Interface
=================================================================
"""

import json
import streamlit as st
import requests
from PIL import Image
from pathlib import Path

# Import components
from components.header import render_header
from components.sidebar import render_sidebar
from components.result_card import render_severity_banner, render_metrics, TIER_ICONS
from components.confidence_chart import render_confidence_chart
from components.care_advice_card import render_care_advice
from components.hospital_map import render_hospital_map

# ═══════════════════════════════════════════════════════════
# PAGE CONFIG
# ═══════════════════════════════════════════════════════════
st.set_page_config(
    page_title="DermaScan AI | Advanced Dermatology Analysis",
    page_icon="πŸ₯",
    layout="wide",
    initial_sidebar_state="expanded",
)

API_URL = "http://localhost:8000"

# ═══════════════════════════════════════════════════════════
# LOAD EXTERNAL DATA & STYLES
# ═══════════════════════════════════════════════════════════
config_dir = Path(__file__).parent.parent / "configs"

with open(config_dir / "india_cities.json", "r", encoding="utf-8") as f:
    STATE_CITIES = json.load(f)

# Load CSS
css_file = Path(__file__).parent / "assets" / "style.css"
with open(css_file, "r", encoding="utf-8") as f:
    st.markdown(f"<style>{f.read()}</style>", unsafe_allow_html=True)

# Remove tooltips with JavaScript
st.markdown("""
<script>
// Remove all title attributes that cause tooltips
document.addEventListener('DOMContentLoaded', function() {
    function removeTooltips() {
        const elements = document.querySelectorAll('[title]');
        elements.forEach(el => {
            if (el.getAttribute('title') === 'keyboard_double') {
                el.removeAttribute('title');
            }
        });
    }
    
    // Run immediately
    removeTooltips();
    
    // Run periodically to catch dynamically added elements
    setInterval(removeTooltips, 500);
    
    // Also run on mutations
    const observer = new MutationObserver(removeTooltips);
    observer.observe(document.body, { 
        childList: true, 
        subtree: true,
        attributes: true,
        attributeFilter: ['title']
    });
});
</script>
""", unsafe_allow_html=True)

# ═══════════════════════════════════════════════════════════
# SIDEBAR
# ═══════════════════════════════════════════════════════════
selected_state, selected_city = render_sidebar(STATE_CITIES)

# ═══════════════════════════════════════════════════════════
# HEADER
# ═══════════════════════════════════════════════════════════
render_header()

# ═══════════════════════════════════════════════════════════
# UPLOAD SECTION
# ═══════════════════════════════════════════════════════════
uploaded_file = st.file_uploader(
    "Upload a skin image for analysis",
    type=["jpg", "jpeg", "png"],
)

if uploaded_file:
    img_col, action_col = st.columns([1, 2])

    with img_col:
        image = Image.open(uploaded_file)
        st.markdown('<div class="image-container">', unsafe_allow_html=True)
        st.image(image, caption="πŸ“Έ Uploaded Image", width="stretch")
        st.markdown('</div>', unsafe_allow_html=True)

    with action_col:
        st.markdown(f"**πŸ“ Location:** {selected_city}, {selected_state}")
        st.markdown("")

        analyze = st.button("πŸ”¬ Analyze Image", width="stretch")

        if analyze:
            with st.spinner("πŸ”„ Analyzing your image with AI..."):
                try:
                    files = {
                        "file": (
                            uploaded_file.name,
                            uploaded_file.getvalue(),
                            uploaded_file.type,
                        )
                    }
                    params = {"city": selected_city, "state": selected_state}

                    resp = requests.post(
                        f"{API_URL}/predict",
                        files=files,
                        params=params,
                        timeout=60,
                    )

                    if resp.status_code == 200:
                        st.session_state["result"] = resp.json()
                        st.success("βœ… Analysis complete!")
                        st.rerun()
                    else:
                        st.error(f"❌ Server error: {resp.text}")

                except requests.exceptions.ConnectionError:
                    st.error(
                        "⚠️ Cannot connect to API server. "
                        "Open another terminal and run: `python -m api.app`"
                    )

        st.markdown("""
        <div class="pro-card">
            <h3>πŸ’‘ Tips for Best Results</h3>
            <p>
                βœ“ Use a clear, well-lit close-up photo<br>
                βœ“ Center the affected area in the frame<br>
                βœ“ Keep camera 10-15 cm from the skin<br>
                βœ“ Avoid shadows and reflections<br>
                βœ“ Use natural lighting when possible
            </p>
        </div>
        """, unsafe_allow_html=True)


# ═══════════════════════════════════════════════════════════
# RESULTS SECTION
# ═══════════════════════════════════════════════════════════
if "result" in st.session_state:
    result = st.session_state["result"]

    st.markdown("---")

    # Severity Banner
    render_severity_banner(result)

    # Key Metrics
    render_metrics(result)

    # Cancer Warning
    cancer_warning = result.get("cancer_warning", "")
    if cancer_warning:
        st.markdown(
            f'<div class="warning-box">'
            f'<div class="warning-box-icon">⚠️</div>'
            f"<div><strong>MEDICAL ALERT:</strong> {cancer_warning}</div>"
            f"</div>",
            unsafe_allow_html=True,
        )

    # Tabs
    tab1, tab2, tab3, tab4 = st.tabs(
        ["πŸ“‹ Diagnosis", "πŸ“Š Confidence Analysis", "πŸ’Š Care Advice", "πŸ₯ Find Hospitals"]
    )

    # Tab 1: Diagnosis
    with tab1:
        st.markdown(
            f'<div class="pro-card">'
            f'<h3>πŸ”¬ {result["predicted_class"]}</h3>'
            f'<p>{result.get("description", "")}</p>'
            f'<p style="margin-top:1rem;padding:0.8rem;background:#334155;border-radius:8px;">'
            f'<strong>⏰ {result.get("urgency_message", "")}</strong></p>'
            f"</div>",
            unsafe_allow_html=True,
        )

        st.markdown(
            f'<div class="pro-card">'
            f"<h3>🎯 AI Confidence Assessment</h3>"
            f'<p>{result.get("confidence_message", "")}</p>'
            f"</div>",
            unsafe_allow_html=True,
        )

        diff = result.get("differential_diagnosis", [])
        if len(diff) > 1:
            diff_html = '<div class="pro-card"><h3>πŸ” Differential Diagnosis</h3>'
            diff_html += '<p style="margin-bottom:1rem;color:#cbd5e1;">Other possible conditions to consider:</p>'
            for d in diff:
                d_icon = TIER_ICONS.get(d.get("tier", ""), "βšͺ")
                d_prob = d.get("probability", 0)
                diff_html += (
                    f'<div class="info-item">'
                    f'<div class="info-item-icon">{d_icon}</div>'
                    f'<div><strong>{d["class_name"]}</strong> β€” Probability: {d_prob:.1%}</div>'
                    f"</div>"
                )
            diff_html += "</div>"
            st.markdown(diff_html, unsafe_allow_html=True)

    # Tab 2: Confidence Chart
    with tab2:
        render_confidence_chart(result)

    # Tab 3: Care Advice
    with tab3:
        render_care_advice(result)

    # Tab 4: Hospitals
    with tab4:
        render_hospital_map(result, selected_city, selected_state)

    # Disclaimer
    disclaimer = result.get(
        "disclaimer",
        "This is an AI tool for educational purposes only. "
        "Not a substitute for professional medical diagnosis.",
    )
    st.markdown(
        f'<div class="disclaimer-box">'
        f"<strong>βš•οΈ MEDICAL DISCLAIMER:</strong> {disclaimer}"
        f"</div>",
        unsafe_allow_html=True,
    )

    inf_t = result.get("inference_time", 0)
    st.markdown(
        f'<p style="text-align:center;color:#94a3b8;font-size:0.85rem;margin-top:1.5rem;">'
        f'⚑ Analysis completed in {inf_t:.2f}s | 🧠 EfficientNet-B3 | πŸ₯ DermaScan AI v1.0'
        f'</p>',
        unsafe_allow_html=True
    )

elif not uploaded_file:
    st.markdown(
        '<div class="upload-placeholder">'
        '<div class="icon">πŸ“Έ</div>'
        "<h3>Upload a Skin Image to Begin Analysis</h3>"
        "<p>Our advanced AI system will analyze the image, identify potential conditions, "
        "provide personalized care recommendations, and help you locate nearby medical facilities.</p>"
        "</div>",
        unsafe_allow_html=True,
    )

# ═══════════════════════════════════════════════════════════
# FOOTER
# ═══════════════════════════════════════════════════════════
st.markdown("---")
st.markdown(
    "<p style='text-align:center;color:#94a3b8;font-size:0.8rem;line-height:1.8;'>"
    "πŸ₯ <strong>DermaScan AI</strong> | 🧠 EfficientNet-B3 Architecture | πŸ“Š HAM10000 + DermNet Dataset<br>"
    "πŸ”¬ 13 Skin Conditions | 🎯 96% AUC-ROC Accuracy | ⚑ Real-time Analysis<br>"
    "πŸ› οΈ Built with PyTorch β€’ FastAPI β€’ Streamlit<br>"
    "<em>For educational and research purposes only. Not a substitute for professional medical advice.</em>"
    "</p>",
    unsafe_allow_html=True,
)