Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -398,62 +398,84 @@ def colorize_mask(mask: np.ndarray, num_colors: int = 512) -> np.ndarray:
|
|
| 398 |
|
| 399 |
# ===== 分割功能 =====
|
| 400 |
def segment_with_choice(use_box_choice, annot_value, mode="Overlay"):
|
| 401 |
-
"""
|
| 402 |
if annot_value is None or len(annot_value) < 1:
|
|
|
|
| 403 |
return None, "⚠️ 请先上传图像"
|
| 404 |
|
| 405 |
img_path = annot_value[0]
|
| 406 |
bboxes = annot_value[1] if len(annot_value) > 1 else []
|
| 407 |
|
| 408 |
-
print(f"🖼️
|
| 409 |
-
|
| 410 |
box_array = None
|
| 411 |
if use_box_choice == "Yes" and bboxes:
|
| 412 |
box = parse_first_bbox(bboxes)
|
| 413 |
if box:
|
| 414 |
xmin, ymin, xmax, ymax = map(int, box)
|
| 415 |
-
box_array =
|
| 416 |
print(f"📦 Using box: {box_array}")
|
| 417 |
|
| 418 |
try:
|
|
|
|
| 419 |
mask = run_seg(SEG_MODEL, img_path, box=box_array, device=SEG_DEVICE)
|
| 420 |
-
|
| 421 |
-
return None, "❌ 分割失败"
|
| 422 |
-
print(f"✅ Segmentation done - Mask shape: {mask.shape}")
|
| 423 |
except Exception as e:
|
| 424 |
-
print(f"❌
|
| 425 |
return None, f"分割失败: {str(e)}"
|
| 426 |
|
| 427 |
try:
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
img_rgb = np.array(img, dtype=np.float32)
|
| 431 |
-
if img_rgb.max() > 1.5:
|
| 432 |
-
img_rgb = img_rgb / 255.0
|
| 433 |
except Exception as e:
|
| 434 |
-
print(f"❌
|
| 435 |
-
return None, f"
|
| 436 |
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 |
alpha = 0.5
|
|
|
|
| 442 |
|
| 443 |
-
for inst_id in np.unique(
|
| 444 |
-
if inst_id == 0:
|
| 445 |
continue
|
| 446 |
-
|
| 447 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
|
| 449 |
overlay = np.clip(overlay * 255.0, 0, 255).astype(np.uint8)
|
| 450 |
-
overlay_img = Image.fromarray(overlay)
|
| 451 |
|
| 452 |
-
#
|
| 453 |
-
|
| 454 |
-
|
| 455 |
|
| 456 |
-
return
|
| 457 |
|
| 458 |
# ===== 计数功能 =====
|
| 459 |
def count_cells_handler(image_path):
|
|
|
|
| 398 |
|
| 399 |
# ===== 分割功能 =====
|
| 400 |
def segment_with_choice(use_box_choice, annot_value, mode="Overlay"):
|
| 401 |
+
"""分割处理函数(与原始版本完全对齐,适配新版 run_seg 接口)"""
|
| 402 |
if annot_value is None or len(annot_value) < 1:
|
| 403 |
+
print("❌ No annotation input")
|
| 404 |
return None, "⚠️ 请先上传图像"
|
| 405 |
|
| 406 |
img_path = annot_value[0]
|
| 407 |
bboxes = annot_value[1] if len(annot_value) > 1 else []
|
| 408 |
|
| 409 |
+
print(f"🖼️ Image path: {img_path}")
|
| 410 |
+
|
| 411 |
box_array = None
|
| 412 |
if use_box_choice == "Yes" and bboxes:
|
| 413 |
box = parse_first_bbox(bboxes)
|
| 414 |
if box:
|
| 415 |
xmin, ymin, xmax, ymax = map(int, box)
|
| 416 |
+
box_array = [[xmin, ymin, xmax, ymax]]
|
| 417 |
print(f"📦 Using box: {box_array}")
|
| 418 |
|
| 419 |
try:
|
| 420 |
+
# 🔹 调用新版 run_seg 接口
|
| 421 |
mask = run_seg(SEG_MODEL, img_path, box=box_array, device=SEG_DEVICE)
|
| 422 |
+
print("📏 Mask shape:", mask.shape, "dtype:", mask.dtype, "unique:", np.unique(mask))
|
|
|
|
|
|
|
| 423 |
except Exception as e:
|
| 424 |
+
print(f"❌ Error during inference: {e}")
|
| 425 |
return None, f"分割失败: {str(e)}"
|
| 426 |
|
| 427 |
try:
|
| 428 |
+
img = Image.open(img_path)
|
| 429 |
+
print("📷 Image mode:", img.mode, "size:", img.size)
|
|
|
|
|
|
|
|
|
|
| 430 |
except Exception as e:
|
| 431 |
+
print(f"❌ Failed to open image: {e}")
|
| 432 |
+
return None, f"图像打开失败: {str(e)}"
|
| 433 |
|
| 434 |
+
try:
|
| 435 |
+
img_rgb = img.convert("RGB").resize(mask.shape[::-1], resample=Image.BILINEAR)
|
| 436 |
+
img_np = np.array(img_rgb, dtype=np.float32)
|
| 437 |
+
if img_np.max() > 1.5:
|
| 438 |
+
img_np = img_np / 255.0
|
| 439 |
+
except Exception as e:
|
| 440 |
+
print(f"❌ Error in image conversion/resizing: {e}")
|
| 441 |
+
return None, f"图像预处理失败: {str(e)}"
|
| 442 |
+
|
| 443 |
+
# 🔹 mask → 实例掩码
|
| 444 |
+
mask_np = np.array(mask)
|
| 445 |
+
inst_mask = mask_np.astype(np.int32)
|
| 446 |
+
unique_ids = np.unique(inst_mask)
|
| 447 |
+
num_instances = len(unique_ids[unique_ids != 0])
|
| 448 |
+
print(f"✅ Instance IDs found: {unique_ids}, Total instances: {num_instances}")
|
| 449 |
+
|
| 450 |
+
if num_instances == 0:
|
| 451 |
+
print("⚠️ No instance found, returning dummy red image")
|
| 452 |
+
return Image.new("RGB", mask.shape[::-1], (255, 0, 0)), "⚠️ 未检测到细胞"
|
| 453 |
+
|
| 454 |
+
# ==== 🎨 Color Overlay (每个实例一个颜色) ====
|
| 455 |
+
overlay = img_np.copy()
|
| 456 |
alpha = 0.5
|
| 457 |
+
cmap = cm.get_cmap("nipy_spectral", num_instances + 1)
|
| 458 |
|
| 459 |
+
for inst_id in np.unique(inst_mask):
|
| 460 |
+
if inst_id == 0:
|
| 461 |
continue
|
| 462 |
+
binary_mask = (inst_mask == inst_id).astype(np.uint8)
|
| 463 |
+
color = np.array(cmap(inst_id / (num_instances + 1))[:3]) # RGB only
|
| 464 |
+
overlay[binary_mask == 1] = (1 - alpha) * overlay[binary_mask == 1] + alpha * color
|
| 465 |
+
|
| 466 |
+
# 🟡 绘制轮廓
|
| 467 |
+
contours = measure.find_contours(binary_mask, 0.5)
|
| 468 |
+
for contour in contours:
|
| 469 |
+
contour = contour.astype(np.int32)
|
| 470 |
+
overlay[contour[:, 0], contour[:, 1]] = [1.0, 1.0, 0.0] # 黄色轮廓
|
| 471 |
|
| 472 |
overlay = np.clip(overlay * 255.0, 0, 255).astype(np.uint8)
|
|
|
|
| 473 |
|
| 474 |
+
# 🔹 两种显示模式
|
| 475 |
+
if mode == "Instance Mask Only":
|
| 476 |
+
return Image.fromarray(colorize_mask(inst_mask, num_colors=512)), f"✅ 检测到 {num_instances} 个细胞"
|
| 477 |
|
| 478 |
+
return Image.fromarray(overlay), f"✅ 检测到 {num_instances} 个细胞"
|
| 479 |
|
| 480 |
# ===== 计数功能 =====
|
| 481 |
def count_cells_handler(image_path):
|