Spaces:
Sleeping
Sleeping
Commit ·
2d03f46
1
Parent(s): 61da00d
Update: Added estimated timer and cold-start model initialization warnings for Hugging Face Space
Browse files
app.py
CHANGED
|
@@ -4,10 +4,10 @@ import os
|
|
| 4 |
import sys
|
| 5 |
import tempfile
|
| 6 |
import pandas as pd
|
|
|
|
| 7 |
|
| 8 |
# Ensure bacsense_v2_package is importable from the repo root
|
| 9 |
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
|
| 10 |
-
from bacsense_v2_package.inference import BacSense
|
| 11 |
|
| 12 |
# Precaution dictionary
|
| 13 |
PRECAUTIONS = {
|
|
@@ -25,19 +25,12 @@ st.set_page_config(
|
|
| 25 |
layout="wide"
|
| 26 |
)
|
| 27 |
|
| 28 |
-
#
|
| 29 |
-
@st.cache_resource
|
| 30 |
def get_classifier():
|
|
|
|
| 31 |
model_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'bacsense_v2_package'))
|
| 32 |
-
|
| 33 |
-
model.warmup()
|
| 34 |
-
return model
|
| 35 |
-
|
| 36 |
-
try:
|
| 37 |
-
classifier = get_classifier()
|
| 38 |
-
except Exception as e:
|
| 39 |
-
st.error(f"Error loading model: {e}")
|
| 40 |
-
st.stop()
|
| 41 |
|
| 42 |
# Dialog function for detailed view
|
| 43 |
def render_details(item):
|
|
@@ -94,6 +87,9 @@ uploaded_files = st.file_uploader("Upload microscopic bacterial images...", type
|
|
| 94 |
if "selected_item" not in st.session_state:
|
| 95 |
st.session_state.selected_item = None
|
| 96 |
|
|
|
|
|
|
|
|
|
|
| 97 |
if uploaded_files:
|
| 98 |
if st.session_state.selected_item is not None:
|
| 99 |
render_details(st.session_state.selected_item)
|
|
@@ -106,9 +102,24 @@ if uploaded_files:
|
|
| 106 |
st.write(f"Processing {len(uploaded_files)} images...")
|
| 107 |
progress_bar = st.progress(0)
|
| 108 |
status_text = st.empty()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
|
| 110 |
for i, uploaded_file in enumerate(uploaded_files):
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
image = Image.open(uploaded_file)
|
| 114 |
fd, temp_path = tempfile.mkstemp(suffix=".png")
|
|
@@ -119,6 +130,8 @@ if uploaded_files:
|
|
| 119 |
image.save(f, format="PNG")
|
| 120 |
|
| 121 |
try:
|
|
|
|
|
|
|
| 122 |
prediction = classifier.predict(temp_path)
|
| 123 |
confidence_pct = prediction['confidence'] * 100 if prediction['confidence'] <= 1.0 else prediction['confidence']
|
| 124 |
results.append({
|
|
@@ -140,6 +153,8 @@ if uploaded_files:
|
|
| 140 |
|
| 141 |
progress_bar.progress((i + 1) / len(uploaded_files))
|
| 142 |
|
|
|
|
|
|
|
| 143 |
status_text.text("Batch Processing Complete!")
|
| 144 |
|
| 145 |
if results:
|
|
|
|
| 4 |
import sys
|
| 5 |
import tempfile
|
| 6 |
import pandas as pd
|
| 7 |
+
import time
|
| 8 |
|
| 9 |
# Ensure bacsense_v2_package is importable from the repo root
|
| 10 |
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
|
|
|
|
| 11 |
|
| 12 |
# Precaution dictionary
|
| 13 |
PRECAUTIONS = {
|
|
|
|
| 25 |
layout="wide"
|
| 26 |
)
|
| 27 |
|
| 28 |
+
# Lazy load — BacSense (and TensorFlow) only imported when first image is uploaded
|
| 29 |
+
@st.cache_resource(show_spinner="⏳ Loading BacSense model — this may take ~60s on first run...")
|
| 30 |
def get_classifier():
|
| 31 |
+
from bacsense_v2_package.inference import BacSense
|
| 32 |
model_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), 'bacsense_v2_package'))
|
| 33 |
+
return BacSense(model_dir=model_dir)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
# Dialog function for detailed view
|
| 36 |
def render_details(item):
|
|
|
|
| 87 |
if "selected_item" not in st.session_state:
|
| 88 |
st.session_state.selected_item = None
|
| 89 |
|
| 90 |
+
if "is_first_run" not in st.session_state:
|
| 91 |
+
st.session_state.is_first_run = True
|
| 92 |
+
|
| 93 |
if uploaded_files:
|
| 94 |
if st.session_state.selected_item is not None:
|
| 95 |
render_details(st.session_state.selected_item)
|
|
|
|
| 102 |
st.write(f"Processing {len(uploaded_files)} images...")
|
| 103 |
progress_bar = st.progress(0)
|
| 104 |
status_text = st.empty()
|
| 105 |
+
timer_text = st.empty()
|
| 106 |
+
|
| 107 |
+
# Calculate estimated time: 6s per image + 120s for cold start on HF
|
| 108 |
+
per_image_time = 6
|
| 109 |
+
cold_start_time = 120 if st.session_state.is_first_run else 10
|
| 110 |
+
total_seconds = int(len(uploaded_files) * per_image_time + cold_start_time)
|
| 111 |
+
start_time = time.time()
|
| 112 |
|
| 113 |
for i, uploaded_file in enumerate(uploaded_files):
|
| 114 |
+
current_elapsed = time.time() - start_time
|
| 115 |
+
remaining = max(1, total_seconds - int(current_elapsed))
|
| 116 |
+
|
| 117 |
+
if st.session_state.is_first_run and i == 0:
|
| 118 |
+
status_text.info(f"⏳ **Initializing VGG16 Model...** (This may take up to 2 mins on first run). \n\n Analyzing: {uploaded_file.name}")
|
| 119 |
+
else:
|
| 120 |
+
status_text.text(f"Analyzing [{i+1}/{len(uploaded_files)}]: {uploaded_file.name}...")
|
| 121 |
+
|
| 122 |
+
timer_text.markdown(f"⏱️ **Estimated time remaining: ~{remaining} seconds**")
|
| 123 |
|
| 124 |
image = Image.open(uploaded_file)
|
| 125 |
fd, temp_path = tempfile.mkstemp(suffix=".png")
|
|
|
|
| 130 |
image.save(f, format="PNG")
|
| 131 |
|
| 132 |
try:
|
| 133 |
+
# Retrieve the classifier (lazy-load)
|
| 134 |
+
classifier = get_classifier()
|
| 135 |
prediction = classifier.predict(temp_path)
|
| 136 |
confidence_pct = prediction['confidence'] * 100 if prediction['confidence'] <= 1.0 else prediction['confidence']
|
| 137 |
results.append({
|
|
|
|
| 153 |
|
| 154 |
progress_bar.progress((i + 1) / len(uploaded_files))
|
| 155 |
|
| 156 |
+
st.session_state.is_first_run = False
|
| 157 |
+
timer_text.empty()
|
| 158 |
status_text.text("Batch Processing Complete!")
|
| 159 |
|
| 160 |
if results:
|