Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -399,102 +399,102 @@ def colorize_mask(mask: np.ndarray, num_colors: int = 512) -> np.ndarray:
|
|
| 399 |
# ===== 分割功能 =====
|
| 400 |
# ===== 彩色 mask 可视化 =====
|
| 401 |
def colorize_mask(mask: np.ndarray, num_colors: int = 512) -> np.ndarray:
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
def hsv_to_rgb(hh, ss, vv):
|
| 406 |
-
i = int(hh * 6.0)
|
| 407 |
-
f = hh * 6.0 - i
|
| 408 |
-
p = vv * (1.0 - ss)
|
| 409 |
-
q = vv * (1.0 - f * ss)
|
| 410 |
-
t = vv * (1.0 - (1.0 - f) * ss)
|
| 411 |
i = i % 6
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
elif i ==
|
| 417 |
-
|
| 418 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 419 |
|
| 420 |
-
palette = [(0, 0, 0)]
|
| 421 |
-
for k in range(1, num_colors):
|
| 422 |
-
hue = (k % num_colors) / float(num_colors)
|
| 423 |
-
palette.append(hsv_to_rgb(hue, 1.0, 0.95))
|
| 424 |
-
|
| 425 |
-
color_idx = mask % num_colors
|
| 426 |
palette_arr = np.array(palette, dtype=np.uint8)
|
|
|
|
| 427 |
return palette_arr[color_idx]
|
| 428 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 429 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
# ===== 推理 + 实例彩色可视化 =====
|
| 431 |
def segment_with_choice(use_box_choice, annot_value, mode="Overlay"):
|
| 432 |
-
|
| 433 |
if annot_value is None or len(annot_value) < 1:
|
| 434 |
-
|
|
|
|
| 435 |
|
| 436 |
img_path = annot_value[0]
|
| 437 |
bboxes = annot_value[1] if len(annot_value) > 1 else []
|
| 438 |
|
|
|
|
| 439 |
box_array = None
|
| 440 |
if use_box_choice == "Yes" and bboxes:
|
| 441 |
box = parse_first_bbox(bboxes)
|
| 442 |
if box:
|
| 443 |
xmin, ymin, xmax, ymax = map(int, box)
|
| 444 |
box_array = [[xmin, ymin, xmax, ymax]]
|
|
|
|
| 445 |
|
|
|
|
| 446 |
try:
|
| 447 |
-
mask =
|
| 448 |
-
|
| 449 |
-
return None, "❌ 分割失败"
|
| 450 |
except Exception as e:
|
| 451 |
-
return None, f"❌
|
| 452 |
|
|
|
|
| 453 |
try:
|
| 454 |
-
img = Image.open(img_path).convert("RGB")
|
| 455 |
-
|
| 456 |
-
img_np = np.array(img_rgb, dtype=np.float32)
|
| 457 |
if img_np.max() > 1.5:
|
| 458 |
-
img_np
|
| 459 |
except Exception as e:
|
| 460 |
-
return None, f"❌
|
| 461 |
|
| 462 |
-
inst_mask =
|
| 463 |
unique_ids = np.unique(inst_mask)
|
| 464 |
num_instances = len(unique_ids[unique_ids != 0])
|
|
|
|
| 465 |
|
| 466 |
if num_instances == 0:
|
| 467 |
-
return Image.new("RGB", mask.shape[::-1], (255,
|
| 468 |
|
| 469 |
-
#
|
| 470 |
-
|
|
|
|
|
|
|
|
|
|
| 471 |
|
| 472 |
-
#
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
|
| 477 |
-
|
| 478 |
-
for inst_id in unique_ids:
|
| 479 |
-
if inst_id == 0:
|
| 480 |
-
continue
|
| 481 |
-
binary_mask = (inst_mask == inst_id).astype(np.uint8)
|
| 482 |
-
contours = measure.find_contours(binary_mask, 0.5)
|
| 483 |
-
for contour in contours:
|
| 484 |
-
contour = contour.astype(np.int32)
|
| 485 |
-
for dy in [-1, 0, 1]:
|
| 486 |
-
for dx in [-1, 0, 1]:
|
| 487 |
-
yy = np.clip(contour[:, 0] + dy, 0, blended.shape[0] - 1).astype(np.int32)
|
| 488 |
-
xx = np.clip(contour[:, 1] + dx, 0, blended.shape[1] - 1).astype(np.int32)
|
| 489 |
-
blended[yy, xx] = [1.0, 1.0, 0.0] # 黄色边
|
| 490 |
-
|
| 491 |
-
blended = np.clip(blended * 255.0, 0, 255).astype(np.uint8)
|
| 492 |
-
result_text = f"✅ 检测到 {num_instances} 个细胞"
|
| 493 |
-
|
| 494 |
-
if mode == "Instance Mask Only":
|
| 495 |
-
return Image.fromarray((color_mask * 255).astype(np.uint8)), result_text
|
| 496 |
-
|
| 497 |
-
return Image.fromarray(blended), result_text
|
| 498 |
|
| 499 |
# ===== 计数功能 =====
|
| 500 |
def count_cells_handler(image_path):
|
|
|
|
| 399 |
# ===== 分割功能 =====
|
| 400 |
# ===== 彩色 mask 可视化 =====
|
| 401 |
def colorize_mask(mask: np.ndarray, num_colors: int = 512) -> np.ndarray:
|
| 402 |
+
def hsv_to_rgb(h, s, v):
|
| 403 |
+
i = int(h * 6.0)
|
| 404 |
+
f = h * 6.0 - i
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
i = i % 6
|
| 406 |
+
p = v * (1 - s)
|
| 407 |
+
q = v * (1 - f * s)
|
| 408 |
+
t = v * (1 - (1 - f) * s)
|
| 409 |
+
if i == 0: r, g, b = v, t, p
|
| 410 |
+
elif i == 1: r, g, b = q, v, p
|
| 411 |
+
elif i == 2: r, g, b = p, v, t
|
| 412 |
+
elif i == 3: r, g, b = p, q, v
|
| 413 |
+
elif i == 4: r, g, b = t, p, v
|
| 414 |
+
else: r, g, b = v, p, q
|
| 415 |
+
return int(r * 255), int(g * 255), int(b * 255)
|
| 416 |
+
|
| 417 |
+
palette = [(0, 0, 0)] # 背景为黑色
|
| 418 |
+
for i in range(1, num_colors):
|
| 419 |
+
h = (i % num_colors) / float(num_colors)
|
| 420 |
+
palette.append(hsv_to_rgb(h, 1.0, 0.95))
|
| 421 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
palette_arr = np.array(palette, dtype=np.uint8)
|
| 423 |
+
color_idx = mask % num_colors
|
| 424 |
return palette_arr[color_idx]
|
| 425 |
|
| 426 |
+
def overlay_instances(img, mask, alpha=0.5, cmap_name="tab20"):
|
| 427 |
+
img = img.astype(np.float32)
|
| 428 |
+
if len(img.shape) == 2:
|
| 429 |
+
img = np.stack([img]*3, axis=-1)
|
| 430 |
+
if img.max() > 1.5:
|
| 431 |
+
img = img / 255.0
|
| 432 |
|
| 433 |
+
overlay = img.copy()
|
| 434 |
+
cmap = cm.get_cmap(cmap_name, np.max(mask) + 1)
|
| 435 |
+
|
| 436 |
+
for inst_id in np.unique(mask):
|
| 437 |
+
if inst_id == 0:
|
| 438 |
+
continue
|
| 439 |
+
color = np.array(cmap(inst_id)[:3])
|
| 440 |
+
overlay[mask == inst_id] = (1 - alpha) * overlay[mask == inst_id] + alpha * color
|
| 441 |
+
|
| 442 |
+
return overlay
|
| 443 |
# ===== 推理 + 实例彩色可视化 =====
|
| 444 |
def segment_with_choice(use_box_choice, annot_value, mode="Overlay"):
|
| 445 |
+
prepare_cuda()
|
| 446 |
if annot_value is None or len(annot_value) < 1:
|
| 447 |
+
print("❌ No annotation input")
|
| 448 |
+
return None, "请上传图像"
|
| 449 |
|
| 450 |
img_path = annot_value[0]
|
| 451 |
bboxes = annot_value[1] if len(annot_value) > 1 else []
|
| 452 |
|
| 453 |
+
print(f"🖼️ 图像路径: {img_path}")
|
| 454 |
box_array = None
|
| 455 |
if use_box_choice == "Yes" and bboxes:
|
| 456 |
box = parse_first_bbox(bboxes)
|
| 457 |
if box:
|
| 458 |
xmin, ymin, xmax, ymax = map(int, box)
|
| 459 |
box_array = [[xmin, ymin, xmax, ymax]]
|
| 460 |
+
print(f"📦 使用边界框: {box_array}")
|
| 461 |
|
| 462 |
+
# === Run model
|
| 463 |
try:
|
| 464 |
+
mask = run(MODEL, img_path, box=box_array, device=DEVICE)
|
| 465 |
+
print("📏 mask shape:", mask.shape, "unique ids:", np.unique(mask))
|
|
|
|
| 466 |
except Exception as e:
|
| 467 |
+
return None, f"❌ 推理失败: {str(e)}"
|
| 468 |
|
| 469 |
+
# === 读取原图
|
| 470 |
try:
|
| 471 |
+
img = Image.open(img_path).convert("RGB").resize(mask.shape[::-1], resample=Image.BILINEAR)
|
| 472 |
+
img_np = np.array(img).astype(np.float32)
|
|
|
|
| 473 |
if img_np.max() > 1.5:
|
| 474 |
+
img_np /= 255.0
|
| 475 |
except Exception as e:
|
| 476 |
+
return None, f"❌ 图像读取失败: {str(e)}"
|
| 477 |
|
| 478 |
+
inst_mask = mask.astype(np.int32)
|
| 479 |
unique_ids = np.unique(inst_mask)
|
| 480 |
num_instances = len(unique_ids[unique_ids != 0])
|
| 481 |
+
print(f"✅ 实例数量: {num_instances}")
|
| 482 |
|
| 483 |
if num_instances == 0:
|
| 484 |
+
return Image.new("RGB", mask.shape[::-1], (255, 0, 0)), "⚠️ 未检测到实例"
|
| 485 |
|
| 486 |
+
# === 可视化:Overlay 模式
|
| 487 |
+
if mode == "Overlay":
|
| 488 |
+
overlay = overlay_instances(img_np, inst_mask, alpha=0.5, cmap_name="tab20")
|
| 489 |
+
overlay_img = Image.fromarray((overlay * 255).astype(np.uint8))
|
| 490 |
+
return overlay_img, f"✅ 检测到 {num_instances} 个��胞"
|
| 491 |
|
| 492 |
+
# === 可视化:纯彩色 mask
|
| 493 |
+
elif mode == "Instance Mask Only":
|
| 494 |
+
color_mask = colorize_mask(inst_mask, num_colors=512)
|
| 495 |
+
return Image.fromarray(color_mask), f"✅ 检测到 {num_instances} 个细胞"
|
| 496 |
|
| 497 |
+
return None, "❓ 无效显示模式"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 498 |
|
| 499 |
# ===== 计数功能 =====
|
| 500 |
def count_cells_handler(image_path):
|