update
This commit is contained in:
@@ -81,13 +81,23 @@ def run_sam_prompt(
|
||||
b = np.asarray(box_xyxy, dtype=np.float32).reshape(4)
|
||||
box_arg = b
|
||||
|
||||
masks, scores, _low = predictor.predict(
|
||||
masks3, scores3, _low3 = predictor.predict(
|
||||
point_coords=pc,
|
||||
point_labels=pl,
|
||||
box=box_arg,
|
||||
multimask_output=True,
|
||||
)
|
||||
m = _pick_best_sam_mask(masks, scores, pc, pl)
|
||||
m_multi = _union_sam_masks_covering_foreground(masks3, scores3, pc, pl)
|
||||
|
||||
masks1, scores1, _low1 = predictor.predict(
|
||||
point_coords=pc,
|
||||
point_labels=pl,
|
||||
box=box_arg,
|
||||
multimask_output=False,
|
||||
)
|
||||
m_single = _to_bool_mask(masks1[0])
|
||||
|
||||
m = _prefer_larger_foreground_mask(m_multi, m_single, pc, pl)
|
||||
m = _refine_sam_instance_mask(m)
|
||||
return m
|
||||
|
||||
@@ -96,15 +106,26 @@ def _mask_contains_foreground_points(
|
||||
mask_bool: np.ndarray,
|
||||
point_coords: np.ndarray,
|
||||
point_labels: np.ndarray,
|
||||
*,
|
||||
dilate_for_hit: int = 0,
|
||||
) -> bool:
|
||||
"""dilate_for_hit>0 时:前景点落在膨胀后的掩膜内即算覆盖(缓解边缘亚像素偏差)。"""
|
||||
m = np.asarray(mask_bool, dtype=bool)
|
||||
if dilate_for_hit > 0:
|
||||
try:
|
||||
from scipy import ndimage as ndi # type: ignore[import]
|
||||
|
||||
m = ndi.binary_dilation(m, structure=np.ones((3, 3), dtype=bool), iterations=dilate_for_hit)
|
||||
except Exception:
|
||||
pass
|
||||
for (x, y), lab in zip(point_coords, point_labels):
|
||||
if int(lab) != 1:
|
||||
continue
|
||||
xi = int(round(float(x)))
|
||||
yi = int(round(float(y)))
|
||||
if yi < 0 or xi < 0 or yi >= mask_bool.shape[0] or xi >= mask_bool.shape[1]:
|
||||
if yi < 0 or xi < 0 or yi >= m.shape[0] or xi >= m.shape[1]:
|
||||
return False
|
||||
if not bool(mask_bool[yi, xi]):
|
||||
if not bool(m[yi, xi]):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -115,45 +136,74 @@ def _to_bool_mask(m: np.ndarray) -> np.ndarray:
|
||||
return m > 0.5
|
||||
|
||||
|
||||
def _pick_best_sam_mask(
|
||||
def _union_sam_masks_covering_foreground(
|
||||
masks: np.ndarray,
|
||||
scores: np.ndarray,
|
||||
point_coords: np.ndarray,
|
||||
point_labels: np.ndarray,
|
||||
) -> np.ndarray:
|
||||
"""
|
||||
多掩膜时:优先覆盖全部前景点;在高分候选中取面积最大,减少“只切到局部/缺边”的概率。
|
||||
对「包含全部前景点」的候选掩膜做逻辑并,通常比单选一片更接近完整物体(船身+棚+人物等)。
|
||||
若并集过大(误吞整图)则退回「有效候选中面积最大」的一片。
|
||||
"""
|
||||
n = int(masks.shape[0])
|
||||
valid_idx: List[int] = []
|
||||
valid: List[np.ndarray] = []
|
||||
for i in range(n):
|
||||
mb = _to_bool_mask(masks[i])
|
||||
if _mask_contains_foreground_points(mb, point_coords, point_labels):
|
||||
valid_idx.append(i)
|
||||
if not valid_idx:
|
||||
if _mask_contains_foreground_points(mb, point_coords, point_labels, dilate_for_hit=2):
|
||||
valid.append(mb)
|
||||
if not valid:
|
||||
best = int(np.argmax(scores))
|
||||
return _to_bool_mask(masks[best])
|
||||
smax = max(float(scores[j]) for j in valid_idx)
|
||||
near = [j for j in valid_idx if float(scores[j]) >= smax - 0.12]
|
||||
best_area = -1
|
||||
best_j = near[0]
|
||||
for j in near:
|
||||
mb = _to_bool_mask(masks[j])
|
||||
area = int(np.count_nonzero(mb))
|
||||
if area > best_area:
|
||||
best_area = area
|
||||
best_j = j
|
||||
return _to_bool_mask(masks[best_j])
|
||||
if len(valid) == 1:
|
||||
return valid[0]
|
||||
u = np.zeros_like(valid[0], dtype=bool)
|
||||
for v in valid:
|
||||
u |= v
|
||||
area_u = int(np.count_nonzero(u))
|
||||
h, w = u.shape[:2]
|
||||
if area_u > int(0.62 * h * w):
|
||||
return max(valid, key=lambda x: int(np.count_nonzero(x)))
|
||||
return u
|
||||
|
||||
|
||||
def _prefer_larger_foreground_mask(
|
||||
a: np.ndarray,
|
||||
b: np.ndarray,
|
||||
point_coords: np.ndarray,
|
||||
point_labels: np.ndarray,
|
||||
) -> np.ndarray:
|
||||
"""在「多掩膜并集」与「单掩膜」之间选:均含前景点时优先面积明显更大者(更接近整物体)。"""
|
||||
a = np.asarray(a, dtype=bool)
|
||||
b = np.asarray(b, dtype=bool)
|
||||
ca = _mask_contains_foreground_points(a, point_coords, point_labels, dilate_for_hit=2)
|
||||
cb = _mask_contains_foreground_points(b, point_coords, point_labels, dilate_for_hit=2)
|
||||
aa = int(np.count_nonzero(a))
|
||||
ab = int(np.count_nonzero(b))
|
||||
|
||||
if ca and cb:
|
||||
if ab > aa * 1.08:
|
||||
return b
|
||||
if aa > ab * 1.08:
|
||||
return a
|
||||
return a if aa >= ab else b
|
||||
if ca:
|
||||
return a
|
||||
if cb:
|
||||
return b
|
||||
return a if aa >= ab else b
|
||||
|
||||
|
||||
def _refine_sam_instance_mask(mask_bool: np.ndarray) -> np.ndarray:
|
||||
"""填内部孔洞 + 轻微闭运算,使复杂物体轮廓更完整。"""
|
||||
"""填孔 + 闭运算连接断裂,再轻微膨胀以包全外轮廓。"""
|
||||
m = np.asarray(mask_bool, dtype=bool)
|
||||
try:
|
||||
from scipy import ndimage as ndi # type: ignore[import]
|
||||
|
||||
m = ndi.binary_fill_holes(m)
|
||||
m = ndi.binary_closing(m, structure=np.ones((5, 5), dtype=bool))
|
||||
m = ndi.binary_closing(m, structure=np.ones((3, 3), dtype=bool))
|
||||
m = ndi.binary_dilation(m, structure=np.ones((3, 3), dtype=bool), iterations=1)
|
||||
return m
|
||||
except Exception:
|
||||
pass
|
||||
@@ -161,8 +211,9 @@ def _refine_sam_instance_mask(mask_bool: np.ndarray) -> np.ndarray:
|
||||
import cv2 # type: ignore[import]
|
||||
|
||||
u8 = (m.astype(np.uint8) * 255)
|
||||
k = np.ones((3, 3), np.uint8)
|
||||
u8 = cv2.morphologyEx(u8, cv2.MORPH_CLOSE, k)
|
||||
k5 = np.ones((5, 5), np.uint8)
|
||||
u8 = cv2.morphologyEx(u8, cv2.MORPH_CLOSE, k5)
|
||||
u8 = cv2.dilate(u8, np.ones((3, 3), np.uint8), iterations=1)
|
||||
return u8 > 127
|
||||
except Exception:
|
||||
return m
|
||||
@@ -239,7 +290,7 @@ def mask_to_contour_xy(
|
||||
if cv2.contourArea(cnt) < 1.0:
|
||||
return []
|
||||
peri = cv2.arcLength(cnt, True)
|
||||
eps = max(epsilon_px, 0.001 * peri)
|
||||
eps = max(epsilon_px, 0.0025 * peri)
|
||||
approx = cv2.approxPolyDP(cnt, eps, True)
|
||||
out: List[List[float]] = []
|
||||
for p in approx:
|
||||
|
||||
Reference in New Issue
Block a user