Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -157,9 +157,9 @@ def overlay_instances(img, mask, alpha=0.5, cmap_name="tab20"):
|
|
| 157 |
|
| 158 |
# ===== 分割功能 =====
|
| 159 |
def segment_with_choice(use_box_choice, annot_value):
|
| 160 |
-
"""分割主函数 - 固定使用Overlay
|
| 161 |
if annot_value is None or len(annot_value) < 1:
|
| 162 |
-
return None
|
| 163 |
|
| 164 |
img_path = annot_value[0]
|
| 165 |
bboxes = annot_value[1] if len(annot_value) > 1 else []
|
|
@@ -179,7 +179,13 @@ def segment_with_choice(use_box_choice, annot_value):
|
|
| 179 |
print("📏 mask shape:", mask.shape, "unique ids:", np.unique(mask))
|
| 180 |
except Exception as e:
|
| 181 |
print(f"❌ 推理失败: {str(e)}")
|
| 182 |
-
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
|
| 184 |
# 读取原图
|
| 185 |
try:
|
|
@@ -189,7 +195,7 @@ def segment_with_choice(use_box_choice, annot_value):
|
|
| 189 |
img_np /= 255.0
|
| 190 |
except Exception as e:
|
| 191 |
print(f"❌ 图像读取失败: {str(e)}")
|
| 192 |
-
return None
|
| 193 |
|
| 194 |
inst_mask = mask.astype(np.int32)
|
| 195 |
unique_ids = np.unique(inst_mask)
|
|
@@ -197,16 +203,16 @@ def segment_with_choice(use_box_choice, annot_value):
|
|
| 197 |
print(f"✅ 实例数量: {num_instances}")
|
| 198 |
|
| 199 |
if num_instances == 0:
|
| 200 |
-
return Image.new("RGB", mask.shape[::-1], (255, 0, 0))
|
| 201 |
|
| 202 |
# 使用Overlay模式可视化
|
| 203 |
overlay = overlay_instances(img_np, inst_mask, alpha=0.5, cmap_name="tab20")
|
| 204 |
overlay_img = Image.fromarray((overlay * 255).astype(np.uint8))
|
| 205 |
-
return overlay_img
|
| 206 |
|
| 207 |
# ===== 计数功能 =====
|
| 208 |
def count_cells_handler(image_path):
|
| 209 |
-
"""计数处理函数"""
|
| 210 |
if image_path is None:
|
| 211 |
return None, "⚠️ 请先上传图像"
|
| 212 |
|
|
@@ -225,12 +231,34 @@ def count_cells_handler(image_path):
|
|
| 225 |
return None, f"❌ 计数失败: {result['error']}"
|
| 226 |
|
| 227 |
count = result['count']
|
| 228 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
result_text = f"✅ 检测到 {count:.1f} 个细胞"
|
| 230 |
|
| 231 |
print(f"✅ Counting done - Count: {count:.1f}")
|
| 232 |
|
| 233 |
-
return
|
| 234 |
|
| 235 |
except Exception as e:
|
| 236 |
print(f"❌ Counting error: {e}")
|
|
@@ -369,6 +397,12 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
|
|
| 369 |
height=400
|
| 370 |
)
|
| 371 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 372 |
# 满意度评分
|
| 373 |
score_slider = gr.Slider(
|
| 374 |
minimum=1,
|
|
@@ -398,7 +432,7 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
|
|
| 398 |
run_seg_btn.click(
|
| 399 |
fn=segment_with_choice,
|
| 400 |
inputs=[use_box_radio, annotator],
|
| 401 |
-
outputs=seg_output
|
| 402 |
)
|
| 403 |
|
| 404 |
# 初始化Gallery显示
|
|
@@ -486,7 +520,7 @@ with gr.Blocks(title="Microscopy Analysis Suite", theme=gr.themes.Soft()) as dem
|
|
| 486 |
|
| 487 |
with gr.Column(scale=2):
|
| 488 |
count_output = gr.Image(
|
| 489 |
-
label="📸
|
| 490 |
type="filepath",
|
| 491 |
height=500
|
| 492 |
)
|
|
|
|
| 157 |
|
| 158 |
# ===== 分割功能 =====
|
| 159 |
def segment_with_choice(use_box_choice, annot_value):
|
| 160 |
+
"""分割主函数 - 固定使用Overlay模式,同时返回原始mask"""
|
| 161 |
if annot_value is None or len(annot_value) < 1:
|
| 162 |
+
return None, None
|
| 163 |
|
| 164 |
img_path = annot_value[0]
|
| 165 |
bboxes = annot_value[1] if len(annot_value) > 1 else []
|
|
|
|
| 179 |
print("📏 mask shape:", mask.shape, "unique ids:", np.unique(mask))
|
| 180 |
except Exception as e:
|
| 181 |
print(f"❌ 推理失败: {str(e)}")
|
| 182 |
+
return None, None
|
| 183 |
+
|
| 184 |
+
# 保存原始mask为TIF文件
|
| 185 |
+
temp_mask_file = tempfile.NamedTemporaryFile(delete=False, suffix=".tif")
|
| 186 |
+
mask_img = Image.fromarray(mask.astype(np.uint16))
|
| 187 |
+
mask_img.save(temp_mask_file.name)
|
| 188 |
+
print(f"💾 原始mask保存到: {temp_mask_file.name}")
|
| 189 |
|
| 190 |
# 读取原图
|
| 191 |
try:
|
|
|
|
| 195 |
img_np /= 255.0
|
| 196 |
except Exception as e:
|
| 197 |
print(f"❌ 图像读取失败: {str(e)}")
|
| 198 |
+
return None, None
|
| 199 |
|
| 200 |
inst_mask = mask.astype(np.int32)
|
| 201 |
unique_ids = np.unique(inst_mask)
|
|
|
|
| 203 |
print(f"✅ 实例数量: {num_instances}")
|
| 204 |
|
| 205 |
if num_instances == 0:
|
| 206 |
+
return Image.new("RGB", mask.shape[::-1], (255, 0, 0)), None
|
| 207 |
|
| 208 |
# 使用Overlay模式可视化
|
| 209 |
overlay = overlay_instances(img_np, inst_mask, alpha=0.5, cmap_name="tab20")
|
| 210 |
overlay_img = Image.fromarray((overlay * 255).astype(np.uint8))
|
| 211 |
+
return overlay_img, temp_mask_file.name
|
| 212 |
|
| 213 |
# ===== 计数功能 =====
|
| 214 |
def count_cells_handler(image_path):
|
| 215 |
+
"""计数处理函数 - 只返回密度图"""
|
| 216 |
if image_path is None:
|
| 217 |
return None, "⚠️ 请先上传图像"
|
| 218 |
|
|
|
|
| 231 |
return None, f"❌ 计数失败: {result['error']}"
|
| 232 |
|
| 233 |
count = result['count']
|
| 234 |
+
|
| 235 |
+
# 只提取密度图部分(假设visualized_path是拼接图,我们只要右半部分)
|
| 236 |
+
viz_path = result.get('visualized_path')
|
| 237 |
+
|
| 238 |
+
# 如果有density_map_path,直接使用
|
| 239 |
+
if 'density_map_path' in result:
|
| 240 |
+
density_path = result['density_map_path']
|
| 241 |
+
elif viz_path and os.path.exists(viz_path):
|
| 242 |
+
# 如果是拼接图,提取右半部分(密度图)
|
| 243 |
+
try:
|
| 244 |
+
viz_img = Image.open(viz_path)
|
| 245 |
+
w, h = viz_img.size
|
| 246 |
+
# 取右半部分
|
| 247 |
+
density_img = viz_img.crop((w//2, 0, w, h))
|
| 248 |
+
# 保存为新文件
|
| 249 |
+
temp_density = tempfile.NamedTemporaryFile(delete=False, suffix=".png")
|
| 250 |
+
density_img.save(temp_density.name)
|
| 251 |
+
density_path = temp_density.name
|
| 252 |
+
except:
|
| 253 |
+
density_path = viz_path
|
| 254 |
+
else:
|
| 255 |
+
density_path = viz_path
|
| 256 |
+
|
| 257 |
result_text = f"✅ 检测到 {count:.1f} 个细胞"
|
| 258 |
|
| 259 |
print(f"✅ Counting done - Count: {count:.1f}")
|
| 260 |
|
| 261 |
+
return density_path, result_text
|
| 262 |
|
| 263 |
except Exception as e:
|
| 264 |
print(f"❌ Counting error: {e}")
|
|
|
|
| 397 |
height=400
|
| 398 |
)
|
| 399 |
|
| 400 |
+
# 下载原始预测结果
|
| 401 |
+
download_mask_btn = gr.File(
|
| 402 |
+
label="📥 下载原始预测 (.tif 格式)",
|
| 403 |
+
visible=True
|
| 404 |
+
)
|
| 405 |
+
|
| 406 |
# 满意度评分
|
| 407 |
score_slider = gr.Slider(
|
| 408 |
minimum=1,
|
|
|
|
| 432 |
run_seg_btn.click(
|
| 433 |
fn=segment_with_choice,
|
| 434 |
inputs=[use_box_radio, annotator],
|
| 435 |
+
outputs=[seg_output, download_mask_btn]
|
| 436 |
)
|
| 437 |
|
| 438 |
# 初始化Gallery显示
|
|
|
|
| 520 |
|
| 521 |
with gr.Column(scale=2):
|
| 522 |
count_output = gr.Image(
|
| 523 |
+
label="📸 密度图",
|
| 524 |
type="filepath",
|
| 525 |
height=500
|
| 526 |
)
|