Meet Radadiya commited on
Commit
9e4f358
·
1 Parent(s): 9c27713

Fine Tuning Output

Browse files
Files changed (2) hide show
  1. app.py +707 -122
  2. appp.py +0 -857
app.py CHANGED
@@ -6,8 +6,15 @@ 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
@@ -23,59 +30,221 @@ 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
  """
@@ -88,25 +257,42 @@ def configure_application():
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"""
@@ -119,154 +305,553 @@ def process_image_data(uploaded_file):
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=1000,
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()
 
 
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, Image as ReportLabImage
10
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
11
+ from reportlab.lib.units import inch
12
+ from datetime import datetime
13
+ import re
14
+ from reportlab.lib import colors
15
+ import random
16
+ import streamlit.components.v1 as components
17
+ from openai import OpenAI
18
 
19
  # ======================
20
  # CONFIGURATION SETTINGS
 
30
 
31
  CSS_STYLES = """
32
  <style>
33
+ /* Main background and text colors */
34
+ .main {
35
+ background-color: #0e1117;
36
+ color: #ffffff;
 
 
 
 
37
  }
38
+ .sidebar .sidebar-content {
39
+ background-color: #1a1d24;
40
+ color: #ffffff;
 
 
41
  }
42
+
43
+ /* Custom title styling */
44
+ .main-title {
45
+ font-size: 2.8rem;
46
+ font-weight: 700;
47
+ color: #ffffff;
48
+ margin-bottom: 0.2rem;
49
+ text-align: center;
50
  }
51
+ .sub-title {
52
+ font-size: 1.5rem;
53
+ color: #9ca3af;
54
+ margin-top: 0.2rem;
55
+ text-align: center;
56
+ margin-bottom: 2rem;
57
  }
58
+
59
+ /* Button styling */
60
+ .stButton>button {
61
+ background-color: #21b9e1 !important;
62
+ color: white !important;
63
+ border-radius: 8px !important;
64
+ padding: 0.5rem 1rem !important;
65
+ border: none !important;
66
+ transition: all 0.3s ease !important;
67
  }
68
+ .stButton>button:hover {
69
+ background-color: #17a2b8 !important;
70
+ transform: translateY(-2px);
71
+ box-shadow: 0 4px 12px rgba(0, 200, 225, 0.3);
72
  }
73
+
74
+ /* Report container */
75
  .report-container {
76
+ background-color: #1a1d24;
77
+ border-radius: 10px;
78
  padding: 25px;
79
  margin-top: 20px;
80
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
81
+ border-left: 5px solid #21b9e1;
82
  }
83
  .report-text {
84
+ font-family: 'Inter', sans-serif;
85
+ font-size: 14px;
86
  line-height: 1.6;
87
+ color: #e2e8f0;
88
  }
89
+
90
+ /* File uploader */
91
+ .uploadedFile {
92
+ background-color: #1a1d24 !important;
93
+ border-radius: 10px !important;
94
+ padding: 10px !important;
95
+ border: 2px dashed #21b9e1 !important;
96
+ }
97
+
98
+ /* Sidebar items */
99
+ .sidebar-item {
100
+ padding: 10px 0;
101
+ border-bottom: 1px solid #2d3748;
102
+ }
103
+ .sidebar-title {
104
+ font-weight: bold;
105
+ color: #21b9e1;
106
+ margin-bottom: 10px;
107
+ }
108
+
109
+ /* Logo container */
110
+ .logo-container {
111
+ display: flex;
112
+ justify-content: center;
113
+ margin-bottom: 20px;
114
+ }
115
+ .logo-pulse {
116
+ width: 100px;
117
+ height: 100px;
118
+ border-radius: 50%;
119
+ animation: pulse 2s infinite;
120
+ display: flex;
121
+ justify-content: center;
122
+ align-items: center;
123
+ background-color: rgba(33, 185, 225, 0.1);
124
+ }
125
+ @keyframes pulse {
126
+ 0% {
127
+ box-shadow: 0 0 0 0 rgba(33, 185, 225, 0.4);
128
+ }
129
+ 70% {
130
+ box-shadow: 0 0 0 20px rgba(33, 185, 225, 0);
131
+ }
132
+ 100% {
133
+ box-shadow: 0 0 0 0 rgba(33, 185, 225, 0);
134
+ }
135
+ }
136
+
137
+ /* Progress bar */
138
+ .stProgress > div > div {
139
+ background-color: #21b9e1 !important;
140
+ }
141
+
142
+ /* Analysis status indicator */
143
+ .analysis-complete {
144
+ display: inline-flex;
145
+ align-items: center;
146
+ background-color: rgba(33, 225, 185, 0.2);
147
+ color: #21e1b9;
148
+ padding: 8px 16px;
149
+ border-radius: 20px;
150
+ font-weight: 600;
151
+ margin-bottom: 20px;
152
+ }
153
+ .analysis-complete svg {
154
+ margin-right: 8px;
155
+ }
156
+
157
+ /* Drop zone */
158
+ .drop-zone {
159
+ background-color: #1a1d24;
160
+ border: 2px dashed #21b9e1;
161
+ border-radius: 10px;
162
+ padding: 40px 20px;
163
+ text-align: center;
164
+ transition: all 0.3s ease;
165
+ margin-bottom: 20px;
166
+ }
167
+ .drop-zone:hover {
168
+ border-color: #17a2b8;
169
+ background-color: #242830;
170
+ }
171
+ .drop-icon {
172
+ font-size: 3rem;
173
+ color: #21b9e1;
174
+ margin-bottom: 10px;
175
+ }
176
+
177
+ /* Markdown adjustments */
178
+ .markdown-text-container p {
179
+ color: #e2e8f0 !important;
180
+ }
181
+
182
+ /* Image styling */
183
+ .stImage img {
184
+ border-radius: 10px;
185
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
186
+ border: 3px solid #1a1d24;
187
+ }
188
+
189
+ /* Card styles */
190
+ .card {
191
+ background-color: #1a1d24;
192
+ border-radius: 10px;
193
+ padding: 20px;
194
+ margin-bottom: 20px;
195
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
196
+ transition: all 0.3s ease;
197
+ }
198
+ .card:hover {
199
+ transform: translateY(-5px);
200
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
201
+ }
202
+ .card-title {
203
+ color: #21b9e1;
204
+ font-size: 1.2rem;
205
+ font-weight: 600;
206
+ margin-bottom: 10px;
207
+ }
208
+
209
+ /* Tooltip */
210
+ .tooltip {
211
+ position: relative;
212
+ display: inline-block;
213
+ cursor: pointer;
214
+ }
215
+ .tooltip .tooltiptext {
216
+ visibility: hidden;
217
+ width: 200px;
218
+ background-color: #2d3748;
219
+ color: #fff;
220
+ text-align: center;
221
+ border-radius: 6px;
222
+ padding: 10px;
223
+ position: absolute;
224
+ z-index: 1;
225
+ bottom: 125%;
226
+ left: 50%;
227
+ margin-left: -100px;
228
+ opacity: 0;
229
+ transition: opacity 0.3s;
230
+ }
231
+ .tooltip:hover .tooltiptext {
232
+ visibility: visible;
233
+ opacity: 1;
234
+ }
235
+
236
+ /* API selector styling */
237
+ .api-selector {
238
+ background-color: #1a1d24;
239
+ border-radius: 10px;
240
+ padding: 15px;
241
+ margin-bottom: 20px;
242
+ border-left: 3px solid #21b9e1;
243
+ }
244
+ .api-selector-title {
245
+ color: #21b9e1;
246
+ font-weight: bold;
247
+ margin-bottom: 10px;
248
  }
249
  </style>
250
  """
 
257
  st.set_page_config(**PAGE_CONFIG)
258
  st.markdown(CSS_STYLES, unsafe_allow_html=True)
259
 
260
+ def initialize_groq_client():
261
  """Create and validate Groq API client"""
262
  load_dotenv()
263
  api_key = os.getenv("GROQ_API_KEY")
 
264
  if not api_key:
265
+ api_key = "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" # Fallback key
266
+
267
+ if not api_key:
268
+ st.error("Groq API key not found. Please provide an API key.")
269
+ return None
270
 
271
  return Groq(api_key=api_key)
272
 
273
+ def initialize_openai_client():
274
+ """Create and validate OpenAI API client"""
275
+ load_dotenv()
276
+ api_key = os.getenv("OPENAI_API_KEY")
277
+
278
+ # Check if API key is in session state (from user input)
279
+ if 'openai_api_key' in st.session_state and st.session_state.openai_api_key:
280
+ api_key = st.session_state.openai_api_key
281
+
282
+ if not api_key:
283
+ # Return None if no API key - we'll handle this in the UI
284
+ return None
285
+
286
+ return OpenAI(api_key=api_key)
287
+
288
  def encode_logo(image_path):
289
  """Encode logo image to base64"""
290
  try:
291
  with open(image_path, "rb") as img_file:
292
  return base64.b64encode(img_file.read()).decode("utf-8")
