Ed-Strak commited on
Commit
3016cb6
Β·
verified Β·
1 Parent(s): 12c510c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +271 -271
app.py CHANGED
@@ -1,272 +1,272 @@
1
- import streamlit as st
2
- from PIL import Image
3
- import os
4
- import base64
5
- import io
6
- from dotenv import load_dotenv
7
- from groq import Groq
8
- from reportlab.lib.pagesizes import letter
9
- from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
10
- from reportlab.lib.styles import getSampleStyleSheet
11
-
12
- # ======================
13
- # CONFIGURATION SETTINGS
14
- # ======================
15
- PAGE_CONFIG = {
16
- "page_title": "Radiology Analyzer",
17
- "page_icon": "🩺",
18
- "layout": "wide",
19
- "initial_sidebar_state": "expanded"
20
- }
21
-
22
- ALLOWED_FILE_TYPES = ['png', 'jpg', 'jpeg']
23
-
24
- CSS_STYLES = """
25
- <style>
26
- .main { background-color: #f4f9f9; color: #000000; }
27
- .sidebar .sidebar-content { background-color: #d1e7dd; }
28
- .stTextInput textarea { color: #000000 !important; }
29
- .stSelectbox div[data-baseweb="select"],
30
- .stSelectbox option,
31
- .stSelectbox div[role="listbox"] div {
32
- color: black !important;
33
- background-color: #d1e7dd !important;
34
- }
35
- .stSelectbox svg { fill: black !important; }
36
- .main-title {
37
- font-size: 88px;
38
- font-weight: bold;
39
- color: rgb(33, 238, 238);
40
- }
41
- .sub-title {
42
- font-size: 100px;
43
- color: #6B6B6B;
44
- margin-top: -1px;
45
- }
46
- .stButton>button {
47
- background-color: rgb(33, 225, 250);
48
- color: white;
49
- font-size: 69px;
50
- }
51
- .stImage img {
52
- border-radius: 10px;
53
- box-shadow: 2px 2px 10px rgba(0,0,0,0.1);
54
- }
55
- .logo {
56
- text-align: center;
57
- margin-bottom: 20px;
58
- }
59
- .report-container {
60
- background-color: #ffffff;
61
- border-radius: 15px;
62
- padding: 25px;
63
- margin-top: 20px;
64
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
65
- border-left: 5px solid #21eeef;
66
- }
67
- .report-text {
68
- font-family: 'Courier New', monospace;
69
- font-size: 16px;
70
- line-height: 1.6;
71
- color: #2c3e50;
72
- }
73
- .download-btn {
74
- background-color: #21eeef !important;
75
- color: white !important;
76
- border: none !important;
77
- border-radius: 8px !important;
78
- padding: 12px 24px !important;
79
- }
80
- </style>
81
- """
82
-
83
- # ======================
84
- # CORE FUNCTIONS
85
- # ======================
86
- def configure_application():
87
- """Initialize application settings and styling"""
88
- st.set_page_config(**PAGE_CONFIG)
89
- st.markdown(CSS_STYLES, unsafe_allow_html=True)
90
-
91
- def initialize_api_client():
92
- """Create and validate Groq API client"""
93
- load_dotenv()
94
- api_key = os.getenv("GROQ_API_KEY")
95
-
96
- if not api_key:
97
- st.error("API key not found. Please verify .env configuration.")
98
- st.stop()
99
-
100
- return Groq(api_key=api_key)
101
-
102
- def encode_logo(image_path):
103
- """Encode logo image to base64"""
104
- try:
105
- with open(image_path, "rb") as img_file:
106
- return base64.b64encode(img_file.read()).decode("utf-8")
107
- except FileNotFoundError:
108
- st.error("Logo image not found! Using placeholder.")
109
- return ""
110
-
111
- def process_image_data(uploaded_file):
112
- """Convert image to base64 encoded string"""
113
- try:
114
- image = Image.open(uploaded_file)
115
- buffer = io.BytesIO()
116
- image.save(buffer, format=image.format)
117
- return base64.b64encode(buffer.getvalue()).decode('utf-8'), image.format
118
- except Exception as e:
119
- st.error(f"Image processing error: {str(e)}")
120
- return None, None
121
-
122
- def generate_pdf_report(report_text):
123
- """Generate PDF document from report text"""
124
- buffer = io.BytesIO()
125
- doc = SimpleDocTemplate(buffer, pagesize=letter)
126
- styles = getSampleStyleSheet()
127
- story = []
128
-
129
- # Add title
130
- title = Paragraph("<b>Radiology Report</b>", styles['Title'])
131
- story.append(title)
132
- story.append(Spacer(1, 12))
133
-
134
- # Add report content
135
- content = Paragraph(report_text.replace('\n', '<br/>'), styles['BodyText'])
136
- story.append(content)
137
-
138
- doc.build(story)
139
- buffer.seek(0)
140
- return buffer
141
-
142
- def generate_radiology_report(uploaded_file, client):
143
- """Generate AI-powered radiology analysis"""
144
- base64_image, img_format = process_image_data(uploaded_file)
145
-
146
- if not base64_image:
147
- return None
148
-
149
- image_url = f"data:image/{img_format.lower()};base64,{base64_image}"
150
-
151
- try:
152
- response = client.chat.completions.create(
153
- model="llama-3.2-11b-vision-preview",
154
- messages=[{
155
- "role": "user",
156
- "content": [
157
- {"type": "text", "text": (
158
- "As an AI radiologist, provide a detailed structured report including: "
159
- "1. Imaging modality identification\n2. Anatomical structures visualized\n"
160
- "3. Abnormal findings description\n4. Differential diagnoses\n"
161
- "5. Clinical correlation recommendations"
162
- )},
163
- {"type": "image_url", "image_url": {"url": image_url}},
164
- ]
165
- }],
166
- temperature=0.2,
167
- max_tokens=400,
168
- top_p=0.5
169
- )
170
- return response.choices[0].message.content
171
- except Exception as e:
172
- st.error(f"API communication error: {str(e)}")
173
- return None
174
-
175
- # ======================
176
- # UI COMPONENTS
177
- # ======================
178
- def display_main_interface():
179
- """Render primary application interface"""
180
- # Encode logo image
181
- logo_b64 = encode_logo("src/radiology.png")
182
-
183
- # Center the logo and title using HTML and CSS
184
- st.markdown(
185
- f"""
186
- <div style="text-align: center;">
187
- <div class="logo">
188
- <img src="data:image/png;base64,{logo_b64}" width="100">
189
- </div>
190
- <p class="main-title"> Radiology Analyzer</p>
191
- <p class="sub-title">Advanced Medical Imaging Analysis</p>
192
- </div>
193
- """,
194
- unsafe_allow_html=True
195
- )
196
-
197
- st.markdown("---")
198
-
199
- # Action buttons
200
- col1, col2 = st.columns([1, 1])
201
- with col1:
202
- if st.session_state.get('analysis_result'):
203
- pdf_report = generate_pdf_report(st.session_state.analysis_result)
204
- st.download_button(
205
- label="πŸ“„ Download PDF Report",
206
- data=pdf_report,
207
- file_name="radiology_report.pdf",
208
- mime="application/pdf",
209
- use_container_width=True,
210
- help="Download formal PDF version of the report",
211
- key="download_pdf"
212
- )
213
-
214
- with col2:
215
- if st.button("Clear Analysis πŸ—‘οΈ", use_container_width=True, help="Remove current results"):
216
- st.session_state.pop('analysis_result')
217
- st.rerun()
218
-
219
- # Display analysis results
220
- if st.session_state.get('analysis_result'):
221
- st.markdown("### 🎯 Radiological Findings Report")
222
- st.markdown(
223
- f'<div class="report-container"><div class="report-text">{st.session_state.analysis_result}</div></div>',
224
- unsafe_allow_html=True
225
- )
226
-
227
- def render_sidebar(client):
228
- """Create sidebar interface elements"""
229
- with st.sidebar:
230
- st.divider()
231
- st.markdown("### Diagnostic Capabilities")
232
- st.markdown("""
233
- - **Multi-Modality Analysis**: X-ray, MRI, CT, Ultrasound
234
- - **Pathology Detection**: Fractures, tumors, infections
235
- - **Comparative Analysis**: Track disease progression
236
- - **Structured Reporting**: Standardized output format
237
- - **Clinical Correlation**: Suggested next steps
238
- - **Disclaimer: This service does not provide medical advice, consult a doctor for analysis and treatment
239
- """)
240
- st.divider()
241
-
242
- st.subheader("Image Upload Section")
243
- uploaded_file = st.file_uploader(
244
- "Select Medical Image",
245
- type=ALLOWED_FILE_TYPES,
246
- help="Supported formats: PNG, JPG, JPEG"
247
- )
248
-
249
- if uploaded_file:
250
- st.image(Image.open(uploaded_file),
251
- caption="Uploaded Medical Image",
252
- use_container_width=True)
253
-
254
- if st.button("Initiate Analysis πŸ”", use_container_width=True):
255
- with st.spinner("Analyzing image. This may take 20-30 seconds..."):
256
- report = generate_radiology_report(uploaded_file, client)
257
- st.session_state.analysis_result = report
258
- st.rerun()
259
-
260
- # ======================
261
- # APPLICATION ENTRYPOINT
262
- # ======================
263
- def main():
264
- """Primary application controller"""
265
- configure_application()
266
- groq_client = initialize_api_client()
267
-
268
- display_main_interface()
269
- render_sidebar(groq_client)
270
-
271
- if __name__ == "__main__":
272
  main()
 
