Fernando Moreno commited on
Commit
2d2d2d9
·
1 Parent(s): 86ab63d

Enhanced app with multi-timepoint progression analysis and simplified patient data input

Browse files
Files changed (1) hide show
  1. app.py +162 -139
app.py CHANGED
@@ -6,6 +6,8 @@ from dotenv import load_dotenv
6
  import PyPDF2
7
  import io
8
  from datetime import datetime
 
 
9
 
10
  # Page configuration
11
  st.set_page_config(
@@ -86,60 +88,89 @@ st.markdown("""
86
  margin: 0.5rem 0;
87
  border-left: 3px solid #03a9f4;
88
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  </style>
90
  """, unsafe_allow_html=True)
91
 
92
  # System prompts
93
- CORNEA_ANALYSIS_PROMPT = """You are an expert ophthalmologist specializing in corneal diseases. A user will ask you questions about interpreting corneal imaging, specifically Pentacam, and about subclinical corneal edema.
94
 
95
- **Part 1: Pentacam Interpretation**
96
- Analyze Pentacam imaging for diagnosing and monitoring corneal diseases
97
- Assess key parameters: corneal shape, thickness, and elevation
98
- Evaluate anterior and posterior corneal surfaces
99
- Consider ABCD staging system and Cruces University Hospital progression score
100
 
101
- **Part 2: Subclinical Corneal Edema Analysis**
102
- Evaluate for Fuchs Endothelial Corneal Dystrophy (FECD)
103
- Apply Sun criteria for subclinical corneal edema
104
- Assess displacement of thinnest point
105
- Check for loss of parallel isopachs
106
- • Evaluate focal depression of posterior surface
107
 
108
- Please analyze the provided images and provide a detailed assessment."""
 
 
 
109
 
110
- COMPARISON_PROMPT = """Compare the provided Pentacam scans from different timepoints, focusing on:
111
 
112
- 1. Changes in corneal parameters:
113
- • Thickness variations
114
- • Topographic changes
115
- • Elevation differences
116
- • Progression indicators
117
 
118
- 2. Clinical significance:
119
- Disease progression/stability
120
- Treatment response
121
- Risk assessment
 
 
 
 
 
 
 
 
 
 
 
122
 
123
- 3. Recommendations:
124
- Follow-up interval
125
- Treatment modifications
126
- Additional testing needs"""
127
 
128
- def extract_pdf_text(pdf_file):
129
- """Extract text from uploaded PDF file"""
130
- pdf_reader = PyPDF2.PdfReader(pdf_file)
131
- text = ""
132
- for page in pdf_reader.pages:
133
- text += page.extract_text()
134
- return text
135
 
136
- def analyze_cornea_images(images, timepoint=None, patient_data=None):
137
- """Analyze corneal images with optional timepoint and patient data"""
138
- if timepoint:
139
- prompt = f"{CORNEA_ANALYSIS_PROMPT}\n\nTimepoint: {timepoint}\n"
 
 
 
 
 
 
140
  else:
141
- prompt = f"{CORNEA_ANALYSIS_PROMPT}\n"
 
142
 
 
 
 
 
 
143
  if patient_data:
144
  prompt += f"\nPatient Information:\n{patient_data}\n"
145
 
@@ -148,15 +179,19 @@ def analyze_cornea_images(images, timepoint=None, patient_data=None):
148
  response = model.generate_content(content)
149
  return response.text
150
 
151
- def compare_timepoints(images1, date1, images2, date2, patient_data=None):
152
- """Compare corneal images from two timepoints"""
153
- prompt = f"{COMPARISON_PROMPT}\n\nTimepoint 1: {date1}\nTimepoint 2: {date2}\n"
 
154
 
155
- if patient_data:
156
- prompt += f"\nPatient Information:\n{patient_data}\n"
 
 
 
157
 
158
- prompt += "\nPlease compare these corneal scans:"
159
- content = [prompt] + images1 + images2
160
  response = model.generate_content(content)
161
  return response.text
162
 
@@ -181,111 +216,99 @@ def main():
181
  </div>
182
  """, unsafe_allow_html=True)
183
 
184
- # Main content
185
- col1, col2 = st.columns([1, 1])
186
-
187
- with col1:
188
- # Patient Information Section
189
- st.markdown("### Patient Information")
190
- with st.container():
191
- st.markdown('<div class="patient-info">', unsafe_allow_html=True)
192
- patient_name = st.text_input("Patient Name")
193
- patient_id = st.text_input("Patient ID")
194
- dob = st.date_input("Date of Birth")
195
- diagnosis = st.text_input("Previous Diagnosis (if any)")
196
-
197
- # Clinical History
198
- st.markdown("#### Clinical History")
199
- history_text = st.text_area("Enter relevant clinical history")
200
-
201
- # Multiple PDF uploads for patient records
202
- st.markdown("#### Additional Patient Records (PDF)")
203
- patient_pdfs = st.file_uploader("Upload Patient Records", type=['pdf'], accept_multiple_files=True)
204
- if patient_pdfs:
205
- for pdf in patient_pdfs:
206
- with st.expander(f"View {pdf.name}"):
207
- st.text(extract_pdf_text(pdf))
208
- st.markdown('</div>', unsafe_allow_html=True)
209
 
210
- # Pentacam Scan Upload Section
211
- st.markdown("### Upload Pentacam Scans")
 
 
 
212
  st.markdown('<div class="upload-section">', unsafe_allow_html=True)
213
- scan_type = st.radio("Select Analysis Type", ["Single Timepoint", "Compare Timepoints"])
 
214
 
215
- if scan_type == "Single Timepoint":
216
- scan_date = st.date_input("Scan Date")
217
- uploaded_files = st.file_uploader("Upload Pentacam Scans", type=['png', 'jpg', 'jpeg', 'pdf'], accept_multiple_files=True)
 
 
218
 
219
- if uploaded_files:
220
- images = []
221
- for file in uploaded_files:
222
- if file.type.startswith('image'):
223
- image = Image.open(file)
224
- images.append(image)
225
- elif file.type == 'application/pdf':
226
- # Handle PDF files here if needed
227
- st.warning(f"PDF processing for {file.name} will be implemented soon")
228
 
229
- if images:
230
- st.markdown("#### Preview Uploaded Scans")
231
- cols = st.columns(len(images))
232
- for idx, (col, img) in enumerate(zip(cols, images)):
233
- with col:
234
- st.image(img, caption=f"Scan {idx + 1}", use_column_width=True)
235
-
236
- if st.button("Analyze Scans"):
237
- with st.spinner("Analyzing..."):
238
- analysis = analyze_cornea_images(
239
- images,
240
- timepoint=scan_date.strftime("%Y-%m-%d"),
241
- patient_data=f"Name: {patient_name}\nID: {patient_id}\nDOB: {str(dob)}\nDiagnosis: {diagnosis}\nHistory: {history_text}"
242
- )
243
- st.markdown("### Analysis Results")
244
- st.markdown('<div class="analysis-container">', unsafe_allow_html=True)
245
- st.markdown(analysis)
246
- st.markdown('</div>', unsafe_allow_html=True)
 
 
 
247
 
248
- else: # Compare Timepoints
249
- col1, col2 = st.columns(2)
 
 
250
 
251
  with col1:
252
- st.markdown("#### First Timepoint")
253
- date1 = st.date_input("Date of First Scan")
254
- files1 = st.file_uploader("Upload First Scans", type=['png', 'jpg', 'jpeg', 'pdf'], accept_multiple_files=True)
255
 
256
  with col2:
257
- st.markdown("#### Second Timepoint")
258
- date2 = st.date_input("Date of Second Scan")
259
- files2 = st.file_uploader("Upload Second Scans", type=['png', 'jpg', 'jpeg', 'pdf'], accept_multiple_files=True)
260
-
261
- if files1 and files2:
262
- images1 = [Image.open(f) for f in files1 if f.type.startswith('image')]
263
- images2 = [Image.open(f) for f in files2 if f.type.startswith('image')]
264
 
265
- if images1 and images2:
266
- st.markdown("#### Preview First Timepoint")
267
- cols1 = st.columns(len(images1))
268
- for idx, (col, img) in enumerate(zip(cols1, images1)):
269
- with col:
270
- st.image(img, caption=f"Scan {idx + 1}", use_column_width=True)
271
-
272
- st.markdown("#### Preview Second Timepoint")
273
- cols2 = st.columns(len(images2))
274
- for idx, (col, img) in enumerate(zip(cols2, images2)):
275
- with col:
276
- st.image(img, caption=f"Scan {idx + 1}", use_column_width=True)
277
 
278
- if st.button("Compare Scans"):
279
- with st.spinner("Analyzing..."):
280
- comparison = compare_timepoints(
281
- images1, date1.strftime("%Y-%m-%d"),
282
- images2, date2.strftime("%Y-%m-%d"),
283
- patient_data=f"Name: {patient_name}\nID: {patient_id}\nDOB: {str(dob)}\nDiagnosis: {diagnosis}\nHistory: {history_text}"
284
- )
285
- st.markdown("### Comparison Results")
286
- st.markdown('<div class="analysis-container">', unsafe_allow_html=True)
287
- st.markdown(comparison)
288
- st.markdown('</div>', unsafe_allow_html=True)
 
 
 
 
 
 
289
 
290
  st.markdown('</div>', unsafe_allow_html=True)
291
 
 
6
  import PyPDF2
7
  import io
8
  from datetime import datetime
9
+ import pandas as pd
10
+ from collections import defaultdict
11
 
12
  # Page configuration
13
  st.set_page_config(
 
88
  margin: 0.5rem 0;
89
  border-left: 3px solid #03a9f4;
90
  }
91
+ .timeline-container {
92
+ margin: 2rem 0;
93
+ padding: 1rem;
94
+ background-color: #fff;
95
+ border-radius: 0.5rem;
96
+ border: 1px solid #e9ecef;
97
+ }
98
+ .timepoint-card {
99
+ background-color: #f8f9fa;
100
+ padding: 1rem;
101
+ margin: 0.5rem 0;
102
+ border-radius: 0.5rem;
103
+ border-left: 3px solid #2E86C1;
104
+ }
105
  </style>
106
  """, unsafe_allow_html=True)