293
  except FileNotFoundError:
294
+ # Return a placeholder image (blue medical technology icon) encoded as base64
295
+ return "iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFLUlEQVR4nO2cfYhUVRTGf+uqlWmpZX6kaZpppX1YWpllEQVFEQVBEVFUpBVF0QdFRlEUVERBRNAfkRVZ9GVFpaXlRwpqlGlqpmZqaZa67q7r6qrr9kf30dxh9u7MnTvz5t6Z+8DLMDvv3nPOuWfOPe++e+6CEEIIIYQQ1cCVwBpgl8/2AnCa7UBjZDjwIbDXR4FUsjbg99zV7AZGpBJoZlwDfK+EeOAHYJTNIPuVkMAca2PghBs/AEcZMpdKSGD20IKJQCuvQSoYLSWjpJRiYzLCHgCeB5YDzTZicYGrgF+V7T10AG+kGcRLwPGWYnGFicA/SmgfeoAZaQ6yFhjmMxzXGAV8q6T24WdggB/DTcDFluJxhYHAFiW1DyuDGG9OIIyrgJ+U2N+4z69xNzxcXZ1L3FdDqoVoXucjgdXkQSz6nKzw5BkFbK/xAV3jUsBpoUEJKYmNQD0Pnw+xtgRCeNdg6WO7wvVuVEkMtiWE8K5SrZ1L+pwPAusV+n7Zk8ZHv35FaWiUEOPcC3xRo8JbV41i+3K8RgVX8aysMbGVnWHVKLYvR2tEcE9Wq9iaiMYaEdzjNSq2JqK+RgR3by2KrQYmdG0MCu6yahdbja16wDe1KLYaW/XA6BoUWxPRWWWCuxw4XKNiq5NVD1QjP/oVW5PCrFbmVrW27Krpz8AVZZrWrBaRvQcsAb4CfitKbEtpgbWxAzg3zKSSEyLBjXIVtzf3vLZYqaUQhRQGRWoTmCcHjvDpX41UKcVGZLqPQPdoYkqTxQEW+DTWC7yfVBDWspmQkFHmlWXMfkwRInoMeKyAn+F6Zw2E4e1c9F7KPsMxdxdwfJQVNTfwU9U7bGBfcYLXmlKp5xUL4WUt/Jvgxp5QUaMd3MbA58XMRlqgBxiYVAw3Ay8m8PFNQC+EQVWXlD8XVVIQVwCtZQgiHwaYMpwwiFuThpn+ZFFBzA7gIzCVnLRNAjodF87GIhdPDzArJmxsJxxicYZvFrnwfUBnwrDG5ApjH5cQHnVFhYsB1gEdMWGjtwwXeZnWZJ1fZtjtucHSUuSY/WWGpXLREcWQeBjYFHGRzeUI4nKgiPWAKbmw0NJC2Hhc6Fx6G3SLe76Wc7s17j7BTOA94IKIY7g8I+PrDfZvZWX/gojj/QtMB74vOcwW/qPQnUZ23FHGZNlX5KLFQgMjciOH+W9nngG25MQwLpXvxcDCnO97YubXDGqLEUZTbsvKA8BDwNWGPvuNsTKKKXEzdKNwbg6mxMnlGcZ7iu1BYOmJy4U1phZpgEJhw4tEGZq8DQR2W57QdUPnTIrA1nZaGLEdJLt3p/uD2aCHo+5GFKt4zYWnxs2Qgej1cVZrJJIcYnoX+MiXdW/ywOYtztEp+Jhjd//5YMaGJ5iHrq0GVtB7X5ZY88p2gTBIlRDjiQ3AAVH2K0oIZTG5hP7ESCWkTzUoFJ3AEVlHYotJWQx0TYwNSoj5w2chKP3DibadnWk7SMepVkKMJ9qBWPocB2QdgG1eL0r7gQxwmBJSXvOeRJaXUcY/sMWExm4lxHhiPtBWRuhryjXeRQkJ1MWfVsb2HVZfpjrFbZ1aAMzJNaMLn3+35LbXwXJZR0IIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYRD/A9AH1PHpL17/AAAAABJRU5ErkJggg=="
296
 
297
  def process_image_data(uploaded_file):
298
  """Convert image to base64 encoded string"""
 
305
  st.error(f"Image processing error: {str(e)}")
306
  return None, None
307
 
308
+ def generate_pdf_report(report_text, uploaded_file):
309
+ """Generate professionally formatted PDF report with bold headers."""
310
  buffer = io.BytesIO()
311
+ doc = SimpleDocTemplate(buffer, pagesize=letter,
312
+ rightMargin=72, leftMargin=72,
313
+ topMargin=72, bottomMargin=72)
314
+
315
+ # Create custom styles for different parts of the report
316
  styles = getSampleStyleSheet()
317
+
318
+ # Custom styles for better formatting
319
+ title_style = ParagraphStyle(
320
+ 'ReportTitle',
321
+ parent=styles['Title'],
322
+ fontSize=16,
323
+ alignment=1, # Center aligned
324
+ spaceAfter=12
325
+ )
326
+
327
+ header_style = ParagraphStyle(
328
+ 'SectionHeader',
329
+ parent=styles['Heading2'],
330
+ fontSize=12,
331
+ fontName='Helvetica-Bold',
332
+ textColor=colors.black,
333
+ spaceBefore=12,
334
+ spaceAfter=6
335
+ )
336
+
337
+ normal_style = ParagraphStyle(
338
+ 'NormalText',
339
+ parent=styles['BodyText'],
340
+ fontSize=11,
341
+ leading=14,
342
+ spaceAfter=8
343
+ )
344
+
345
+ abnormal_style = ParagraphStyle(
346
+ 'AbnormalText',
347
+ parent=styles['BodyText'],
348
+ fontSize=11,
349
+ leading=14,
350
+ textColor=colors.red,
351
+ backColor=colors.lightgrey,
352
+ borderPadding=5,
353
+ spaceAfter=8
354
+ )
355
+
356
+ footer_style = ParagraphStyle(
357
+ 'FooterText',
358
+ parent=styles['Italic'],
359
+ fontSize=9,
360
+ alignment=1 # Center aligned
361
+ )
362
+
363
+ # Begin building the report
364
  story = []
365
 
366
+ # Hospital/Institution Header
367
+ header_text = "RADIOLOGY DEPARTMENT"
368
+ header = Paragraph(header_text, title_style)
369
+ story.append(header)
370
+
371
+ # Report Title
372
+ report_title = "RADIOLOGICAL EXAMINATION REPORT"
373
+ title = Paragraph(report_title, title_style)
374
  story.append(title)
375
+
376
+ # Add date and report ID
377
+ date_text = f"Date: {datetime.now().strftime('%B %d, %Y')}"
378
+ report_id = f"Report ID: RAD-{datetime.now().strftime('%Y%m%d')}-{random.randint(1000, 9999)}"
379
+ date_para = Paragraph(date_text, normal_style)
380
+ id_para = Paragraph(report_id, normal_style)
381
+ story.append(date_para)
382
+ story.append(id_para)
383
  story.append(Spacer(1, 12))
384
 
385
+ # Add the image to the PDF
386
+ if uploaded_file:
387
+ try:
388
+ uploaded_file.seek(0) # Reset file pointer to beginning
389
+ pil_image = Image.open(uploaded_file)
390
+ img_width = 5 * inch
391
+ aspect = float(pil_image.height) / float(pil_image.width)
392
+ img_height = img_width * aspect
393
+
394
+ img_temp = io.BytesIO()
395
+ pil_image.save(img_temp, format=pil_image.format if pil_image.format else 'JPEG')
396
+ img_temp.seek(0)
397
+
398
+ img = ReportLabImage(img_temp, width=img_width, height=img_height)
399
+ story.append(img)
400
+ story.append(Spacer(1, 12))
401
+
402
+ # Add image caption
403
+ caption = Paragraph("Figure 1: Radiological Image for Analysis", normal_style)
404
+ story.append(caption)
405
+ story.append(Spacer(1, 12))
406
+
407
+ except Exception as e:
408
+ error_text = Paragraph(f"Image processing error: {str(e)}", normal_style)
409
+ story.append(error_text)
410
+ story.append(Spacer(1, 12))
411
 
