Commit
·
6af334f
1
Parent(s):
adabe36
init
Browse files- .streamlit/config.toml +11 -0
- Dockerfile +8 -5
- download_model.py +10 -0
- requirements.txt +6 -1
- src/streamlit_app.py +290 -38
.streamlit/config.toml
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
[server]
|
2 |
+
headless = true
|
3 |
+
enableCORS = false
|
4 |
+
port = 8501
|
5 |
+
enableXsrfProtection = false
|
6 |
+
|
7 |
+
[browser]
|
8 |
+
gatherUsageStats = false
|
9 |
+
|
10 |
+
[global]
|
11 |
+
disableWatchdogWarning = true
|
Dockerfile
CHANGED
@@ -9,13 +9,16 @@ RUN apt-get update && apt-get install -y \
|
|
9 |
git \
|
10 |
&& rm -rf /var/lib/apt/lists/*
|
11 |
|
12 |
-
COPY requirements.txt
|
13 |
-
COPY
|
|
|
|
|
14 |
|
15 |
-
RUN pip3 install -r requirements.txt
|
|
|
16 |
|
17 |
EXPOSE 8501
|
18 |
|
19 |
-
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health
|
20 |
|
21 |
-
ENTRYPOINT ["streamlit", "run", "src/streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"]
|
|
|
9 |
git \
|
10 |
&& rm -rf /var/lib/apt/lists/*
|
11 |
|
12 |
+
COPY requirements.txt /app
|
13 |
+
COPY download_model.py /app
|
14 |
+
COPY .streamlit/ /app/.streamlit/
|
15 |
+
COPY src/ /app/src/
|
16 |
|
17 |
+
RUN pip3 install --no-cache-dir -r requirements.txt
|
18 |
+
RUN python3 download_model.py
|
19 |
|
20 |
EXPOSE 8501
|
21 |
|
22 |
+
HEALTHCHECK CMD curl --fail http://localhost:8501/_stcore/health || exit 1
|
23 |
|
24 |
+
ENTRYPOINT ["streamlit", "run", "src/streamlit_app.py", "--server.port=8501", "--server.address=0.0.0.0"]
|
download_model.py
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from transformers import AutoModelForImageClassification, AutoImageProcessor
|
2 |
+
|
3 |
+
model_name = "Asmaa111/diabetic-eye"
|
4 |
+
save_path = "./diabetic_model"
|
5 |
+
|
6 |
+
model = AutoModelForImageClassification.from_pretrained(model_name)
|
7 |
+
processor = AutoImageProcessor.from_pretrained(model_name)
|
8 |
+
|
9 |
+
model.save_pretrained(save_path)
|
10 |
+
processor.save_pretrained(save_path)
|
requirements.txt
CHANGED
@@ -1,3 +1,8 @@
|
|
1 |
altair
|
2 |
pandas
|
3 |
-
streamlit
|
|
|
|
|
|
|
|
|
|
|
|
1 |
altair
|
2 |
pandas
|
3 |
+
streamlit
|
4 |
+
torch
|
5 |
+
transformers
|
6 |
+
Pillow
|
7 |
+
requests
|
8 |
+
pdf2image
|
src/streamlit_app.py
CHANGED
@@ -1,40 +1,292 @@
|
|
1 |
-
import altair as alt
|
2 |
-
import numpy as np
|
3 |
-
import pandas as pd
|
4 |
import streamlit as st
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import streamlit as st
|
2 |
+
import torch
|
3 |
+
from transformers import AutoImageProcessor, AutoModelForImageClassification
|
4 |
+
from PIL import Image
|
5 |
+
import requests
|
6 |
+
from io import BytesIO
|
7 |
+
from pdf2image import convert_from_bytes
|
8 |
+
import json
|
9 |
+
import os
|
10 |
|
11 |
+
# تأكد أن Streamlit تستخدم مجلد .streamlit القابل للكتابة
|
12 |
+
os.environ["STREAMLIT_HOME"] = "/app/.streamlit"
|
13 |
+
|
14 |
+
# Load Lottie animation
|
15 |
+
def load_lottiefile(filepath: str):
|
16 |
+
if filepath.startswith(('http://', 'https://')):
|
17 |
+
response = requests.get(filepath)
|
18 |
+
response.raise_for_status()
|
19 |
+
return response.json()
|
20 |
+
else:
|
21 |
+
with open(filepath, "r") as f:
|
22 |
+
return json.load(f)
|
23 |
+
|
24 |
+
# Load the model and processor
|
25 |
+
# model_path = "Asmaa111/diabetic-eye" # new version
|
26 |
+
model_path = "./diabetic_model" # for space
|
27 |
+
# model_path = r"C:\Users\Milestone\dinov2-base-finetuned-eye"
|
28 |
+
# model_path = r"AsmaaElnagger/Diabetic_RetinoPathy_detection" # old version
|
29 |
+
|
30 |
+
|
31 |
+
@st.cache_resource
|
32 |
+
def load_model():
|
33 |
+
|
34 |
+
processor = AutoImageProcessor.from_pretrained(model_path)
|
35 |
+
model = AutoModelForImageClassification.from_pretrained(model_path)
|
36 |
+
model.eval()
|
37 |
+
return model, processor
|
38 |
+
|
39 |
+
def predict(image, model, processor):
|
40 |
+
inputs = processor(images=image, return_tensors="pt")
|
41 |
+
with torch.no_grad():
|
42 |
+
outputs = model(**inputs)
|
43 |
+
predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
|
44 |
+
predicted_class = predictions.argmax().item()
|
45 |
+
predicted_label = model.config.id2label[predicted_class]
|
46 |
+
return predicted_label
|
47 |
+
|
48 |
+
# App Config
|
49 |
+
st.set_page_config(
|
50 |
+
page_title="Diabetic Eye Classifier",
|
51 |
+
page_icon="👁️",
|
52 |
+
layout="wide"
|
53 |
+
)
|
54 |
+
|
55 |
+
# Custom CSS with animations
|
56 |
+
st.markdown("""
|
57 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
58 |
+
<style>
|
59 |
+
@keyframes fadeIn {
|
60 |
+
from { opacity: 0; transform: translateY(20px); }
|
61 |
+
to { opacity: 1; transform: translateY(0); }
|
62 |
+
}
|
63 |
+
.section {
|
64 |
+
padding: 4rem 0;
|
65 |
+
animation: fadeIn 0.8s ease-out;
|
66 |
+
}
|
67 |
+
.hero {
|
68 |
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
69 |
+
padding: 5rem 1rem;
|
70 |
+
border-radius: 10px;
|
71 |
+
margin-bottom: 3rem;
|
72 |
+
}
|
73 |
+
.feature-card {
|
74 |
+
padding: 2rem;
|
75 |
+
border-radius: 10px;
|
76 |
+
background: white;
|
77 |
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
78 |
+
height: 100%;
|
79 |
+
transition: transform 0.3s ease;
|
80 |
+
}
|
81 |
+
.feature-card:hover {
|
82 |
+
transform: translateY(-5px);
|
83 |
+
}
|
84 |
+
.team-card {
|
85 |
+
text-align: center;
|
86 |
+
padding: 1.5rem;
|
87 |
+
}
|
88 |
+
.testimonial-card {
|
89 |
+
padding: 2rem;
|
90 |
+
background: white;
|
91 |
+
border-radius: 10px;
|
92 |
+
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
93 |
+
margin-bottom: 1rem;
|
94 |
+
}
|
95 |
+
.icon {
|
96 |
+
font-size: 2.5rem;
|
97 |
+
margin-bottom: 1rem;
|
98 |
+
color: #4b6cb7;
|
99 |
+
}
|
100 |
+
.stButton>button {
|
101 |
+
transition: all 0.3s ease;
|
102 |
+
}
|
103 |
+
.stButton>button:hover {
|
104 |
+
transform: scale(1.02);
|
105 |
+
}
|
106 |
+
.medical-info {
|
107 |
+
background: #f0f8ff;
|
108 |
+
padding: 1.5rem;
|
109 |
+
border-radius: 10px;
|
110 |
+
margin: 1rem 0;
|
111 |
+
}
|
112 |
+
.image-section-prefix img{
|
113 |
+
height: 300px ! IMPORTANT;
|
114 |
+
max-width: 300px ! IMPORTANT;
|
115 |
+
text-align: center;
|
116 |
+
}
|
117 |
+
</style>
|
118 |
+
""", unsafe_allow_html=True)
|
119 |
+
|
120 |
+
# Hero Section with animation
|
121 |
+
with st.container():
|
122 |
+
st.markdown('<div class="hero">', unsafe_allow_html=True)
|
123 |
+
col1, col2 = st.columns([2, 1])
|
124 |
+
with col1:
|
125 |
+
st.title("👁️ Advanced Diabetic Retinopathy Screening")
|
126 |
+
st.markdown("""
|
127 |
+
**Revolutionizing** early detection of diabetic eye disease with **AI-powered** analysis.
|
128 |
+
Get instant preliminary screening results from retinal images.
|
129 |
+
""")
|
130 |
+
|
131 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
132 |
+
|
133 |
+
# Medical Information Section
|
134 |
+
with st.container():
|
135 |
+
st.markdown('<div class="section">', unsafe_allow_html=True)
|
136 |
+
st.header("ℹ️ About Diabetic Retinopathy", anchor="about-dr")
|
137 |
+
|
138 |
+
with st.expander("What is Diabetic Retinopathy?"):
|
139 |
+
st.markdown("""
|
140 |
+
Diabetic retinopathy is a diabetes complication that affects eyes. It's caused by damage to the blood vessels
|
141 |
+
of the light-sensitive tissue at the back of the eye (retina). At first, diabetic retinopathy might cause no
|
142 |
+
symptoms or only mild vision problems. Eventually, it can cause blindness.
|
143 |
+
""")
|
144 |
+
|
145 |
+
with st.expander("Risk Factors"):
|
146 |
+
cols = st.columns(2)
|
147 |
+
with cols[0]:
|
148 |
+
st.markdown("""
|
149 |
+
- **Duration of diabetes**: The longer you have diabetes, the greater your risk
|
150 |
+
- **Poor blood sugar control**
|
151 |
+
- **High blood pressure**
|
152 |
+
- **High cholesterol**
|
153 |
+
""")
|
154 |
+
with cols[1]:
|
155 |
+
st.markdown("""
|
156 |
+
- **Pregnancy**
|
157 |
+
- **Tobacco use**
|
158 |
+
- **Being African-American, Hispanic or Native American**
|
159 |
+
""")
|
160 |
+
|
161 |
+
with st.expander("Prevention Tips"):
|
162 |
+
st.markdown("""
|
163 |
+
- **Manage your diabetes**: Keep your blood sugar levels in target range
|
164 |
+
- **Monitor your blood sugar levels**
|
165 |
+
- **Keep blood pressure and cholesterol under control**
|
166 |
+
- **Quit smoking**
|
167 |
+
- **Pay attention to vision changes**
|
168 |
+
- **Have regular eye exams**
|
169 |
+
""")
|
170 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
171 |
+
|
172 |
+
# Features Section
|
173 |
+
with st.container():
|
174 |
+
st.markdown('<div class="section">', unsafe_allow_html=True)
|
175 |
+
st.header("✨ Key Features", anchor="features")
|
176 |
+
cols = st.columns(3)
|
177 |
+
features = [
|
178 |
+
{"icon": "fa-brain", "title": "AI-Powered Analysis",
|
179 |
+
"desc": "Our deep learning model provides accurate preliminary screening in seconds"},
|
180 |
+
{"icon": "fa-file-pdf", "title": "PDF Report Processing",
|
181 |
+
"desc": "Upload medical reports and we'll extract and analyze the images"},
|
182 |
+
{"icon": "fa-shield-alt", "title": "Secure & Private",
|
183 |
+
"desc": "HIPAA compliant processing with no data retention"}
|
184 |
+
]
|
185 |
+
for i, col in enumerate(cols):
|
186 |
+
with col:
|
187 |
+
st.markdown(f"""
|
188 |
+
<div class="feature-card">
|
189 |
+
<div class="icon"><i class="fas {features[i]['icon']}"></i></div>
|
190 |
+
<h3>{features[i]['title']}</h3>
|
191 |
+
<p>{features[i]['desc']}</p>
|
192 |
+
</div>
|
193 |
+
""", unsafe_allow_html=True)
|
194 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
195 |
+
|
196 |
+
|
197 |
+
# How It Works Section
|
198 |
+
with st.container():
|
199 |
+
st.markdown('<div class="section">', unsafe_allow_html=True)
|
200 |
+
st.header("🔍 How It Works", anchor="how-it-works")
|
201 |
+
steps = [
|
202 |
+
{"title": "1. Upload Image", "desc": "Provide a retinal image or PDF report"},
|
203 |
+
{"title": "2. AI Analysis", "desc": "Our model processes the image in seconds"},
|
204 |
+
{"title": "3. Get Results", "desc": "Receive preliminary screening results"}
|
205 |
+
]
|
206 |
+
for step in steps:
|
207 |
+
with st.expander(step["title"]):
|
208 |
+
st.write(step["desc"])
|
209 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
210 |
+
|
211 |
+
|
212 |
+
# Classifier Section
|
213 |
+
model, processor = load_model()
|
214 |
+
|
215 |
+
with st.container():
|
216 |
+
st.markdown('<div class="section">', unsafe_allow_html=True)
|
217 |
+
st.header("📷 Try Our Classifier", anchor="classifier")
|
218 |
+
|
219 |
+
col1, col2 = st.columns([1, 2])
|
220 |
+
with col1:
|
221 |
+
option = st.radio(
|
222 |
+
"Select input method:",
|
223 |
+
("Upload Image/PDF", "Image URL"),
|
224 |
+
index=0
|
225 |
+
)
|
226 |
+
|
227 |
+
image = None
|
228 |
+
if option == "Upload Image/PDF":
|
229 |
+
uploaded_file = st.file_uploader(
|
230 |
+
"Choose file",
|
231 |
+
type=["jpg", "png", "jpeg", "pdf"],
|
232 |
+
label_visibility="collapsed"
|
233 |
+
)
|
234 |
+
if uploaded_file:
|
235 |
+
if uploaded_file.type == "application/pdf":
|
236 |
+
images = convert_from_bytes(uploaded_file.read())
|
237 |
+
if images:
|
238 |
+
image = images[0]
|
239 |
+
else:
|
240 |
+
image = Image.open(uploaded_file)
|
241 |
+
else:
|
242 |
+
url = st.text_input("Enter image URL", placeholder="https://example.com/image.jpg")
|
243 |
+
if url:
|
244 |
+
try:
|
245 |
+
response = requests.get(url)
|
246 |
+
image = Image.open(BytesIO(response.content))
|
247 |
+
except:
|
248 |
+
st.error("Invalid URL or image")
|
249 |
+
|
250 |
+
with col2:
|
251 |
+
if image:
|
252 |
+
st.image(image, width=600)
|
253 |
+
if st.button("Analyze Image", type="primary"):
|
254 |
+
with st.spinner("Analyzing..."):
|
255 |
+
prediction = predict(image, model, processor)
|
256 |
+
st.success(f"**Prediction:** {prediction}")
|
257 |
+
#st.balloons()
|
258 |
+
else:
|
259 |
+
st.info("Please upload an image or enter a URL to begin analysis")
|
260 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
261 |
+
|
262 |
+
# Medical Disclaimer
|
263 |
+
with st.container():
|
264 |
+
st.markdown("""
|
265 |
+
<div class="medical-info">
|
266 |
+
<h4>Medical Disclaimer</h4>
|
267 |
+
<p>This tool provides preliminary screening only and is not a substitute for professional medical advice, diagnosis, or treatment.
|
268 |
+
Always seek the advice of your physician or other qualified health provider with any questions you may have regarding a medical condition.</p>
|
269 |
+
</div>
|
270 |
+
""", unsafe_allow_html=True)
|
271 |
+
|
272 |
+
# Footer
|
273 |
+
st.markdown("---")
|
274 |
+
cols = st.columns(3)
|
275 |
+
with cols[0]:
|
276 |
+
st.markdown("""
|
277 |
+
**Contact Us**
|
278 |
+
<i class="fas fa-envelope"></i> [email protected]
|
279 |
+
<i class="fas fa-phone"></i> (123) 456-7890
|
280 |
+
""", unsafe_allow_html=True)
|
281 |
+
with cols[1]:
|
282 |
+
st.markdown("""
|
283 |
+
**Quick Links**
|
284 |
+
[About DR](#about-dr)
|
285 |
+
[Features](#features)
|
286 |
+
[How It Works](#how-it-works)
|
287 |
+
""")
|
288 |
+
with cols[2]:
|
289 |
+
st.markdown("""
|
290 |
+
© 2025 Diabetic Eye Classifier
|
291 |
+
Medical AI Application
|
292 |
+
""")
|