107
 
108
  # System prompts
109
+ CORNEA_ANALYSIS_PROMPT = """You are an expert ophthalmologist specializing in corneal diseases. Analyze these Pentacam scans and patient data with focus on:
110
 
111
+ 1. Corneal Parameters Analysis:
112
+ Thickness mapping and progression
113
+ Topographic changes
114
+ Elevation data (anterior and posterior)
115
+ Keratoconus indices and classification
116
 
117
+ 2. Disease Assessment:
118
+ ABCD Keratoconus staging
119
+ Fuchs Endothelial Corneal Dystrophy evaluation
120
+ Subclinical corneal edema (Sun criteria)
121
+ Risk assessment
 
122
 
123
+ 3. Clinical Interpretation:
124
+ • Pattern recognition
125
+ • Disease progression markers
126
+ • Treatment implications
127
 
128
+ Please provide a detailed clinical assessment."""
129
 
130
+ PROGRESSION_ANALYSIS_PROMPT = """Analyze the progression of corneal parameters across multiple timepoints, focusing on:
 
 
 
 
131
 
132
+ 1. Temporal Changes:
133
+ Progressive changes in corneal thickness
134
+ Evolution of topographic patterns
135
+ Changes in elevation maps
136
+ • Progression of keratoconus indices
137
+
138
+ 2. Rate of Progression:
139
+ • Quantify changes between timepoints
140
+ • Identify acceleration or stabilization periods
141
+ • Compare with expected disease progression
142
+
143
+ 3. Risk Assessment:
144
+ • Current status evaluation
145
+ • Future progression risk
146
+ • Treatment recommendations
147
 
148
+ 4. Timeline Analysis:
149
+ Key changes between each timepoint
150
+ Overall progression pattern
151
+ Critical periods of change
152
 
153
+ Please provide a comprehensive progression analysis with clinical recommendations."""
 
 
 
 
 
 
154
 