412
+ # Clean the report text (remove markdown-style formatting and unwanted characters)
413
+ cleaned_text = report_text.replace('**', '').replace('##', '').replace('*', '-')
414
+
415
+ # Define section headers to identify
416
+ section_headers = [
417
+ "DIAGNOSIS",
418
+ "ETIOLOGY",
419
+ "RISK FACTORS",
420
+ "PATHOPHYSIOLOGY",
421
+ "CLINICAL FEATURES",
422
+ "SIGNS AND SYMPTOMS",
423
+ "INVESTIGATIONS",
424
+ "MANAGEMENT",
425
+ "INITIAL STABILIZATION",
426
+ "MEDICAL MANAGEMENT",
427
+ "SURGICAL MANAGEMENT",
428
+ "PROGNOSIS"
429
+ ]
430
+
431
+ # Split into lines for more precise processing
432
+ lines = cleaned_text.split('\n')
433
+ current_section = ""
434
+ section_content = ""
435
+
436
+ for i, line in enumerate(lines):
437
+ line = line.strip()
438
+ if not line:
439
+ continue
440
+
441
+ # Remove any "Step X:" prefixes
442
+ line = re.sub(r'^Step \d+:\s*', '', line)
443
+
444
+ # Check if this is a section header
445
+ is_header = False
446
+ for header in section_headers:
447
+ if line.upper().startswith(header) or line.upper() == header + ":":
448
+ is_header = True
449
+ break
450
+
451
+ # Also check if it's a short line ending with a colon (likely a header)
452
+ if not is_header and len(line) < 60 and line.endswith(':'):
453
+ is_header = True
454
+
455
+ # If we found a header
456
+ if is_header:
457
+ # First add any accumulated content from previous section
458
+ if section_content.strip():
459
+ # Check for severe abnormalities to highlight
460
+ severe_abnormal_keywords = [
461
+ 'severe', 'critical', 'urgent', 'emergency', 'life-threatening',
462
+ 'malignant', 'neoplasm', 'carcinoma', 'metastasis', 'hemorrhage',
463
+ 'fracture', 'rupture', 'perforation',
464
+ ]
465
+
466
+ has_severe_issue = any(keyword in section_content.lower() for keyword in severe_abnormal_keywords)
467
+
468
+ if current_section.upper().startswith("DIAGNOSIS") or current_section.upper().startswith("ABNORMAL"):
469
+ # This is a diagnosis section - highlight abnormalities
470
+ p = Paragraph(section_content, abnormal_style if has_severe_issue else normal_style)
471
+ else:
472
+ p = Paragraph(section_content, normal_style)
473
+
474
+ story.append(p)
475
+ story.append(Spacer(1, 6))
476
+ section_content = ""
477
+
478
+ # Add the new section header - remove any trailing colon for cleaner look
479
+ clean_header = line.strip()
480
+ if clean_header.endswith(':'):
481
+ clean_header = clean_header[:-1]
482
+
483
+ current_section = clean_header
484
+ p = Paragraph(f"<b>{clean_header}</b>", header_style) # Bold the header
485
+ story.append(p)
486
+
487
+ else:
488
+ # This is content - append to the current section content
489
+ if section_content:
490
+ section_content += "<br/>" + line
491
+ else:
492
+ section_content = line
493
+
494
+ # Add any remaining content
495
+ if section_content.strip():
496
+ p = Paragraph(section_content, normal_style)
497
+ story.append(p)
498
+
499
+ # Add conclusion if not present
500
+ if not any("PROGNOSIS" in line.upper() for line in lines):
501
+ conclusion_header = Paragraph("<b>PROGNOSIS</b>", header_style)
502
+ story.append(conclusion_header)
503
+ story.append(Spacer(1, 6))
504
+
505
+ conclusion_text = "Prognosis varies based on the extent and location of findings. Clinical correlation with the patient's symptoms and medical history is recommended."
506
+ conclusion_para = Paragraph(conclusion_text, normal_style)
507
+ story.append(conclusion_para)
508
+
509
+ # Add footer with disclaimer
510
+ story.append(Spacer(1, 24))
511
+ disclaimer = "This report was generated with AI assistance and should be reviewed by a qualified healthcare professional."
512
+ footer = Paragraph(disclaimer, footer_style)
513
+ story.append(footer)
514
+
515
+ # Build PDF
516
  doc.build(story)
517
  buffer.seek(0)
518
  return buffer
519
 
520
+
521
+
522
+
523
+
524
+
525
+ def generate_radiology_report_groq(uploaded_file, client):
526
+ """Generate AI-powered radiology analysis using Groq API"""
527
  base64_image, img_format = process_image_data(uploaded_file)
528
 
529
  if not base64_image:
530
  return None
531
+
532
  image_url = f"data:image/{img_format.lower()};base64,{base64_image}"
533
+
534
+ try:
535
+ with st.spinner("Analyzing image with Groq..."):
536
+ # Add progress bar for visual feedback
537
+ progress_bar = st.progress(0)
538
+ for i in range(100):
539
+ # Update progress bar
540
+ progress_bar.progress(i + 1)
541
+ import time
542
+ time.sleep(0.025) # Simulate processing time
543
+
544
+ # Updated prompt to request the detailed, structured format
545
+ response = client.chat.completions.create(
546
+ model="llama-3.2-90b-vision-preview", # Use Groq's model
547
+ messages=[{
548
+ "role": "user",
549
+ "content": [
550
+ {"type": "text", "text": (
551
+ """As a radiologist, analyze the following MRI report and provide a comprehensive report structured as follows:
552
+
553
+ 1. **DIAGNOSIS**: Clearly state the primary diagnosis, including dimensions where applicable (e.g., if a tumor is present). Use specific anatomical terms relevant to the body part being examined.
554
+
555
+ 2. **FINDINGS**:
556
+ - Provide detailed observations from the MRI report, including:
557
+ - The size, shape, and location of any lesions or abnormalities if applicable.
558
+ - Description of the surrounding tissues and structures if applicable.
559
+ - Any noted changes in signal intensity on various sequences (e.g., T1W, T2W, FLAIR) if applicable.
560
+ - Mention of any associated findings, such as edema, mass effect, or midline shift if applicble.
561
+ - Specific comments on vascular structures, if applicable.
562
 
563
+ 3. **PATHOPHYSIOLOGY**: Briefly explain the disease mechanism related to the diagnosis, focusing on how it affects the specific body part.
564
+
565
+ 4. **CLINICAL FEATURES**: Provide an overview of typical clinical presentations associated with this diagnosis, emphasizing symptoms that may arise from abnormalities in the specified anatomical area.
566
+
567
+ 5. **SIGNS AND SYMPTOMS**: List common signs and symptoms relevant to the findings in the MRI report. Tailor this section to align with the specific anatomy being assessed.
568
+
569
+ 6. **INVESTIGATIONS**: Mention diagnostic tests typically used for confirmation of the diagnosis, including imaging studies or laboratory tests pertinent to the body part.
570
+
571
+ 7. **MANAGEMENT**: Outline the management plans in three parts:
572
+ - Initial Stabilization: Describe immediate steps for patient care.
573
+ - Medical Management: Outline pharmacological treatments and monitoring.
574
+ - Surgical Management (if applicable): Discuss any surgical interventions specific to the diagnosis and body part.
575
+
576
+ 8. **PROGNOSIS**: Describe expected outcomes and factors that may affect prognosis based on the diagnosis. Include considerations specific to the anatomical region and associated complications.
577
+
578
+ Please ensure to focus on the following findings from the report:
579
+ - Mention specific abnormalities based on the region (e.g., "T2/FLAIR hyperintensities in the right fronto-parietal region" for brain MRI).
580
+ - Highlight any significant lesions or deviations from the norm.
581
+ - Include any other abnormal findings noted in the report that are relevant to the specific anatomy.
582
+
583
+ Format each section with appropriate headings and use bullet points for lists. Base your analysis on the provided MRI report details."""
584
+ )},
585
+ {"type": "image_url", "image_url": {"url": image_url}},
586
+ ]
587
+ }],
588
+ temperature=0.1,
589
+ max_tokens=3000, # Increased token limit for more detailed response
590
+ top_p=0.3
591
+ )
592
+ return response.choices[0].message.content
593
+ except Exception as e:
594
+ st.error(f"Groq API error: {str(e)}")
595
+ return None
596
+
597
+ def generate_radiology_report_openai(uploaded_file, client):
598
+ """Generate AI-powered radiology analysis using OpenAI API"""
599
+ base64_image, img_format = process_image_data(uploaded_file)
600
+
601
+ if not base64_image:
602
+ return None
603
+
604
  try:
