pskeshu commited on
Commit
cc1addd
Β·
1 Parent(s): 36076d6

updated stuf

Browse files
.gitattributes CHANGED
@@ -33,4 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
36
  *.tif filter=lfs diff=lfs merge=lfs -text
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.BMP filter=lfs diff=lfs merge=lfs -text
37
  *.tif filter=lfs diff=lfs merge=lfs -text
app.py CHANGED
@@ -1,6 +1,9 @@
1
  #!/usr/bin/env python3
2
  """
3
- Minimal Anton Streamlit App - Crash-Safe Version
 
 
 
4
  """
5
 
6
  import streamlit as st
@@ -9,12 +12,7 @@ import sys
9
  from pathlib import Path
10
  import numpy as np
11
  from PIL import Image
12
- import tempfile
13
- import traceback
14
- import gc # Garbage collection
15
-
16
- # Configure PIL to handle large images better
17
- Image.MAX_IMAGE_PIXELS = None # Remove PIL size limit
18
 
19
  # Setup page
20
  st.set_page_config(
@@ -23,148 +21,472 @@ st.set_page_config(
23
  layout="wide"
24
  )
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  # Header
27
  st.title("πŸ”¬ Anton Microscopy Analysis")
28
- st.markdown("**Simple Interface**: Upload image β†’ See basic analysis")
29
 
30
- # Debug info container
31
- debug_container = st.empty()
32
 
33
- # Sidebar
 
 
 
 
 
34
  st.sidebar.header("πŸŽ›οΈ Controls")
35
 
36
- # Check API status
37
  api_status = []
 
 
38
  if os.getenv('GOOGLE_API_KEY'):
39
  api_status.append("βœ… Google API Key")
 
40
  elif os.getenv('ANTHROPIC_API_KEY'):
41
  api_status.append("βœ… Anthropic API Key")
 
42
  else:
43
- api_status.append("⚠️ No API key - demo mode")
44
 
45
  for status in api_status:
46
  st.sidebar.write(status)
47
 
48
- # Simple file upload with unique key - avoid st.rerun() issues
49
- st.sidebar.subheader("πŸ“ Upload Image")
50
 
51
- # Use session state to track upload state to avoid rerun issues
52
- if 'upload_key' not in st.session_state:
53
- st.session_state.upload_key = 0
54
 
55
- uploaded_file = st.sidebar.file_uploader(
56
- "Choose an image",
57
- type=['png', 'jpg', 'jpeg', 'tiff', 'bmp'],
58
- help="Upload microscopy image",
59
- key=f"image_uploader_{st.session_state.upload_key}" # Dynamic key
60
- )
61
 
62
- # Analysis button with unique key
63
- analyze_btn = st.sidebar.button("πŸš€ Analyze", type="primary", key="analyze_button")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
- # Main content
66
  col1, col2 = st.columns([1, 1])
67
 
68
- # Left: Image display
69
  with col1:
70
  st.subheader("πŸ–ΌοΈ Image")
71
 
72
- if uploaded_file is not None:
73
- debug_msg = f"πŸ› DEBUG: File uploaded - {uploaded_file.name}, size: {uploaded_file.size}"
74
- print(debug_msg)
75
- debug_container.info(debug_msg)
 
 
76
  try:
77
- # Reset file pointer to beginning (important!)
78
- uploaded_file.seek(0)
79
- print("DEBUG: File pointer reset to beginning")
80
-
81
- # Simple PIL loading - most reliable
82
- image = Image.open(uploaded_file)
83
- debug_msg2 = f"πŸ› DEBUG: Image loaded successfully - size: {image.size}, mode: {image.mode}"
84
- print(debug_msg2)
85
- debug_container.success(debug_msg2)
86
-
87
- # Resize if too large (prevent memory issues)
88
- max_size = (1024, 1024)
89
- if image.size[0] > max_size[0] or image.size[1] > max_size[1]:
90
- print(f"DEBUG: Resizing image from {image.size} to max {max_size}")
91
- image.thumbnail(max_size, Image.Resampling.LANCZOS)
92
- st.info(f"πŸ“ Image resized to {image.size} for display")
93
- print(f"DEBUG: Image resized to {image.size}")
94
-
95
- # Convert to RGB if needed
96
- if image.mode != 'RGB':
97
- image = image.convert('RGB')
98
-
99
- # Store in session state to prevent reprocessing
100
- if 'current_image' not in st.session_state or st.session_state.get('uploaded_filename') != uploaded_file.name:
101
- # Clear old image from memory
102
- if 'current_image' in st.session_state:
103
- del st.session_state.current_image
104
- gc.collect()
105
 
106
- st.session_state.current_image = image
107
- st.session_state.uploaded_filename = uploaded_file.name
 
 
 
108
 
109
- # Display image
110
- st.image(st.session_state.current_image, caption=f"Uploaded: {uploaded_file.name}", width=400)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
 
112
- # Basic info
113
- st.caption(f"Size: {st.session_state.current_image.size} | Mode: {st.session_state.current_image.mode}")
 
 
 
 
 
114
 
115
  except Exception as e:
116
- error_msg = f"πŸ› DEBUG: Error loading image: {e}"
117
- st.error(error_msg)
118
- debug_container.error(error_msg)
119
- # Don't show full traceback to users - just log it
120
- print(f"Image loading error: {traceback.format_exc()}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  else:
122
- st.info("πŸ‘† Upload an image to start")
 
 
 
123
 
124
- # Right: Analysis results
125
  with col2:
126
  st.subheader("🧠 Analysis Results")
127
 
128
- if analyze_btn and uploaded_file is not None:
129
- print("DEBUG: Analysis button clicked")
 
 
 
 
130
  try:
131
- # Simple mock analysis to test if basic functionality works
132
- st.success("βœ… Analysis Started!")
133
- print("DEBUG: Analysis started successfully")
134
-
135
- with st.spinner("Processing..."):
136
- # Mock processing
137
- import time
138
- print("DEBUG: Starting mock processing...")
139
- time.sleep(2)
140
- print("DEBUG: Mock processing complete")
141
-
142
- # Mock results
143
- st.markdown("### πŸ“Š Mock Analysis Results")
144
-
145
- st.write("**Stage 1: Global Analysis**")
146
- st.text_area("Description:",
147
- "Mock analysis: This appears to be a microscopy image with cellular structures. "
148
- "The image shows good contrast and appears suitable for analysis.",
149
- height=100)
150
-
151
- st.write("**Stage 2: Object Detection**")
152
- st.text_area("Objects:",
153
- "Mock detection: Multiple cellular objects detected. "
154
- "Estimated cell count: 15-25 cells visible.",
155
- height=100)
156
-
157
- st.success("βœ… Mock analysis complete!")
 
 
 
158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  except Exception as e:
160
- st.error(f"Analysis failed: {e}")
161
- st.code(traceback.format_exc())
 
 
 
162
 
163
  elif analyze_btn:
164
- st.warning("Please upload an image first!")
 
165
  else:
166
- st.info("πŸ‘ˆ Upload image and click Analyze")
167
 
168
  # Footer
169
  st.markdown("---")
170
- st.markdown("πŸ”¬ **Anton Framework** - Minimal Demo Version")
 
1
  #!/usr/bin/env python3
2
  """
3
+ Anton Simple UI - Streamlit Web Interface
4
+
5
+ Simple interface: Load image β†’ Click analyze β†’ See results
6
+ Perfect for quick microscopy analysis with VLM and CMPO annotations.
7
  """
8
 
9
  import streamlit as st
 
12
  from pathlib import Path
13
  import numpy as np
14
  from PIL import Image
15
+ import random
 
 
 
 
 
16
 
17
  # Setup page
18
  st.set_page_config(
 
21
  layout="wide"
22
  )
23
 
24
+ # Add Anton to path
25
+ sys.path.append(str(Path(__file__).parent))
26
+
27
+ # Import Anton components
28
+ try:
29
+ from anton.core.pipeline import AnalysisPipeline
30
+ from anton.utils.image_io import ImageLoader
31
+ from anton.cmpo.mapping import map_to_cmpo
32
+ from anton.cmpo.ontology import CMPOOntology
33
+ anton_available = True
34
+ except ImportError as e:
35
+ anton_available = False
36
+ import_error = str(e)
37
+
38
  # Header
39
  st.title("πŸ”¬ Anton Microscopy Analysis")
40
+ st.markdown("**Simple Interface**: Load image β†’ Click analyze β†’ See VLM analysis + CMPO phenotypes")
41
 
42
+ # Add helpful info for cloud users
43
+ st.info("πŸ’‘ **Demo Mode**: Upload your own microscopy images to test the analysis pipeline. The app works with PNG, JPG, TIFF, and BMP formats.")
44
 
45
+ # Check if Anton is available
46
+ if not anton_available:
47
+ st.error(f"❌ Anton not available: {import_error}")
48
+ st.stop()
49
+
50
+ # Sidebar for controls
51
  st.sidebar.header("πŸŽ›οΈ Controls")
52
 
53
+ # API Key check
54
  api_status = []
55
+ vlm_provider = "mock"
56
+
57
  if os.getenv('GOOGLE_API_KEY'):
58
  api_status.append("βœ… Google API Key")
59
+ vlm_provider = "gemini"
60
  elif os.getenv('ANTHROPIC_API_KEY'):
61
  api_status.append("βœ… Anthropic API Key")
62
+ vlm_provider = "claude"
63
  else:
64
+ api_status.append("⚠️ No API key - using mock")
65
 
66
  for status in api_status:
67
  st.sidebar.write(status)
68
 
69
+ st.sidebar.write(f"**VLM Provider**: {vlm_provider.upper()}")
 
70
 
71
+ # Dataset selection
72
+ st.sidebar.subheader("πŸ“ Image Source")
 
73
 
74
+ # Check for sample images
75
+ sample_images_path = Path("sample_images")
76
+ sample_images_available = sample_images_path.exists()
 
 
 
77
 
78
+ if sample_images_available:
79
+ # Get sample images
80
+ sample_images = sorted(list(sample_images_path.glob("*.BMP")))
81
+ if sample_images:
82
+ st.sidebar.success(f"βœ… Sample Images ({len(sample_images)} available)")
83
+
84
+ use_source = st.sidebar.radio("Select Source:", ["Sample Images", "Upload Image"])
85
+
86
+ if use_source == "Sample Images":
87
+ # Image selector
88
+ image_names = [img.name for img in sample_images]
89
+ selected_image = st.sidebar.selectbox("Choose Sample Image:", image_names)
90
+
91
+ if st.sidebar.button("🎲 Random Sample Image"):
92
+ selected_image = random.choice(image_names)
93
+ st.rerun()
94
+
95
+ sample_image_path = sample_images_path / selected_image
96
+ uploaded_file = None
97
+ else:
98
+ sample_image_path = None
99
+ uploaded_file = st.sidebar.file_uploader(
100
+ "Upload Image",
101
+ type=['png', 'jpg', 'jpeg', 'tiff', 'bmp'],
102
+ help="Upload your own microscopy image"
103
+ )
104
+ else:
105
+ sample_image_path = None
106
+ uploaded_file = st.sidebar.file_uploader(
107
+ "Upload Image",
108
+ type=['png', 'jpg', 'jpeg', 'tiff', 'bmp'],
109
+ help="Upload your own microscopy image for analysis"
110
+ )
111
+ else:
112
+ # Cloud deployment - sample images not available
113
+ st.sidebar.info("πŸ“ Upload your own microscopy images")
114
+ sample_image_path = None
115
+ uploaded_file = st.sidebar.file_uploader(
116
+ "Upload Image",
117
+ type=['png', 'jpg', 'jpeg', 'tiff', 'bmp'],
118
+ help="Upload your own microscopy image for analysis"
119
+ )
120
+
121
+ # Analysis controls
122
+ st.sidebar.subheader("πŸ”¬ Analysis")
123
+ analyze_btn = st.sidebar.button("πŸš€ Analyze Image", type="primary", use_container_width=True)
124
 
125
+ # Main content area
126
  col1, col2 = st.columns([1, 1])
127
 
128
+ # Left column: Image display
129
  with col1:
130
  st.subheader("πŸ–ΌοΈ Image")
131
 
132
+ # Load and display image
133
+ current_image = None
134
+ image_to_analyze = None
135
+
136
+ if sample_image_path and sample_image_path.exists():
137
+ # Sample image
138
  try:
139
+ loader = ImageLoader()
140
+ current_image = loader.load(str(sample_image_path))
141
+ image_to_analyze = str(sample_image_path)
142
+
143
+ # Image display controls
144
+ col_img1, col_img2 = st.columns([3, 1])
145
+
146
+ with col_img2:
147
+ st.markdown("**πŸ” Display Options:**")
148
+ zoom_level = st.select_slider(
149
+ "Zoom Level",
150
+ options=[0.5, 0.75, 1.0, 1.5, 2.0, 3.0],
151
+ value=1.0,
152
+ help="Adjust image size"
153
+ )
154
+
155
+ crop_enabled = st.checkbox("Enable Crop View", help="Show a cropped region of the image")
 
 
 
 
 
 
 
 
 
 
 
156
 
157
+ if crop_enabled:
158
+ st.markdown("**Crop Region:**")
159
+ crop_x = st.slider("X Start", 0, current_image.shape[1]-100, 0, step=10, key="sample_crop_x")
160
+ crop_y = st.slider("Y Start", 0, current_image.shape[0]-100, 0, step=10, key="sample_crop_y")
161
+ crop_size = st.slider("Crop Size", 100, min(current_image.shape), 200, step=10, key="sample_crop_size")
162
 
163
+ with col_img1:
164
+ # Apply zoom and crop
165
+ display_image = current_image.copy()
166
+
167
+ if crop_enabled:
168
+ # Crop the image
169
+ x_end = min(crop_x + crop_size, current_image.shape[1])
170
+ y_end = min(crop_y + crop_size, current_image.shape[0])
171
+ display_image = current_image[crop_y:y_end, crop_x:x_end]
172
+ caption_text = f"Sample: {sample_image_path.name} (Cropped)"
173
+ else:
174
+ caption_text = f"Sample: {sample_image_path.name}"
175
+
176
+ # Calculate display width based on zoom
177
+ base_width = 400
178
+ display_width = int(base_width * zoom_level)
179
+
180
+ st.image(
181
+ display_image,
182
+ caption=caption_text,
183
+ width=display_width,
184
+ use_column_width=False
185
+ )
186
 
187
+ # Image info
188
+ if crop_enabled:
189
+ st.caption(f"Original Shape: {current_image.shape} | Crop Shape: {display_image.shape}")
190
+ st.caption(f"Crop Region: [{crop_x}:{crop_x+crop_size}, {crop_y}:{crop_y+crop_size}]")
191
+ else:
192
+ st.caption(f"Shape: {current_image.shape} | Type: {current_image.dtype}")
193
+ st.caption(f"Range: [{current_image.min():.0f}, {current_image.max():.0f}]")
194
 
195
  except Exception as e:
196
+ st.error(f"Error loading sample image: {e}")
197
+
198
+ elif uploaded_file:
199
+ # Uploaded image
200
+ try:
201
+ # Use BytesIO to avoid file system issues in cloud deployment
202
+ from io import BytesIO
203
+ import tempfile
204
+
205
+ # Create a temporary file that gets cleaned up automatically
206
+ with tempfile.NamedTemporaryFile(delete=False, suffix=f"_{uploaded_file.name}") as tmp_file:
207
+ tmp_file.write(uploaded_file.getbuffer())
208
+ temp_path = tmp_file.name
209
+
210
+ # Try to load with ImageLoader first, fallback to PIL/OpenCV
211
+ try:
212
+ loader = ImageLoader()
213
+ current_image = loader.load(temp_path)
214
+ except Exception as loader_error:
215
+ st.warning(f"ImageLoader failed: {loader_error}, trying fallback...")
216
+ # Fallback to PIL
217
+ from PIL import Image
218
+ import numpy as np
219
+ pil_img = Image.open(temp_path)
220
+ current_image = np.array(pil_img)
221
+ if len(current_image.shape) == 3 and current_image.shape[2] == 3:
222
+ # Convert RGB to grayscale for analysis
223
+ current_image = np.mean(current_image, axis=2).astype(np.uint8)
224
+ image_to_analyze = temp_path
225
+
226
+ # Image display controls
227
+ col_img1, col_img2 = st.columns([3, 1])
228
+
229
+ with col_img2:
230
+ st.markdown("**πŸ” Display Options:**")
231
+ zoom_level = st.select_slider(
232
+ "Zoom Level",
233
+ options=[0.5, 0.75, 1.0, 1.5, 2.0, 3.0],
234
+ value=1.0,
235
+ help="Adjust image size"
236
+ )
237
+
238
+ crop_enabled = st.checkbox("Enable Crop View", help="Show a cropped region of the image")
239
+
240
+ if crop_enabled:
241
+ st.markdown("**Crop Region:**")
242
+ crop_x = st.slider("X Start", 0, current_image.shape[1]-100, 0, step=10, key="upload_crop_x")
243
+ crop_y = st.slider("Y Start", 0, current_image.shape[0]-100, 0, step=10, key="upload_crop_y")
244
+ crop_size = st.slider("Crop Size", 100, min(current_image.shape), 200, step=10, key="upload_crop_size")
245
+
246
+ with col_img1:
247
+ # Apply zoom and crop
248
+ display_image = current_image.copy()
249
+
250
+ if crop_enabled:
251
+ # Crop the image
252
+ x_end = min(crop_x + crop_size, current_image.shape[1])
253
+ y_end = min(crop_y + crop_size, current_image.shape[0])
254
+ display_image = current_image[crop_y:y_end, crop_x:x_end]
255
+ caption_text = f"Uploaded: {uploaded_file.name} (Cropped)"
256
+ else:
257
+ caption_text = f"Uploaded: {uploaded_file.name}"
258
+
259
+ # Calculate display width based on zoom
260
+ base_width = 400
261
+ display_width = int(base_width * zoom_level)
262
+
263
+ st.image(
264
+ display_image,
265
+ caption=caption_text,
266
+ width=display_width,
267
+ use_column_width=False
268
+ )
269
+
270
+ # Image info
271
+ if crop_enabled:
272
+ st.caption(f"Original Shape: {current_image.shape} | Crop Shape: {display_image.shape}")
273
+ st.caption(f"Crop Region: [{crop_x}:{crop_x+crop_size}, {crop_y}:{crop_y+crop_size}]")
274
+ else:
275
+ st.caption(f"Shape: {current_image.shape} | Type: {current_image.dtype}")
276
+ st.caption(f"Range: [{current_image.min():.0f}, {current_image.max():.0f}]")
277
+
278
+ except Exception as e:
279
+ st.error(f"Error loading uploaded image: {e}")
280
+ st.info("Please try uploading a different image format (PNG, JPG, TIFF, BMP)")
281
+ # Clean up temp file if it exists
282
+ try:
283
+ if 'temp_path' in locals() and os.path.exists(temp_path):
284
+ os.remove(temp_path)
285
+ except:
286
+ pass
287
  else:
288
+ if sample_images_available:
289
+ st.info("πŸ‘† Select a sample image or upload your own image to analyze")
290
+ else:
291
+ st.info("πŸ‘† Upload an image to analyze")
292
 
293
+ # Right column: Analysis results
294
  with col2:
295
  st.subheader("🧠 Analysis Results")
296
 
297
+ # Run analysis when button is clicked
298
+ if analyze_btn and image_to_analyze:
299
+
300
+ # Progress indicator
301
+ progress_bar = st.progress(0, text="Starting analysis...")
302
+
303
  try:
304
+ # Configure pipeline based on image source
305
+ if sample_image_path:
306
+ # BBBC013 dataset context
307
+ biological_context = {
308
+ "experiment_type": "protein_translocation",
309
+ "cell_line": "U2OS_osteosarcoma",
310
+ "protein": "FKHR-GFP",
311
+ "drugs": ["Wortmannin", "LY294002"],
312
+ "readout": "nuclear_vs_cytoplasmic_localization",
313
+ "channels": ["FKHR-GFP", "DNA_DRAQ"]
314
+ }
315
+ else:
316
+ # General microscopy context for uploaded images
317
+ biological_context = {
318
+ "experiment_type": "general_microscopy",
319
+ "cell_line": "unknown",
320
+ "readout": "cellular_morphology_and_phenotypes",
321
+ "channels": ["brightfield"]
322
+ }
323
+
324
+ config = {
325
+ "vlm_provider": vlm_provider,
326
+ "channels": [0],
327
+ "biological_context": biological_context
328
+ }
329
+
330
+ progress_bar.progress(20, text="Initializing pipeline...")
331
+
332
+ # Initialize and run pipeline
333
+ pipeline = AnalysisPipeline(config)
334
 
335
+ progress_bar.progress(40, text="Running VLM analysis...")
336
+ results = pipeline.run_pipeline_sync(image_to_analyze)
337
+
338
+ progress_bar.progress(80, text="Processing CMPO mappings...")
339
+
340
+ # Display results
341
+ progress_bar.progress(100, text="Analysis complete!")
342
+
343
+ # Clear progress bar
344
+ progress_bar.empty()
345
+
346
+ # Create tabs for results
347
+ tab1, tab2 = st.tabs(["🧠 VLM Analysis", "🧬 CMPO Phenotypes"])
348
+
349
+ with tab1:
350
+ st.markdown("### VLM Analysis Results")
351
+
352
+ # Display each stage
353
+ stages = [
354
+ ("Stage 1: Global Scene", "stage_1_global"),
355
+ ("Stage 2: Object Detection", "stage_2_objects"),
356
+ ("Stage 3: Feature Analysis", "stage_3_features"),
357
+ ("Stage 4: Population Insights", "stage_4_population")
358
+ ]
359
+
360
+ for stage_name, stage_key in stages:
361
+ if stage_key in results and results[stage_key]:
362
+ with st.expander(f"πŸ“‹ {stage_name}", expanded=(stage_key == "stage_1_global")):
363
+ stage_data = results[stage_key]
364
+
365
+ # Extract content for display
366
+ if 'description' in stage_data:
367
+ content = stage_data['description']
368
+ elif 'segmentation_guidance' in stage_data:
369
+ content = stage_data['segmentation_guidance']
370
+ elif 'population_summary' in stage_data:
371
+ content = stage_data['population_summary']
372
+ else:
373
+ content = f"Stage completed. Keys: {list(stage_data.keys())}"
374
+
375
+ # Clean up content for display
376
+ if content.startswith('```'):
377
+ lines = content.split('\n')
378
+ content = '\n'.join([line for line in lines if not line.strip().startswith('```')])
379
+
380
+ st.text_area(
381
+ f"{stage_name} Results:",
382
+ content[:2000] + "\n\n[Truncated for display...]" if len(content) > 2000 else content,
383
+ height=200,
384
+ key=f"text_{stage_key}"
385
+ )
386
+
387
+ with tab2:
388
+ st.markdown("### CMPO Phenotype Classifications")
389
+
390
+ try:
391
+ cmpo_mapper = CMPOOntology()
392
+ all_cmpo_results = []
393
+
394
+ # Test CMPO mapping on all stages
395
+ for stage_name, stage_key in stages:
396
+ if stage_key in results and results[stage_key]:
397
+ stage_data = results[stage_key]
398
+
399
+ # Extract text for CMPO mapping
400
+ stage_text = ""
401
+ if 'description' in stage_data:
402
+ stage_text = stage_data['description']
403
+ elif 'segmentation_guidance' in stage_data:
404
+ stage_text = stage_data['segmentation_guidance']
405
+ elif 'population_summary' in stage_data:
406
+ stage_text = stage_data['population_summary']
407
+
408
+ if stage_text and len(stage_text) > 50:
409
+ # Clean JSON formatting
410
+ if '```' in stage_text:
411
+ lines = stage_text.split('\n')
412
+ stage_text = '\n'.join([line for line in lines if not line.strip().startswith('```')])
413
+
414
+ # Use appropriate CMPO context
415
+ cmpo_context = 'protein_localization' if sample_image_path else 'general_microscopy'
416
+ cmpo_results = map_to_cmpo(stage_text, cmpo_mapper, context=cmpo_context)
417
+
418
+ if cmpo_results:
419
+ st.markdown(f"**{stage_name}:**")
420
+
421
+ for i, mapping in enumerate(cmpo_results[:3]): # Top 3 per stage
422
+ term_name = mapping.get('term_name', 'Unknown')
423
+ confidence = mapping.get('confidence', 0)
424
+ cmpo_id = mapping.get('CMPO_ID', 'Unknown')
425
+
426
+ # Color code by confidence
427
+ if confidence >= 4.5:
428
+ color = "green"
429
+ icon = "🟒"
430
+ elif confidence >= 3.5:
431
+ color = "orange"
432
+ icon = "🟑"
433
+ else:
434
+ color = "red"
435
+ icon = "🟠"
436
+
437
+ st.markdown(f"{icon} **{term_name}**")
438
+ st.caption(f"Confidence: {confidence:.2f} | ID: {cmpo_id}")
439
+
440
+ all_cmpo_results.extend(cmpo_results)
441
+ st.markdown("---")
442
+
443
+ # Summary
444
+ if all_cmpo_results:
445
+ st.markdown("### πŸ“Š Summary")
446
+ col_a, col_b = st.columns(2)
447
+
448
+ with col_a:
449
+ st.metric("Total CMPO Terms", len(all_cmpo_results))
450
+ with col_b:
451
+ unique_terms = len(set(m.get('CMPO_ID') for m in all_cmpo_results))
452
+ st.metric("Unique Terms", unique_terms)
453
+
454
+ # Top terms overall
455
+ st.markdown("**πŸ† Top Phenotypes Overall:**")
456
+ top_terms = sorted(all_cmpo_results, key=lambda x: x.get('confidence', 0), reverse=True)[:5]
457
+
458
+ for i, term in enumerate(top_terms, 1):
459
+ name = term.get('term_name', 'Unknown')
460
+ conf = term.get('confidence', 0)
461
+ st.write(f"{i}. **{name}** ({conf:.2f})")
462
+
463
+ else:
464
+ st.warning("No CMPO phenotypes identified in the analysis.")
465
+
466
+ except Exception as e:
467
+ st.error(f"CMPO mapping failed: {e}")
468
+
469
+ # Clean up temp file if it exists
470
+ if uploaded_file and 'temp_path' in locals():
471
+ try:
472
+ if os.path.exists(temp_path):
473
+ os.remove(temp_path)
474
+ except:
475
+ pass
476
+
477
  except Exception as e:
478
+ progress_bar.empty()
479
+ st.error(f"Analysis failed: {str(e)}")
480
+ import traceback
481
+ with st.expander("Error Details"):
482
+ st.code(traceback.format_exc())
483
 
484
  elif analyze_btn:
485
+ st.warning("Please select or upload an image first!")
486
+
487
  else:
488
+ st.info("πŸ‘ˆ Click 'Analyze Image' to start analysis")
489
 
490
  # Footer
491
  st.markdown("---")
492
+ st.markdown("πŸ”¬ **Anton Framework** - Hybrid Microscopy Analysis with VLM + Computer Vision")
requirements.txt CHANGED
@@ -1,5 +1,13 @@
1
- altair
2
  pandas
3
- streamlit
4
- Pillow
5
- numpy
 
 
 
 
 
 
 
 
 
 
 
1
  pandas
2
+ numpy
3
+ scikit-image
4
+ pillow
5
+ opencv-python
6
+ scipy
7
+ requests
8
+ google-generativeai
9
+ anthropic
10
+ python-dotenv
11
+ streamlit==1.30.0
12
+ plotly
13
+ networkx
sample_images/Channel1-01-A-01.BMP ADDED

Git LFS Details

  • SHA256: 39fe5efdad79eb2cea8447c32d78424d4c8c5aada5a50c2cedb6b58d0ebfa888
  • Pointer size: 131 Bytes
  • Size of remote file: 411 kB
sample_images/Channel1-02-A-02.BMP ADDED

Git LFS Details

  • SHA256: 440042201ce7e21edfcceed35e1004acee2f4c0afc61652c95b785f07718604f
  • Pointer size: 131 Bytes
  • Size of remote file: 411 kB
sample_images/Channel1-03-A-03.BMP ADDED

Git LFS Details

  • SHA256: a795c35ec714c15500d3a98d4a3203414ab72d0789365901bf51ab1efddace5a
  • Pointer size: 131 Bytes
  • Size of remote file: 411 kB
sample_images/Channel1-04-A-04.BMP ADDED

Git LFS Details

  • SHA256: 11ddcc8591789f6ee437f346c98a5418ea9a4d789e938eb536cb15bc5e2af501
  • Pointer size: 131 Bytes
  • Size of remote file: 411 kB
sample_images/Channel1-05-A-05.BMP ADDED

Git LFS Details

  • SHA256: e78dff62be379cce4f51d4e1600ec1206d129170c8a5409549bd62e68b87ecef
  • Pointer size: 131 Bytes
  • Size of remote file: 411 kB
sample_images/Channel1-06-A-06.BMP ADDED

Git LFS Details

  • SHA256: 8ce5fea94d02026ac487d2b7917b52d1920740a7c32422a52a1e10e2ccead672
  • Pointer size: 131 Bytes
  • Size of remote file: 411 kB
sample_images/Channel1-07-A-07.BMP ADDED

Git LFS Details

  • SHA256: a0bf8a93af90180a1a2b1dea56bab3059d093965fcb24392213f0035f4e79ece
  • Pointer size: 131 Bytes
  • Size of remote file: 411 kB
sample_images/Channel1-08-A-08.BMP ADDED

Git LFS Details

  • SHA256: 5991c805d26ae3dab2a267717ee2652c40c53f4027f516d335b8e6c3b95eba43
  • Pointer size: 131 Bytes
  • Size of remote file: 411 kB
sample_images/Channel1-09-A-09.BMP ADDED

Git LFS Details

  • SHA256: ee976bbaa534459e481a1e119e3ff18529a0fa521c657cd4a8d3836ddb61c937
  • Pointer size: 131 Bytes
  • Size of remote file: 411 kB
sample_images/Channel1-10-A-10.BMP ADDED

Git LFS Details

  • SHA256: ae522bf37ec78e5aafc5afe283e94fdb95b6d1c8f08c96c4cff4529c3aca1b48
  • Pointer size: 131 Bytes
  • Size of remote file: 411 kB