155
+ def extract_patient_data(uploaded_file):
156
+ """Extract and process patient data from uploaded file"""
157
+ patient_data = {}
158
+
159
+ if uploaded_file.type == "application/pdf":
160
+ pdf_reader = PyPDF2.PdfReader(uploaded_file)
161
+ text = ""
162
+ for page in pdf_reader.pages:
163
+ text += page.extract_text()
164
+ patient_data['raw_text'] = text
165
  else:
166
+ # Handle other file types if needed
167
+ patient_data['raw_text'] = "File type not supported for detailed extraction"
168
 
169
+ return patient_data
170
+
171
+ def analyze_timepoint(images, date, patient_data=None):
172
+ """Analyze a single timepoint"""
173
+ prompt = f"{CORNEA_ANALYSIS_PROMPT}\n\nTimepoint: {date}\n"
174
  if patient_data:
175
  prompt += f"\nPatient Information:\n{patient_data}\n"
176
 
 
179
  response = model.generate_content(content)
180
  return response.text
181
 
182
+ def analyze_progression(timepoints_data):
183
+ """Analyze progression across multiple timepoints"""
184
+ prompt = f"{PROGRESSION_ANALYSIS_PROMPT}\n\n"
185
+ prompt += "Timepoints for analysis:\n"
186
 
187
+ # Add all timepoints to the prompt
188
+ all_images = []
189
+ for date, images in timepoints_data.items():
190
+ prompt += f"\n- {date}:"
191
+ all_images.extend(images)
192
 
193
+ prompt += "\n\nPlease analyze the progression across these timepoints:"
194
+ content = [prompt] + all_images
195
  response = model.generate_content(content)