605
+ with st.spinner("Analyzing image with OpenAI..."):
606
+ # Add progress bar for visual feedback
607
+ progress_bar = st.progress(0)
608
+ for i in range(100):
609
+ # Update progress bar
610
+ progress_bar.progress(i + 1)
611
+ import time
612
+ time.sleep(0.025) # Simulate processing time
613
+
614
+ # Updated prompt to request the detailed, structured format
615
+ response = client.chat.completions.create(
616
+ model="gpt-4-vision-preview", # Use OpenAI's vision model
617
+ messages=[
618
+ {
619
+ "role": "user",
620
+ "content": [
621
+ {
622
+ "type": "text",
623
+ "text": (
624
+ "As a radiologist, analyze this medical image and provide a comprehensive report with the following structure:\n\n"
625
+ "1. DIAGNOSIS: Clear statement of primary diagnosis\n\n"
626
+ "2. ETIOLOGY: List possible causes of the identified condition\n\n"
627
+ "3. RISK FACTORS: Bullet-point list of risk factors associated with the condition\n\n"
628
+ "4. PATHOPHYSIOLOGY: Brief explanation of the disease mechanism\n\n"
629
+ "5. CLINICAL FEATURES: Overview of typical clinical presentation\n\n"
630
+ "6. SIGNS AND SYMPTOMS: Bullet-point list of common signs and symptoms\n\n"
631
+ "7. INVESTIGATIONS: Diagnostic tests typically used for confirmation\n\n"
632
+ "8. MANAGEMENT: Divided into three parts:\n"
633
+ " - Initial Stabilization\n"
634
+ " - Medical Management\n"
635
+ " - Surgical Management (if applicable)\n\n"
636
+ "9. PROGNOSIS: Expected outcomes and factors affecting prognosis\n\n"
637
+ "Format each section with appropriate headings and use bullet points for lists. If you identify abnormal findings, make sure to highlight these clearly."
638
+ )
639
+ },
640
+ {
641
+ "type": "image_url",
642
+ "image_url": {
643
+ "url": f"data:image/{img_format.lower()};base64,{base64_image}"
644
+ }
645
+ }
646
+ ]
647
+ }
648
+ ],
649
+ max_tokens=3000 # Increased token limit for more detailed response
650
+ )
651
+ return response.choices[0].message.content
652
  except Exception as e:
653
+ st.error(f"OpenAI API error: {str(e)}")
654
  return None
655
 
656
+ def generate_radiology_report(uploaded_file, api_choice='groq'):
657
+ """Generate report using the selected API"""
658
+ if api_choice == 'groq':
659
+ client = initialize_groq_client()
660
+ if client:
661
+ return generate_radiology_report_groq(uploaded_file, client)
662
+ else:
663
+ st.error("Failed to initialize Groq client. Please check your API key.")
664
+ return None
665
+ else: # OpenAI
666
+ client = initialize_openai_client()
667
+ if client:
668
+ return generate_radiology_report_openai(uploaded_file, client)
669
+ else:
670
+ st.error("OpenAI API key is required. Please provide your API key.")
671
+ return None
672
+
673
  # ======================
674
  # UI COMPONENTS
675
  # ======================
676
+ def display_animated_logo():
677
+ """Display an animated medical logo"""
678
+ logo_b64 = encode_logo("MediSight\src\Round_image_depicting_a_futuristic_medical_image_a-1742282117033-photoaidcom-cropped.png")
 
679
 
680
+ # If logo file doesn't exist, use the placeholder from encode_logo
681
  st.markdown(
682
  f"""
683
+ <div class="logo-container">
684
+ <div class="logo-pulse">
685
+ <img src="data:image/png;base64,{logo_b64}" width="200">
686
  </div>
 
 
687
  </div>
688
  """,
689
  unsafe_allow_html=True
690
  )
 
 
691
 
692
+ def display_main_interface():
693
+ """Render primary application interface"""
694
+ # Display animated logo and titles
695
+ display_animated_logo()
696
+
697
+ st.markdown('<h1 class="main-title">Radiology Analyzer</h1>', unsafe_allow_html=True)
698
+ st.markdown('<p class="sub-title">Advanced Medical Imaging Analysis</p>', unsafe_allow_html=True)
699
+
700
  # Action buttons
701
+ if st.session_state.get('analysis_result'):
702
+ st.markdown(
703
+ """
704
+ <div class="analysis-complete">
705
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
706
+ <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
707
+ </svg>
708
+ Analysis Complete
709
+ </div>
710
+ """,
711
+ unsafe_allow_html=True
712
+ )
713
+
714
+ col1, col2 = st.columns([1, 1])
715
+ with col1:
716
+ pdf_report = generate_pdf_report(st.session_state.analysis_result, st.session_state.uploaded_file)
717
  st.download_button(
718
  label="📄 Download PDF Report",
719
  data=pdf_report,
720
  file_name="radiology_report.pdf",
721
  mime="application/pdf",
722
  use_container_width=True,
723
+ help="Download formal PDF version of the report"
 
724
  )
725
 
726
+ with col2:
727
+ if st.button("Clear Analysis 🗑️", use_container_width=True, help="Remove current results"):
728
+ st.session_state.pop('analysis_result', None)
729
+ st.session_state.pop('uploaded_file', None)
730
+ st.rerun()
731
 
732
+ # Display analysis results in a styled container
 
733
  st.markdown("### 🎯 Radiological Findings Report")
734
  st.markdown(
735
  f'<div class="report-container"><div class="report-text">{st.session_state.analysis_result}</div></div>',
736
  unsafe_allow_html=True
737
  )
738
+ else:
739
+ # Show a centered placeholder message
740
+ st.markdown(
741
+ """
742
+ <div style="text-align: center; margin-top: 50px; color: #9ca3af; padding: 100px 0;">
743
+ <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="currentColor" viewBox="0 0 16 16" style="margin-bottom: 20px;">
744
+ <path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0ZM1.5 8a6.5 6.5 0 1 1 13 0 6.5 6.5 0 0 1-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"/>
745
+ </svg>
746
+ <p style="font-size: 1.2rem;">Upload a medical image to begin analysis</p>
747
+ </div>
748
+ """,
749
+ unsafe_allow_html=True
750
+ )
751
 
752
+ def render_sidebar():
753
  """Create sidebar interface elements"""
754
  with st.sidebar:
755
+ st.markdown('<div class="sidebar-item">', unsafe_allow_html=True)
756
+ st.markdown('<div class="sidebar-title">Diagnostic Capabilities</div>', unsafe_allow_html=True)
757
  st.markdown("""
758
+ - **Multi-Modality Analysis:** X-ray, MRI, CT, Ultrasound
759
+ - **Pathology Detection:** Fractures, tumors, infections
760
+ - **Comparative Analysis:** Track disease progression
761
+ - **Structured Reporting:** Standardized output format
762
+ - **Clinical Correlation:** Suggested next steps
 
763
  """)
764
+ st.markdown("""
765
+ <div class="tooltip">
766
+ <strong>Disclaimer:</strong> This service does not provide medical advice.
767
+ <span class="tooltiptext">Always consult with a qualified healthcare professional for diagnosis and treatment.</span>
768
+ </div>
769
+ """, unsafe_allow_html=True)
770
+ st.markdown('</div>', unsafe_allow_html=True)
771
+
772
+ # API Selection
773
+ st.markdown('<div class="sidebar-item">', unsafe_allow_html=True)
774
+ st.markdown('<div class="sidebar-title">API Configuration</div>', unsafe_allow_html=True)
775
+
776
+ # API provider selection
777
+ api_provider = st.radio(
778
+ "Select AI Provider",
779
+ options=["Groq"],
780
+ index=0, # Default to Groq
781
+ )
782
+
783
+ # If OpenAI is selected, show API key input
784
+ # if api_provider == "OpenAI":
785
+ # openai_key = st.text_input(
786
+ # "OpenAI API Key",
787
+ # type="password",
788
+ # help="Enter your OpenAI API key",
789
+ # value=st.session_state.get('openai_api_key', '')
790
+ # )
791
+ # if openai_key:
792
+ # st.session_state.openai_api_key = openai_key
793
+
794
+ st.markdown('</div>', unsafe_allow_html=True)
795
+
796
+ # Image Upload Section
797
+ st.markdown('<div class="sidebar-item">', unsafe_allow_html=True)
798
+ st.markdown('<div class="sidebar-title">Image Upload Section</div>', unsafe_allow_html=True)
799
+
800
+ # Create a styled drop zone
801
+ st.markdown(
802
+ """
803
+ <div class="drop-zone">
804
+ <div class="drop-icon">📤</div>
805
+ <p>Drag and drop file here</p>
806
+ <p style="font-size: 0.8rem; color: #9ca3af;">or use the uploader below</p>
807
+ </div>
808
+ """,
809
+ unsafe_allow_html=True
810
+ )
811
 
 
812
  uploaded_file = st.file_uploader(
813
  "Select Medical Image",
814
  type=ALLOWED_FILE_TYPES,
815
+ label_visibility="collapsed",
816
  help="Supported formats: PNG, JPG, JPEG"
817
  )
818
 
819
  if uploaded_file:
820
+ st.session_state.uploaded_file = uploaded_file # Store uploaded file in session state
821
+
822
+ # Display image with a styled container
823
+ st.markdown('<div class="card">', unsafe_allow_html=True)
824
  st.image(Image.open(uploaded_file),
825
  caption="Uploaded Medical Image",
826
  use_container_width=True)
827
+ st.markdown('</div>', unsafe_allow_html=True)
828
 
829
+ # Styled analysis button
830
+ if st.button("▶️ Initiate Analysis", use_container_width=True):
831
+ with st.spinner("Processing image..."):
832
+ # Use the selected API provider
833
+ api_choice = 'groq' if api_provider == "Groq" else 'openai'
834
+ report = generate_radiology_report(uploaded_file, api_choice)
835
+
836
+ if report:
837
+ st.session_state.analysis_result = report
838
+ st.rerun()
839
+ st.markdown('</div>', unsafe_allow_html=True)
840
 
