Spaces:
Sleeping
Sleeping
Fernando Moreno
commited on
Commit
·
4ebcec2
1
Parent(s):
2d2d2d9
Enhanced progression analysis with automatic timepoint organization
Browse files
app.py
CHANGED
@@ -8,6 +8,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(
|
@@ -195,6 +196,44 @@ def analyze_progression(timepoints_data):
|
|
195 |
response = model.generate_content(content)
|
196 |
return response.text
|
197 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
def main():
|
199 |
# Header
|
200 |
st.markdown("""
|
@@ -227,7 +266,7 @@ def main():
|
|
227 |
|
228 |
# Scan Analysis Section
|
229 |
st.markdown("### Pentacam Scan Analysis")
|
230 |
-
analysis_type = st.radio("Select Analysis Type", ["Single Timepoint", "
|
231 |
|
232 |
if analysis_type == "Single Timepoint":
|
233 |
st.markdown('<div class="upload-section">', unsafe_allow_html=True)
|
@@ -261,54 +300,70 @@ def main():
|
|
261 |
|
262 |
st.markdown('</div>', unsafe_allow_html=True)
|
263 |
|
264 |
-
else: #
|
265 |
st.markdown('<div class="upload-section">', unsafe_allow_html=True)
|
266 |
-
st.info("Upload scans
|
|
|
267 |
|
268 |
-
|
269 |
-
|
270 |
-
|
|
|
|
|
271 |
|
272 |
-
|
273 |
-
|
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 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
)
|
287 |
|
288 |
-
|
|
|
|
|
|
|
|
|
289 |
images = []
|
290 |
-
|
291 |
-
image = Image.open(file)
|
292 |
-
images.append(image)
|
293 |
-
timepoints_data[date.strftime("%Y-%m-%d")].extend(images)
|
294 |
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
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 |
|
|
|
8 |
from datetime import datetime
|
9 |
import pandas as pd
|
10 |
from collections import defaultdict
|
11 |
+
import re
|
12 |
|
13 |
# Page configuration
|
14 |
st.set_page_config(
|
|
|
196 |
response = model.generate_content(content)
|
197 |
return response.text
|
198 |
|
199 |
+
def extract_date_from_filename(filename):
|
200 |
+
"""Extract date from filename using common patterns"""
|
201 |
+
# Common date patterns (add more patterns if needed)
|
202 |
+
patterns = [
|
203 |
+
r'(\d{4}[-_/]\d{2}[-_/]\d{2})', # YYYY-MM-DD, YYYY_MM_DD
|
204 |
+
r'(\d{2}[-_/]\d{2}[-_/]\d{4})', # DD-MM-YYYY, DD_MM_YYYY
|
205 |
+
r'(\d{8})', # YYYYMMDD
|
206 |
+
]
|
207 |
+
|
208 |
+
for pattern in patterns:
|
209 |
+
match = re.search(pattern, filename)
|
210 |
+
if match:
|
211 |
+
date_str = match.group(1)
|
212 |
+
try:
|
213 |
+
# Try different date formats
|
214 |
+
for fmt in ['%Y-%m-%d', '%Y_%m_%d', '%d-%m-%Y', '%d_%m_%Y', '%Y%m%d']:
|
215 |
+
try:
|
216 |
+
return datetime.strptime(date_str.replace('/', '-'), fmt).strftime('%Y-%m-%d')
|
217 |
+
except ValueError:
|
218 |
+
continue
|
219 |
+
except ValueError:
|
220 |
+
continue
|
221 |
+
return None
|
222 |
+
|
223 |
+
def organize_scans_by_date(files):
|
224 |
+
"""Organize uploaded files by their dates"""
|
225 |
+
organized_files = defaultdict(list)
|
226 |
+
unorganized_files = []
|
227 |
+
|
228 |
+
for file in files:
|
229 |
+
date = extract_date_from_filename(file.name)
|
230 |
+
if date:
|
231 |
+
organized_files[date].append(file)
|
232 |
+
else:
|
233 |
+
unorganized_files.append(file)
|
234 |
+
|
235 |
+
return organized_files, unorganized_files
|
236 |
+
|
237 |
def main():
|
238 |
# Header
|
239 |
st.markdown("""
|
|
|
266 |
|
267 |
# Scan Analysis Section
|
268 |
st.markdown("### Pentacam Scan Analysis")
|
269 |
+
analysis_type = st.radio("Select Analysis Type", ["Single Timepoint", "Progression Analysis"])
|
270 |
|
271 |
if analysis_type == "Single Timepoint":
|
272 |
st.markdown('<div class="upload-section">', unsafe_allow_html=True)
|
|
|
300 |
|
301 |
st.markdown('</div>', unsafe_allow_html=True)
|
302 |
|
303 |
+
else: # Progression Analysis
|
304 |
st.markdown('<div class="upload-section">', unsafe_allow_html=True)
|
305 |
+
st.info("""Upload all your Pentacam scans at once. The system will automatically organize them by date and analyze progression.
|
306 |
+
For best results, ensure your scan filenames include dates (e.g., 'scan_2023-01-15.jpg' or 'pentacam_20230115.png')""")
|
307 |
|
308 |
+
uploaded_files = st.file_uploader(
|
309 |
+
"Upload All Pentacam Scans",
|
310 |
+
type=['png', 'jpg', 'jpeg'],
|
311 |
+
accept_multiple_files=True
|
312 |
+
)
|
313 |
|
314 |
+
if uploaded_files:
|
315 |
+
organized_files, unorganized_files = organize_scans_by_date(uploaded_files)
|
|
|
|
|
|
|
|
|
|
|
316 |
|
317 |
+
if organized_files:
|
318 |
+
st.markdown("### Organized Scans by Date")
|
319 |
+
st.markdown('<div class="timeline-container">', unsafe_allow_html=True)
|
320 |
+
|
321 |
+
timepoints_data = defaultdict(list)
|
322 |
+
dates = sorted(organized_files.keys())
|
|
|
323 |
|
324 |
+
for date in dates:
|
325 |
+
st.markdown(f'<div class="timepoint-card">', unsafe_allow_html=True)
|
326 |
+
st.markdown(f"#### Timepoint: {date}")
|
327 |
+
|
328 |
+
files = organized_files[date]
|
329 |
images = []
|
330 |
+
cols = st.columns(len(files))
|
|
|
|
|
|
|
331 |
|
332 |
+
for idx, (file, col) in enumerate(zip(files, cols)):
|
333 |
+
with col:
|
334 |
+
image = Image.open(file)
|
335 |
+
images.append(image)
|
336 |
+
st.image(image, caption=f"Scan {idx + 1}", use_column_width=True)
|
337 |
+
|
338 |
+
timepoints_data[date].extend(images)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
339 |
st.markdown('</div>', unsafe_allow_html=True)
|
340 |
+
|
341 |
+
if unorganized_files:
|
342 |
+
st.warning(f"{len(unorganized_files)} files couldn't be automatically dated. Please ensure filenames include dates.")
|
343 |
+
with st.expander("Manually Assign Dates"):
|
344 |
+
for file in unorganized_files:
|
345 |
+
col1, col2 = st.columns([2, 1])
|
346 |
+
with col1:
|
347 |
+
st.text(file.name)
|
348 |
+
with col2:
|
349 |
+
date = st.date_input(f"Date for {file.name}", key=f"manual_{file.name}")
|
350 |
+
image = Image.open(file)
|
351 |
+
timepoints_data[date.strftime("%Y-%m-%d")].append(image)
|
352 |
+
|
353 |
+
if len(timepoints_data) >= 2:
|
354 |
+
if st.button("Analyze Progression"):
|
355 |
+
with st.spinner("Analyzing progression across timepoints..."):
|
356 |
+
progression_analysis = analyze_progression(timepoints_data)
|
357 |
+
st.markdown("### Progression Analysis Results")
|
358 |
+
st.markdown('<div class="analysis-container">', unsafe_allow_html=True)
|
359 |
+
st.markdown(progression_analysis)
|
360 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
361 |
+
else:
|
362 |
+
st.warning("Please upload scans from at least 2 different timepoints for progression analysis.")
|
363 |
+
|
364 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
365 |
+
else:
|
366 |
+
st.error("No dated scans found. Please ensure your filenames include dates (e.g., 'scan_2023-01-15.jpg').")
|
367 |
|
368 |
st.markdown('</div>', unsafe_allow_html=True)
|
369 |
|