196
  return response.text
197
 
 
216
  </div>
217
  """, unsafe_allow_html=True)
218
 
219
+ # Patient Information Section
220
+ st.markdown("### Patient Information")
221
+ patient_file = st.file_uploader("Upload Patient Information (PDF/Text)", type=['pdf', 'txt'])
222
+ patient_data = None
223
+ if patient_file:
224
+ patient_data = extract_patient_data(patient_file)
225
+ with st.expander("View Extracted Patient Information"):
226
+ st.text(patient_data.get('raw_text', 'No text extracted'))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
+ # Scan Analysis Section
229
+ st.markdown("### Pentacam Scan Analysis")
230
+ analysis_type = st.radio("Select Analysis Type", ["Single Timepoint", "Multiple Timepoint Progression"])
231
+
232
+ if analysis_type == "Single Timepoint":
233
  st.markdown('<div class="upload-section">', unsafe_allow_html=True)
234
+ scan_date = st.date_input("Scan Date")
235
+ uploaded_files = st.file_uploader("Upload Pentacam Scans", type=['png', 'jpg', 'jpeg'], accept_multiple_files=True)
236
 
237
+ if uploaded_files:
238
+ images = []
239
+ for file in uploaded_files:
240
+ image = Image.open(file)
241
+ images.append(image)
242
 
243
+ if images:
244
+ st.markdown("#### Preview Scans")
245
+ cols = st.columns(len(images))
246
+ for idx, (col, img) in enumerate(zip(cols, images)):
247
+ with col:
248
+ st.image(img, caption=f"Scan {idx + 1}", use_column_width=True)
 
 
 
249
 
250
+ if st.button("Analyze Scans"):
251
+ with st.spinner("Analyzing..."):
252
+ analysis = analyze_timepoint(
253
+ images,
254
+ scan_date.strftime("%Y-%m-%d"),
255
+ patient_data.get('raw_text') if patient_data else None
256
+ )
257
+ st.markdown("### Analysis Results")
258
+ st.markdown('<div class="analysis-container">', unsafe_allow_html=True)
259
+ st.markdown(analysis)
260
+ st.markdown('</div>', unsafe_allow_html=True)
261
+
262
+ st.markdown('</div>', unsafe_allow_html=True)
263
+
264
+ else: # Multiple Timepoint Progression
265
+ st.markdown('<div class="upload-section">', unsafe_allow_html=True)
266
+ st.info("Upload scans from multiple timepoints to analyze progression")
267
+
268
+ # Create a dynamic form for multiple timepoints
269
+ timepoints_data = defaultdict(list)
270
+ num_timepoints = st.number_input("Number of Timepoints", min_value=2, max_value=10, value=3)
271
 
272
+ st.markdown('<div class="timeline-container">', unsafe_allow_html=True)
273
+ for i in range(num_timepoints):
274
+ st.markdown(f'<div class="timepoint-card">', unsafe_allow_html=True)
275
+ col1, col2 = st.columns([1, 3])
276
 
277
  with col1:
278
+ date = st.date_input(f"Date - Timepoint {i+1}", key=f"date_{i}")
 
 
279
 
280
  with col2:
281
+ files = st.file_uploader(
282
+ f"Upload Scans - Timepoint {i+1}",
283
+ type=['png', 'jpg', 'jpeg'],
284
+ accept_multiple_files=True,
285
+ key=f"files_{i}"
286
+ )
 
287
 
288
+ if files:
289
+ images = []
290
+ for file in files:
291
+ image = Image.open(file)
292
+ images.append(image)
293
+ timepoints_data[date.strftime("%Y-%m-%d")].extend(images)
 
 
 
 
 
 
294
 
295
+ if images:
296
+ cols = st.columns(len(images))
297
+ for idx, (col, img) in enumerate(zip(cols, images)):
298
+ with col:
299
+ st.image(img, caption=f"Scan {idx + 1}", use_column_width=True)
300
+
301
+ st.markdown('</div>', unsafe_allow_html=True)
302
+ st.markdown('</div>', unsafe_allow_html=True)
303
+
304
+ if timepoints_data and len(timepoints_data) >= 2:
305
+ if st.button("Analyze Progression"):
306
+ with st.spinner("Analyzing progression across timepoints..."):
307
+ progression_analysis = analyze_progression(timepoints_data)
308
+ st.markdown("### Progression Analysis Results")
309
+ st.markdown('<div class="analysis-container">', unsafe_allow_html=True)
310
+ st.markdown(progression_analysis)
311
+ st.markdown('</div>', unsafe_allow_html=True)
312
 
313
  st.markdown('</div>', unsafe_allow_html=True)
314