841
  # ======================
842
  # APPLICATION ENTRYPOINT
843
  # ======================
844
  def main():
845
  """Primary application controller"""
846
+ # Check if dark mode is in session state, default to true
847
+ if 'dark_mode' not in st.session_state:
848
+ st.session_state.dark_mode = True
849
+
850
  configure_application()
 
851
 
852
+ render_sidebar()
853
  display_main_interface()
 
854
 
855
  if __name__ == "__main__":
856
+ main()
857
+
appp.py DELETED
@@ -1,857 +0,0 @@
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, Image as ReportLabImage
10
- from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
11
- from reportlab.lib.units import inch
12
- from datetime import datetime
13
- import re
14
- from reportlab.lib import colors
15
- import random
16
- import streamlit.components.v1 as components
17
- from openai import OpenAI
18
-
19
- # ======================
20
- # CONFIGURATION SETTINGS
21
- # ======================
22
- PAGE_CONFIG = {
23
- "page_title": "Radiology Analyzer",
24
- "page_icon": "🩺",
25
- "layout": "wide",
26
- "initial_sidebar_state": "expanded"
27
- }
28
-
29
- ALLOWED_FILE_TYPES = ['png', 'jpg', 'jpeg']
30
-
31
- CSS_STYLES = """
32
- <style>
33
- /* Main background and text colors */
34
- .main {
35
- background-color: #0e1117;
36
- color: #ffffff;
37
- }
38
- .sidebar .sidebar-content {
39
- background-color: #1a1d24;
40
- color: #ffffff;
41
- }
42
-
43
- /* Custom title styling */
44
- .main-title {
45
- font-size: 2.8rem;
46
- font-weight: 700;
47
- color: #ffffff;
48
- margin-bottom: 0.2rem;
49
- text-align: center;
50
- }
51
- .sub-title {
52
- font-size: 1.5rem;
53
- color: #9ca3af;
54
- margin-top: 0.2rem;
55
- text-align: center;
56
- margin-bottom: 2rem;
57
- }
58
-
59
- /* Button styling */
60
- .stButton>button {
61
- background-color: #21b9e1 !important;
62
- color: white !important;
63
- border-radius: 8px !important;
64
- padding: 0.5rem 1rem !important;
65
- border: none !important;
66
- transition: all 0.3s ease !important;
67
- }
68
- .stButton>button:hover {
69
- background-color: #17a2b8 !important;
70
- transform: translateY(-2px);
71
- box-shadow: 0 4px 12px rgba(0, 200, 225, 0.3);
72
- }
73
-
74
- /* Report container */
75
- .report-container {
76
- background-color: #1a1d24;
77
- border-radius: 10px;
78
- padding: 25px;
79
- margin-top: 20px;
80
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
81
- border-left: 5px solid #21b9e1;
82
- }
83
- .report-text {
84
- font-family: 'Inter', sans-serif;
85
- font-size: 14px;
86
- line-height: 1.6;
87
- color: #e2e8f0;
88
- }
89
-
90
- /* File uploader */
91
- .uploadedFile {
92
- background-color: #1a1d24 !important;
93
- border-radius: 10px !important;
94
- padding: 10px !important;
95
- border: 2px dashed #21b9e1 !important;
96
- }
97
-
98
- /* Sidebar items */
99
- .sidebar-item {
100
- padding: 10px 0;
101
- border-bottom: 1px solid #2d3748;
102
- }
103
- .sidebar-title {
104
- font-weight: bold;
105
- color: #21b9e1;
106
- margin-bottom: 10px;
107
- }
108
-
109
- /* Logo container */
110
- .logo-container {
111
- display: flex;
112
- justify-content: center;
113
- margin-bottom: 20px;
114
- }
115
- .logo-pulse {
116
- width: 100px;
117
- height: 100px;
118
- border-radius: 50%;
119
- animation: pulse 2s infinite;
120
- display: flex;
121
- justify-content: center;
122
- align-items: center;
123
- background-color: rgba(33, 185, 225, 0.1);
124
- }
125
- @keyframes pulse {
126
- 0% {
127
- box-shadow: 0 0 0 0 rgba(33, 185, 225, 0.4);
128
- }
129
- 70% {
130
- box-shadow: 0 0 0 20px rgba(33, 185, 225, 0);
131
- }
132
- 100% {
133
- box-shadow: 0 0 0 0 rgba(33, 185, 225, 0);
134
- }
135
- }
136
-
137
- /* Progress bar */
138
- .stProgress > div > div {
139
- background-color: #21b9e1 !important;
140
- }
141
-
142
- /* Analysis status indicator */
143
- .analysis-complete {
144
- display: inline-flex;
145
- align-items: center;
146
- background-color: rgba(33, 225, 185, 0.2);
147
- color: #21e1b9;
148
- padding: 8px 16px;
149
- border-radius: 20px;
150
- font-weight: 600;
151
- margin-bottom: 20px;
152
- }
153
- .analysis-complete svg {
154
- margin-right: 8px;
155
- }
156
-
157
- /* Drop zone */
158
- .drop-zone {
159
- background-color: #1a1d24;
160
- border: 2px dashed #21b9e1;
161
- border-radius: 10px;
162
- padding: 40px 20px;
163
- text-align: center;
164
- transition: all 0.3s ease;
165
- margin-bottom: 20px;
166
- }
167
- .drop-zone:hover {
168
- border-color: #17a2b8;
169
- background-color: #242830;
170
- }
171
- .drop-icon {
172
- font-size: 3rem;
173
- color: #21b9e1;
174
- margin-bottom: 10px;
175
- }
176
-
177
- /* Markdown adjustments */
178
- .markdown-text-container p {
179
- color: #e2e8f0 !important;
180
- }
181
-
182
- /* Image styling */
183
- .stImage img {
184
- border-radius: 10px;
185
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
186
- border: 3px solid #1a1d24;
187
- }
188
-
189
- /* Card styles */
190
- .card {
191
- background-color: #1a1d24;
192
- border-radius: 10px;
193
- padding: 20px;
194
- margin-bottom: 20px;
195
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
196
- transition: all 0.3s ease;
197
- }
198
- .card:hover {
199
- transform: translateY(-5px);
200
- box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
201
- }
202
- .card-title {
203
- color: #21b9e1;
204
- font-size: 1.2rem;
205
- font-weight: 600;
206
- margin-bottom: 10px;
207
- }
208
-
209
- /* Tooltip */
210
- .tooltip {
211
- position: relative;
212
- display: inline-block;
213
- cursor: pointer;
214
- }
215
- .tooltip .tooltiptext {
216
- visibility: hidden;
217
- width: 200px;
218
- background-color: #2d3748;
219
- color: #fff;
220
- text-align: center;
221
- border-radius: 6px;
222
- padding: 10px;
223
- position: absolute;
224
- z-index: 1;
225
- bottom: 125%;
226
- left: 50%;
227
- margin-left: -100px;
228
- opacity: 0;
229
- transition: opacity 0.3s;
230
- }
231
- .tooltip:hover .tooltiptext {
232
- visibility: visible;
233
- opacity: 1;
234
- }
235
-
236
- /* API selector styling */
237
- .api-selector {
238
- background-color: #1a1d24;
239
- border-radius: 10px;
240
- padding: 15px;
241
- margin-bottom: 20px;
242
- border-left: 3px solid #21b9e1;
243
- }
244
- .api-selector-title {
245
- color: #21b9e1;
246
- font-weight: bold;
247
- margin-bottom: 10px;
248
- }
249
- </style>
250
- """
251
-
252
- # ======================
253
- # CORE FUNCTIONS
254
- # ======================
255
- def configure_application():
256
- """Initialize application settings and styling"""
257
- st.set_page_config(**PAGE_CONFIG)
258
- st.markdown(CSS_STYLES, unsafe_allow_html=True)
259
-
260
- def initialize_groq_client():
261
- """Create and validate Groq API client"""
262
- load_dotenv()
263
- api_key = os.getenv("GROQ_API_KEY")
264
- if not api_key:
265
- api_key = "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" # Fallback key
266
-
267
- if not api_key:
268
- st.error("Groq API key not found. Please provide an API key.")
269
- return None
270
-
271
- return Groq(api_key=api_key)
272
-
273
- def initialize_openai_client():
274
- """Create and validate OpenAI API client"""
275
- load_dotenv()
276
- api_key = os.getenv("OPENAI_API_KEY")
277
-
278
- # Check if API key is in session state (from user input)
279
- if 'openai_api_key' in st.session_state and st.session_state.openai_api_key:
280
- api_key = st.session_state.openai_api_key
281
-
282
- if not api_key:
283
- # Return None if no API key - we'll handle this in the UI
284
- return None
285
-
286
- return OpenAI(api_key=api_key)
287
-
288
- def encode_logo(image_path):
289
- """Encode logo image to base64"""
290
- try:
291
- with open(image_path, "rb") as img_file:
292
- return base64.b64encode(img_file.read()).decode("utf-8")
293
- except FileNotFoundError:
294
- # Return a placeholder image (blue medical technology icon) encoded as base64
295
- return "iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFLUlEQVR4nO2cfYhUVRTGf+uqlWmpZX6kaZpppX1YWpllEQVFEQVBEVFUpBVF0QdFRlEUVERBRNAfkRVZ9GVFpaXlRwpqlGlqpmZqaZa67q7r6qrr9kf30dxh9u7MnTvz5t6Z+8DLMDvv3nPOuWfOPe++e+6CEEIIIYQQ1cCVwBpgl8/2AnCa7UBjZDjwIbDXR4FUsjbg99zV7AZGpBJoZlwDfK+EeOAHYJTNIPuVkMAca2PghBs/AEcZMpdKSGD20IKJQCuvQSoYLSWjpJRiYzLCHgCeB5YDzTZicYGrgF+V7T10AG+kGcRLwPGWYnGFicA/SmgfeoAZaQ6yFhjmMxzXGAV8q6T24WdggB/DTcDFluJxhYHAFiW1DyuDGG9OIIyrgJ+U2N+4z69xNzxcXZ1L3FdDqoVoXucjgdXkQSz6nKzw5BkFbK/xAV3jUsBpoUEJKYmNQD0Pnw+xtgRCeNdg6WO7wvVuVEkMtiWE8K5SrZ1L+pwPAusV+n7Zk8ZHv35FaWiUEOPcC3xRo8JbV41i+3K8RgVX8aysMbGVnWHVKLYvR2tEcE9Wq9iaiMYaEdzjNSq2JqK+RgR3by2KrQYmdG0MCu6yahdbja16wDe1KLYaW/XA6BoUWxPRWWWCuxw4XKNiq5NVD1QjP/oVW5PCrFbmVrW27Krpz8AVZZrWrBaRvQcsAb4CfitKbEtpgbWxAzg3zKSSEyLBjXIVtzf3vLZYqaUQhRQGRWoTmCcHjvDpX41UKcVGZLqPQPdoYkqTxQEW+DTWC7yfVBDWspmQkFHmlWXMfkwRInoMeKyAn+F6Zw2E4e1c9F7KPsMxdxdwfJQVNTfwU9U7bGBfcYLXmlKp5xUL4WUt/Jvgxp5QUaMd3MbA58XMRlqgBxiYVAw3Ay8m8PFNQC+EQVWXlD8XVVIQVwCtZQgiHwaYMpwwiFuThpn+ZFFBzA7gIzCVnLRNAjodF87GIhdPDzArJmxsJxxicYZvFrnwfUBnwrDG5ApjH5cQHnVFhYsB1gEdMWGjtwwXeZnWZJ1fZtjtucHSUuSY/WWGpXLREcWQeBjYFHGRzeUI4nKgiPWAKbmw0NJC2Hhc6Fx6G3SLe76Wc7s17j7BTOA94IKIY7g8I+PrDfZvZWX/gojj/QtMB74vOcwW/qPQnUZ23FHGZNlX5KLFQgMjciOH+W9nngG25MQwLpXvxcDCnO97YubXDGqLEUZTbsvKA8BDwNWGPvuNsTKKKXEzdKNwbg6mxMnlGcZ7iu1BYOmJy4U1phZpgEJhw4tEGZq8DQR2W57QdUPnTIrA1nZaGLEdJLt3p/uD2aCHo+5GFKt4zYWnxs2Qgej1cVZrJJIcYnoX+MiXdW/ywOYtztEp+Jhjd//5YMaGJ5iHrq0GVtB7X5ZY88p2gTBIlRDjiQ3AAVH2K0oIZTG5hP7ESCWkTzUoFJ3AEVlHYotJWQx0TYwNSoj5w2chKP3DibadnWk7SMepVkKMJ9qBWPocB2QdgG1eL0r7gQxwmBJSXvOeRJaXUcY/sMWExm4lxHhiPtBWRuhryjXeRQkJ1MWfVsb2HVZfpjrFbZ1aAMzJNaMLn3+35LbXwXJZR0IIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYRD/A9AH1PHpL17/AAAAABJRU5ErkJggg=="
296
-
297
- def process_image_data(uploaded_file):
298
- """Convert image to base64 encoded string"""
299
- try:
300
- image = Image.open(uploaded_file)
301
- buffer = io.BytesIO()
302
- image.save(buffer, format=image.format)
303
- return base64.b64encode(buffer.getvalue()).decode('utf-8'), image.format
304
- except Exception as e:
305
- st.error(f"Image processing error: {str(e)}")
306
- return None, None
307
-
308
- def generate_pdf_report(report_text, uploaded_file):
309
- """Generate professionally formatted PDF report with bold headers."""
310
- buffer = io.BytesIO()
311
- doc = SimpleDocTemplate(buffer, pagesize=letter,
312
- rightMargin=72, leftMargin=72,
313
- topMargin=72, bottomMargin=72)
314
-
315
- # Create custom styles for different parts of the report
316
- styles = getSampleStyleSheet()
317
-
318
- # Custom styles for better formatting
319
- title_style = ParagraphStyle(
320
- 'ReportTitle',
321
- parent=styles['Title'],
322
- fontSize=16,
323
- alignment=1, # Center aligned
324
- spaceAfter=12
325
- )
326
-
327
- header_style = ParagraphStyle(
328
- 'SectionHeader',
329
- parent=styles['Heading2'],
330
- fontSize=12,
331
- fontName='Helvetica-Bold',
332
- textColor=colors.black,
333
- spaceBefore=12,
334
- spaceAfter=6
335
- )
336
-
337
- normal_style = ParagraphStyle(
338
- 'NormalText',
339
- parent=styles['BodyText'],
340
- fontSize=11,
341
- leading=14,
342
- spaceAfter=8
343
- )
344
-
345
- abnormal_style = ParagraphStyle(
346
- 'AbnormalText',
347
- parent=styles['BodyText'],
348
- fontSize=11,
349
- leading=14,
350
- textColor=colors.red,
351
- backColor=colors.lightgrey,
352
- borderPadding=5,
353
- spaceAfter=8
354
- )
355
-
356
- footer_style = ParagraphStyle(
357
- 'FooterText',
358
- parent=styles['Italic'],
359
- fontSize=9,
360
- alignment=1 # Center aligned
361
- )
362
-
363
- # Begin building the report
364
- story = []
365
-
366
- # Hospital/Institution Header
367
- header_text = "RADIOLOGY DEPARTMENT"
368
- header = Paragraph(header_text, title_style)
369
- story.append(header)
370
-
371
- # Report Title
372
- report_title = "RADIOLOGICAL EXAMINATION REPORT"
373
- title = Paragraph(report_title, title_style)
374
- story.append(title)
375
-
376
- # Add date and report ID
377
- date_text = f"Date: {datetime.now().strftime('%B %d, %Y')}"
378
- report_id = f"Report ID: RAD-{datetime.now().strftime('%Y%m%d')}-{random.randint(1000, 9999)}"
379
- date_para = Paragraph(date_text, normal_style)
380
- id_para = Paragraph(report_id, normal_style)
381
- story.append(date_para)
382
- story.append(id_para)
383
- story.append(Spacer(1, 12))
384
-
385
- # Add the image to the PDF
386
- if uploaded_file:
387
- try:
388
- uploaded_file.seek(0) # Reset file pointer to beginning
389
- pil_image = Image.open(uploaded_file)
390
- img_width = 5 * inch
391
- aspect = float(pil_image.height) / float(pil_image.width)
392
- img_height = img_width * aspect
393
-
394
- img_temp = io.BytesIO()
395
- pil_image.save(img_temp, format=pil_image.format if pil_image.format else 'JPEG')
396
- img_temp.seek(0)
397
-
398
- img = ReportLabImage(img_temp, width=img_width, height=img_height)
399
- story.append(img)
400
- story.append(Spacer(1, 12))
401
-
402
- # Add image caption
403
- caption = Paragraph("Figure 1: Radiological Image for Analysis", normal_style)
404
- story.append(caption)
405
- story.append(Spacer(1, 12))
406
-
407
- except Exception as e:
408
- error_text = Paragraph(f"Image processing error: {str(e)}", normal_style)
409
- story.append(error_text)
410
- story.append(Spacer(1, 12))
411
-
412
- # Clean the report text (remove markdown-style formatting and unwanted characters)
413
- cleaned_text = report_text.replace('**', '').replace('##', '').replace('*', '-')
414
-
415
- # Define section headers to identify
416
- section_headers = [
417
- "DIAGNOSIS",
418
- "ETIOLOGY",
419
- "RISK FACTORS",
420
- "PATHOPHYSIOLOGY",
421
- "CLINICAL FEATURES",
422
- "SIGNS AND SYMPTOMS",
423
- "INVESTIGATIONS",
424
- "MANAGEMENT",
425
- "INITIAL STABILIZATION",
426
- "MEDICAL MANAGEMENT",
427
- "SURGICAL MANAGEMENT",
428
- "PROGNOSIS"
429
- ]
430
-
431
- # Split into lines for more precise processing
432
- lines = cleaned_text.split('\n')
433
- current_section = ""
434
- section_content = ""
435
-
436
- for i, line in enumerate(lines):
437
- line = line.strip()
438
- if not line:
439
- continue
440
-
441
- # Remove any "Step X:" prefixes
442
- line = re.sub(r'^Step \d+:\s*', '', line)
443
-
444
- # Check if this is a section header
445
- is_header = False
446
- for header in section_headers:
447
- if line.upper().startswith(header) or line.upper() == header + ":":
448
- is_header = True
449
- break
450
-
451
- # Also check if it's a short line ending with a colon (likely a header)
452
- if not is_header and len(line) < 60 and line.endswith(':'):
453
- is_header = True
454
-
455
- # If we found a header
456
- if is_header:
457
- # First add any accumulated content from previous section
458
- if section_content.strip():
459
- # Check for severe abnormalities to highlight
460
- severe_abnormal_keywords = [
461
- 'severe', 'critical', 'urgent', 'emergency', 'life-threatening',
462
- 'malignant', 'neoplasm', 'carcinoma', 'metastasis', 'hemorrhage',
463
- 'fracture', 'rupture', 'perforation',
464
- ]
465
-
466
- has_severe_issue = any(keyword in section_content.lower() for keyword in severe_abnormal_keywords)
467
-
468
- if current_section.upper().startswith("DIAGNOSIS") or current_section.upper().startswith("ABNORMAL"):
469
- # This is a diagnosis section - highlight abnormalities
470
- p = Paragraph(section_content, abnormal_style if has_severe_issue else normal_style)
471
- else:
472
- p = Paragraph(section_content, normal_style)
473
-
474
- story.append(p)
475
- story.append(Spacer(1, 6))
476
- section_content = ""
477
-
478
- # Add the new section header - remove any trailing colon for cleaner look
479
- clean_header = line.strip()
480
- if clean_header.endswith(':'):
481
- clean_header = clean_header[:-1]
482
-
483
- current_section = clean_header
484
- p = Paragraph(f"<b>{clean_header}</b>", header_style) # Bold the header
485
- story.append(p)
486
-
487
- else:
488
- # This is content - append to the current section content
489
- if section_content:
490
- section_content += "<br/>" + line
491
- else:
492
- section_content = line
493
-
494
- # Add any remaining content
495
- if section_content.strip():
496
- p = Paragraph(section_content, normal_style)
497
- story.append(p)
498
-
499
- # Add conclusion if not present
500
- if not any("PROGNOSIS" in line.upper() for line in lines):
501
- conclusion_header = Paragraph("<b>PROGNOSIS</b>", header_style)
502
- story.append(conclusion_header)
503
- story.append(Spacer(1, 6))
504
-
505
- conclusion_text = "Prognosis varies based on the extent and location of findings. Clinical correlation with the patient's symptoms and medical history is recommended."
506
- conclusion_para = Paragraph(conclusion_text, normal_style)
507
- story.append(conclusion_para)
508
-
509
- # Add footer with disclaimer
510
- story.append(Spacer(1, 24))
511
- disclaimer = "This report was generated with AI assistance and should be reviewed by a qualified healthcare professional."
512
- footer = Paragraph(disclaimer, footer_style)
513
- story.append(footer)
514
-
515
- # Build PDF
516
- doc.build(story)
517
- buffer.seek(0)
518
- return buffer
519
-
520
-
521
-
522
-
523
-
524
-
525
- def generate_radiology_report_groq(uploaded_file, client):
526
- """Generate AI-powered radiology analysis using Groq API"""
527
- base64_image, img_format = process_image_data(uploaded_file)
528
-
529
- if not base64_image:
530
- return None
531
-
532
- image_url = f"data:image/{img_format.lower()};base64,{base64_image}"
533
-
534
- try:
535
- with st.spinner("Analyzing image with Groq..."):
536
- # Add progress bar for visual feedback
537
- progress_bar = st.progress(0)
538
- for i in range(100):
539
- # Update progress bar
540
- progress_bar.progress(i + 1)
541
- import time
542
- time.sleep(0.025) # Simulate processing time
543
-
544
- # Updated prompt to request the detailed, structured format
545
- response = client.chat.completions.create(
546
- model="llama-3.2-90b-vision-preview", # Use Groq's model
547
- messages=[{
548
- "role": "user",
549
- "content": [
550
- {"type": "text", "text": (
551
- """As a radiologist, analyze the following MRI report and provide a comprehensive report structured as follows:
552
-
553
- 1. **DIAGNOSIS**: Clearly state the primary diagnosis, including dimensions where applicable (e.g., if a tumor is present). Use specific anatomical terms relevant to the body part being examined.
554
-
555
- 2. **FINDINGS**:
556
- - Provide detailed observations from the MRI report, including:
557
- - The size, shape, and location of any lesions or abnormalities if applicable.
558
- - Description of the surrounding tissues and structures if applicable.
559
- - Any noted changes in signal intensity on various sequences (e.g., T1W, T2W, FLAIR) if applicable.
560
- - Mention of any associated findings, such as edema, mass effect, or midline shift if applicble.
561
- - Specific comments on vascular structures, if applicable.
562
-
563
- 3. **PATHOPHYSIOLOGY**: Briefly explain the disease mechanism related to the diagnosis, focusing on how it affects the specific body part.
564
-
565
- 4. **CLINICAL FEATURES**: Provide an overview of typical clinical presentations associated with this diagnosis, emphasizing symptoms that may arise from abnormalities in the specified anatomical area.
566
-
567
- 5. **SIGNS AND SYMPTOMS**: List common signs and symptoms relevant to the findings in the MRI report. Tailor this section to align with the specific anatomy being assessed.
568
-
569
- 6. **INVESTIGATIONS**: Mention diagnostic tests typically used for confirmation of the diagnosis, including imaging studies or laboratory tests pertinent to the body part.
570
-
571
- 7. **MANAGEMENT**: Outline the management plans in three parts:
572
- - Initial Stabilization: Describe immediate steps for patient care.
573
- - Medical Management: Outline pharmacological treatments and monitoring.
574
- - Surgical Management (if applicable): Discuss any surgical interventions specific to the diagnosis and body part.
575
-
576
- 8. **PROGNOSIS**: Describe expected outcomes and factors that may affect prognosis based on the diagnosis. Include considerations specific to the anatomical region and associated complications.
577
-
578
- Please ensure to focus on the following findings from the report:
579
- - Mention specific abnormalities based on the region (e.g., "T2/FLAIR hyperintensities in the right fronto-parietal region" for brain MRI).
580
- - Highlight any significant lesions or deviations from the norm.
581
- - Include any other abnormal findings noted in the report that are relevant to the specific anatomy.
582
-
583
- Format each section with appropriate headings and use bullet points for lists. Base your analysis on the provided MRI report details."""
584
- )},
585
- {"type": "image_url", "image_url": {"url": image_url}},
586
- ]
587
- }],
588
- temperature=0.1,
589
- max_tokens=3000, # Increased token limit for more detailed response
590
- top_p=0.3
591
- )
592
- return response.choices[0].message.content
593
- except Exception as e:
594
- st.error(f"Groq API error: {str(e)}")
595
- return None
596
-
597
- def generate_radiology_report_openai(uploaded_file, client):
598
- """Generate AI-powered radiology analysis using OpenAI API"""
599
- base64_image, img_format = process_image_data(uploaded_file)
600
-
601
- if not base64_image:
602
- return None
603
-
604
- try:
605
- with st.spinner("Analyzing image with OpenAI..."):
606
- # Add progress bar for visual feedback
607
- progress_bar = st.progress(0)
608
- for i in range(100):
609
- # Update progress bar
610
- progress_bar.progress(i + 1)
611
- import time
612
- time.sleep(0.025) # Simulate processing time
613
-
614
- # Updated prompt to request the detailed, structured format
615
- response = client.chat.completions.create(
616
- model="gpt-4-vision-preview", # Use OpenAI's vision model
617
- messages=[
618
- {
619
- "role": "user",
620
- "content": [
621
- {
622
- "type": "text",
623
- "text": (
624
- "As a radiologist, analyze this medical image and provide a comprehensive report with the following structure:\n\n"
625
- "1. DIAGNOSIS: Clear statement of primary diagnosis\n\n"
626
- "2. ETIOLOGY: List possible causes of the identified condition\n\n"
627
- "3. RISK FACTORS: Bullet-point list of risk factors associated with the condition\n\n"
628
- "4. PATHOPHYSIOLOGY: Brief explanation of the disease mechanism\n\n"
629
- "5. CLINICAL FEATURES: Overview of typical clinical presentation\n\n"
630
- "6. SIGNS AND SYMPTOMS: Bullet-point list of common signs and symptoms\n\n"
631
- "7. INVESTIGATIONS: Diagnostic tests typically used for confirmation\n\n"
632
- "8. MANAGEMENT: Divided into three parts:\n"
633
- " - Initial Stabilization\n"
634
- " - Medical Management\n"
635
- " - Surgical Management (if applicable)\n\n"
636
- "9. PROGNOSIS: Expected outcomes and factors affecting prognosis\n\n"
637
- "Format each section with appropriate headings and use bullet points for lists. If you identify abnormal findings, make sure to highlight these clearly."
638
- )
639
- },
640
- {
641
- "type": "image_url",
642
- "image_url": {
643
- "url": f"data:image/{img_format.lower()};base64,{base64_image}"
644
- }
645
- }
646
- ]
647
- }
648
- ],
649
- max_tokens=3000 # Increased token limit for more detailed response
650
- )
651
- return response.choices[0].message.content
652
- except Exception as e:
653
- st.error(f"OpenAI API error: {str(e)}")
654
- return None
655
-
656
- def generate_radiology_report(uploaded_file, api_choice='groq'):
657
- """Generate report using the selected API"""
658
- if api_choice == 'groq':
659
- client = initialize_groq_client()
660
- if client:
661
- return generate_radiology_report_groq(uploaded_file, client)
662
- else:
663
- st.error("Failed to initialize Groq client. Please check your API key.")
664
- return None
665
- else: # OpenAI
666
- client = initialize_openai_client()
667
- if client:
668
- return generate_radiology_report_openai(uploaded_file, client)
669
- else:
670
- st.error("OpenAI API key is required. Please provide your API key.")
671
- return None
672
-
673
- # ======================
674
- # UI COMPONENTS
675
- # ======================
676
- def display_animated_logo():
677
- """Display an animated medical logo"""
678
- logo_b64 = encode_logo("MediSight\src\Round_image_depicting_a_futuristic_medical_image_a-1742282117033-photoaidcom-cropped.png")
679
-
680
- # If logo file doesn't exist, use the placeholder from encode_logo
681
- st.markdown(
682
- f"""
683
- <div class="logo-container">
684
- <div class="logo-pulse">
685
- <img src="data:image/png;base64,{logo_b64}" width="200">
686
- </div>
687
- </div>
688
- """,
689
- unsafe_allow_html=True
690
- )
691
-
692
- def display_main_interface():
693
- """Render primary application interface"""
694
- # Display animated logo and titles
695
- display_animated_logo()
696
-
697
- st.markdown('<h1 class="main-title">Radiology Analyzer</h1>', unsafe_allow_html=True)
698
- st.markdown('<p class="sub-title">Advanced Medical Imaging Analysis</p>', unsafe_allow_html=True)
699
-
700
- # Action buttons
701
- if st.session_state.get('analysis_result'):
702
- st.markdown(
703
- """
704
- <div class="analysis-complete">
705
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
706
- <path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
707
- </svg>
708
- Analysis Complete
709
- </div>
710
- """,
711
- unsafe_allow_html=True
712
- )
713
-
714
- col1, col2 = st.columns([1, 1])
715
- with col1:
716
- pdf_report = generate_pdf_report(st.session_state.analysis_result, st.session_state.uploaded_file)
717
- st.download_button(
718
- label="📄 Download PDF Report",
719
- data=pdf_report,
720
- file_name="radiology_report.pdf",
721
- mime="application/pdf",
722
- use_container_width=True,
723
- help="Download formal PDF version of the report"
724
- )
725
-
726
- with col2:
727
- if st.button("Clear Analysis 🗑️", use_container_width=True, help="Remove current results"):
728
- st.session_state.pop('analysis_result', None)
729
- st.session_state.pop('uploaded_file', None)
730
- st.rerun()
731
-
732
- # Display analysis results in a styled container
733
- st.markdown("### 🎯 Radiological Findings Report")
734
- st.markdown(
735
- f'<div class="report-container"><div class="report-text">{st.session_state.analysis_result}</div></div>',
736
- unsafe_allow_html=True
737
- )
738
- else:
739
- # Show a centered placeholder message
740
- st.markdown(
741
- """
742
- <div style="text-align: center; margin-top: 50px; color: #9ca3af; padding: 100px 0;">
743
- <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="currentColor" viewBox="0 0 16 16" style="margin-bottom: 20px;">
744
- <path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0ZM1.5 8a6.5 6.5 0 1 1 13 0 6.5 6.5 0 0 1-13 0Zm4.879-2.773 4.264 2.559a.25.25 0 0 1 0 .428l-4.264 2.559A.25.25 0 0 1 6 10.559V5.442a.25.25 0 0 1 .379-.215Z"/>
745
- </svg>
746
- <p style="font-size: 1.2rem;">Upload a medical image to begin analysis</p>
747
- </div>
748
- """,
749
- unsafe_allow_html=True
750
- )
751
-
752
- def render_sidebar():
753
- """Create sidebar interface elements"""
754
- with st.sidebar:
755
- st.markdown('<div class="sidebar-item">', unsafe_allow_html=True)
756
- st.markdown('<div class="sidebar-title">Diagnostic Capabilities</div>', unsafe_allow_html=True)
757
- st.markdown("""
758
- - **Multi-Modality Analysis:** X-ray, MRI, CT, Ultrasound
759
- - **Pathology Detection:** Fractures, tumors, infections
760
- - **Comparative Analysis:** Track disease progression
761
- - **Structured Reporting:** Standardized output format
762
- - **Clinical Correlation:** Suggested next steps
763
- """)
764
- st.markdown("""
765
- <div class="tooltip">
766
- <strong>Disclaimer:</strong> This service does not provide medical advice.
767
- <span class="tooltiptext">Always consult with a qualified healthcare professional for diagnosis and treatment.</span>
768
- </div>
769
- """, unsafe_allow_html=True)
770
- st.markdown('</div>', unsafe_allow_html=True)
771
-
772
- # API Selection
773
- st.markdown('<div class="sidebar-item">', unsafe_allow_html=True)
774
- st.markdown('<div class="sidebar-title">API Configuration</div>', unsafe_allow_html=True)
775
-
776
- # API provider selection
777
- api_provider = st.radio(
778
- "Select AI Provider",
779
- options=["Groq"],
780
- index=0, # Default to Groq
781
- )
782
-
783
- # If OpenAI is selected, show API key input
784
- # if api_provider == "OpenAI":
785
- # openai_key = st.text_input(
786
- # "OpenAI API Key",
787
- # type="password",
788
- # help="Enter your OpenAI API key",
789
- # value=st.session_state.get('openai_api_key', '')
790
- # )
791
- # if openai_key:
792
- # st.session_state.openai_api_key = openai_key
793
-
794
- st.markdown('</div>', unsafe_allow_html=True)
795
-
796
- # Image Upload Section
797
- st.markdown('<div class="sidebar-item">', unsafe_allow_html=True)
798
- st.markdown('<div class="sidebar-title">Image Upload Section</div>', unsafe_allow_html=True)
799
-
800
- # Create a styled drop zone
801
- st.markdown(
802
- """
803
- <div class="drop-zone">
804
- <div class="drop-icon">📤</div>
805
- <p>Drag and drop file here</p>
806
- <p style="font-size: 0.8rem; color: #9ca3af;">or use the uploader below</p>
807
- </div>
808
- """,
809
- unsafe_allow_html=True
810
- )
811
-
812
- uploaded_file = st.file_uploader(
813
- "Select Medical Image",
814
- type=ALLOWED_FILE_TYPES,
815
- label_visibility="collapsed",
816
- help="Supported formats: PNG, JPG, JPEG"
817
- )
818
-
819
- if uploaded_file:
820
- st.session_state.uploaded_file = uploaded_file # Store uploaded file in session state
821
-
822
- # Display image with a styled container
823
- st.markdown('<div class="card">', unsafe_allow_html=True)
824
- st.image(Image.open(uploaded_file),
825
- caption="Uploaded Medical Image",
826
- use_container_width=True)
827
- st.markdown('</div>', unsafe_allow_html=True)
828
-
829
- # Styled analysis button
830
- if st.button("▶️ Initiate Analysis", use_container_width=True):
831
- with st.spinner("Processing image..."):
832
- # Use the selected API provider
833
- api_choice = 'groq' if api_provider == "Groq" else 'openai'
834
- report = generate_radiology_report(uploaded_file, api_choice)
835
-
836
- if report:
837
- st.session_state.analysis_result = report
838
- st.rerun()
839
- st.markdown('</div>', unsafe_allow_html=True)
840
-
841
- # ======================
842
- # APPLICATION ENTRYPOINT
843
- # ======================
844
- def main():
845
- """Primary application controller"""
846
- # Check if dark mode is in session state, default to true
847
- if 'dark_mode' not in st.session_state:
848
- st.session_state.dark_mode = True
849
-
850
- configure_application()
851
-
852
- render_sidebar()
853
- display_main_interface()
854
-
855
- if __name__ == "__main__":
856
- main()
857
-