1
+ import streamlit as st
2
+ from PIL import Image
3
+ import os
4
+ import base64
5
+ import io
6
+ from dotenv import load_dotenv
7
+ from groq import Groq
8
+ from reportlab.lib.pagesizes import letter
9
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
10
+ from reportlab.lib.styles import getSampleStyleSheet
11
+
12
+ # ======================
13
+ # CONFIGURATION SETTINGS
14
+ # ======================
15
+ PAGE_CONFIG = {
16
+ "page_title": "Radiology Analyzer",
17
+ "page_icon": "🩺",
18
+ "layout": "wide",
19
+ "initial_sidebar_state": "expanded"
20
+ }
21
+
22
+ ALLOWED_FILE_TYPES = ['png', 'jpg', 'jpeg']
23
+
24
+ CSS_STYLES = """
25
+ <style>
26
+ .main { background-color: #f4f9f9; color: #000000; }
27
+ .sidebar .sidebar-content { background-color: #d1e7dd; }
28
+ .stTextInput textarea { color: #000000 !important; }
29
+ .stSelectbox div[data-baseweb="select"],
30
+ .stSelectbox option,
31
+ .stSelectbox div[role="listbox"] div {
32
+ color: black !important;
33
+ background-color: #d1e7dd !important;
34
+ }
35
+ .stSelectbox svg { fill: black !important; }
36
+ .main-title {
37
+ font-size: 88px;
38
+ font-weight: bold;
39
+ color: rgb(33, 238, 238);
40
+ }
41
+ .sub-title {
42
+ font-size: 100px;
43
+ color: #6B6B6B;
44
+ margin-top: -1px;
45
+ }
46
+ .stButton>button {
47
+ background-color: rgb(33, 225, 250);
48
+ color: white;
49
+ font-size: 69px;
50
+ }
51
+ .stImage img {
52
+ border-radius: 10px;
53
+ box-shadow: 2px 2px 10px rgba(0,0,0,0.1);
54
+ }
55
+ .logo {
56
+ text-align: center;
57
+ margin-bottom: 20px;
58
+ }
59
+ .report-container {
60
+ background-color: #ffffff;
61
+ border-radius: 15px;
62
+ padding: 25px;
63
+ margin-top: 20px;
64
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
65
+ border-left: 5px solid #21eeef;
66
+ }
67
+ .report-text {
68
+ font-family: 'Courier New', monospace;
69
+ font-size: 16px;
70
+ line-height: 1.6;
71
+ color: #2c3e50;
72
+ }
73
+ .download-btn {
74
+ background-color: #21eeef !important;
75
+ color: white !important;
76
+ border: none !important;
77
+ border-radius: 8px !important;
78
+ padding: 12px 24px !important;
79
+ }
80
+ </style>
81
+ """
82
+
83
+ # ======================
84
+ # CORE FUNCTIONS
85
+ # ======================
86
+ def configure_application():
87
+ """Initialize application settings and styling"""
88
+ st.set_page_config(**PAGE_CONFIG)
89
+ st.markdown(CSS_STYLES, unsafe_allow_html=True)
90
+
91
+ def initialize_api_client():
92
+ """Create and validate Groq API client"""
93
+ load_dotenv()
94
+ api_key = os.getenv("GROQ_API_KEY")
95
+
96
+ if not api_key:
97
+ st.error("API key not found. Please verify .env configuration.")
98
+ st.stop()
99
+
100
+ return Groq(api_key=api_key)
101
+
102
+ def encode_logo(image_path):
103
+ """Encode logo image to base64"""
104
+ try:
105
+ with open(image_path, "rb") as img_file:
106
+ return base64.b64encode(img_file.read()).decode("utf-8")
107
+ except FileNotFoundError:
108
+ st.error("Logo image not found! Using placeholder.")
109
+ return ""
110
+
111
+ def process_image_data(uploaded_file):
112
+ """Convert image to base64 encoded string"""
113
+ try:
114
+ image = Image.open(uploaded_file)
115
+ buffer = io.BytesIO()
116
+ image.save(buffer, format=image.format)
117
+ return base64.b64encode(buffer.getvalue()).decode('utf-8'), image.format
118
+ except Exception as e:
119
+ st.error(f"Image processing error: {str(e)}")
120
+ return None, None
121
+
122
+ def generate_pdf_report(report_text):
123
+ """Generate PDF document from report text"""
124
+ buffer = io.BytesIO()
125
+ doc = SimpleDocTemplate(buffer, pagesize=letter)
126
+ styles = getSampleStyleSheet()
127
+ story = []
128
+
129
+ # Add title
130
+ title = Paragraph("<b>Radiology Report</b>", styles['Title'])
131
+ story.append(title)
132
+ story.append(Spacer(1, 12))
133
+
134
+ # Add report content
135
+ content = Paragraph(report_text.replace('\n', '<br/>'), styles['BodyText'])
136
+ story.append(content)
137
+
138
+ doc.build(story)
139
+ buffer.seek(0)
140
+ return buffer
141
+
142
+ def generate_radiology_report(uploaded_file, client):
143
+ """Generate AI-powered radiology analysis"""
144
+ base64_image, img_format = process_image_data(uploaded_file)
145
+
146
+ if not base64_image:
147
+ return None
148
+
149
+ image_url = f"data:image/{img_format.lower()};base64,{base64_image}"
150
+
151
+ try:
152
+ response = client.chat.completions.create(
153
+ model="llama-3.2-90b-vision-preview",
154
+ messages=[{
155
+ "role": "user",
156
+ "content": [
157
+ {"type": "text", "text": (
158
+ "As an AI radiologist, provide a detailed structured report including: "
159
+ "1. Imaging modality identification\n2. Anatomical structures visualized\n"
160
+ "3. Abnormal findings description\n4. Differential diagnoses\n"
161
+ "5. Clinical correlation recommendations"
162
+ )},
163
+ {"type": "image_url", "image_url": {"url": image_url}},
164
+ ]
165
+ }],
166
+ temperature=0.2,
167
+ max_tokens=500,
168
+ top_p=0.5
169
+ )
170
+ return response.choices[0].message.content
171
+ except Exception as e:
172
+ st.error(f"API communication error: {str(e)}")
173
+ return None
174
+
175
+ # ======================
176
+ # UI COMPONENTS
177
+ # ======================
178
+ def display_main_interface():
179
+ """Render primary application interface"""
180
+ # Encode logo image
181
+ logo_b64 = encode_logo("src/radiology.png")
182
+
183
+ # Center the logo and title using HTML and CSS
184
+ st.markdown(
185
+ f"""
186
+ <div style="text-align: center;">
187
+ <div class="logo">
188
+ <img src="data:image/png;base64,{logo_b64}" width="100">
189
+ </div>
190
+ <p class="main-title"> Radiology Analyzer</p>
191
+ <p class="sub-title">Advanced Medical Imaging Analysis</p>
192
+ </div>
193
+ """,
194
+ unsafe_allow_html=True
195
+ )
196
+
197
+ st.markdown("---")
198
+
199
+ # Action buttons
200
+ col1, col2 = st.columns([1, 1])
201
+ with col1:
202
+ if st.session_state.get('analysis_result'):
203
+ pdf_report = generate_pdf_report(st.session_state.analysis_result)
204
+ st.download_button(
205
+ label="πŸ“„ Download PDF Report",
206
+ data=pdf_report,
207
+ file_name="radiology_report.pdf",
208
+ mime="application/pdf",
209
+ use_container_width=True,
210
+ help="Download formal PDF version of the report",
211
+ key="download_pdf"
212
+ )
213
+
214
+ with col2:
215
+ if st.button("Clear Analysis πŸ—‘οΈ", use_container_width=True, help="Remove current results"):
216
+ st.session_state.pop('analysis_result')
217
+ st.rerun()
218
+
219
+ # Display analysis results
220
+ if st.session_state.get('analysis_result'):
221
+ st.markdown("### 🎯 Radiological Findings Report")
222
+ st.markdown(
223
+ f'<div class="report-container"><div class="report-text">{st.session_state.analysis_result}</div></div>',
224
+ unsafe_allow_html=True
225
+ )
226
+
227
+ def render_sidebar(client):
228
+ """Create sidebar interface elements"""
229
+ with st.sidebar:
230
+ st.divider()
231
+ st.markdown("### Diagnostic Capabilities")
232
+ st.markdown("""
233
+ - **Multi-Modality Analysis**: X-ray, MRI, CT, Ultrasound
234
+ - **Pathology Detection**: Fractures, tumors, infections
235
+ - **Comparative Analysis**: Track disease progression
236
+ - **Structured Reporting**: Standardized output format
237
+ - **Clinical Correlation**: Suggested next steps
238
+ - **Disclaimer: This service does not provide medical advice, consult a doctor for analysis and treatment
239
+ """)
240
+ st.divider()
241
+
242
+ st.subheader("Image Upload Section")
243
+ uploaded_file = st.file_uploader(
244
+ "Select Medical Image",
245
+ type=ALLOWED_FILE_TYPES,
246
+ help="Supported formats: PNG, JPG, JPEG"
247
+ )
248
+
249
+ if uploaded_file:
250
+ st.image(Image.open(uploaded_file),
251
+ caption="Uploaded Medical Image",
252
+ use_container_width=True)
253
+
254
+ if st.button("Initiate Analysis πŸ”", use_container_width=True):
255
+ with st.spinner("Analyzing image. This may take 20-30 seconds..."):
256
+ report = generate_radiology_report(uploaded_file, client)
257
+ st.session_state.analysis_result = report
258
+ st.rerun()
259
+
260
+ # ======================
261
+ # APPLICATION ENTRYPOINT
262
+ # ======================
263
+ def main():
264
+ """Primary application controller"""
265
+ configure_application()
266
+ groq_client = initialize_api_client()
267
+
268
+ display_main_interface()
269
+ render_sidebar(groq_client)
270
+
271
+ if __name__ == "__main__":
272
  main()