refactor: 后处理阈值完全自动化,零人工参数
- keep_largest_object: 每格仅留最大块,不设最低门槛 - remove_small_objects: 统计全局面积中位数,<25%自动判定为噪声
This commit is contained in:
+24
-20
@@ -203,27 +203,36 @@ def gridding(gray: np.ndarray) -> tuple:
|
|||||||
# 第四部分:后处理(参考choice.m, choosemaxobj.m)
|
# 第四部分:后处理(参考choice.m, choosemaxobj.m)
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
def remove_small_objects(binary: np.ndarray, cell_area: int = 1225, pct: float = 0.02) -> np.ndarray:
|
def remove_small_objects(binary: np.ndarray) -> np.ndarray:
|
||||||
"""去除面积小于 cell_area*pct 的连通域(pct默认2%)"""
|
"""
|
||||||
min_size = int(cell_area * pct)
|
自动去除小连通域。
|
||||||
|
|
||||||
|
统计所有连通域的面积中位数,小于中位数 25% 的视为噪声,
|
||||||
|
自动剔除,不需要人设定阈值。
|
||||||
|
"""
|
||||||
labeled, num = ndimage.label(binary)
|
labeled, num = ndimage.label(binary)
|
||||||
|
if num == 0:
|
||||||
|
return binary
|
||||||
|
|
||||||
|
# 统计每个连通域的面积
|
||||||
|
areas = [int(np.sum(labeled == i)) for i in range(1, num + 1)]
|
||||||
|
median_area = np.median(areas)
|
||||||
|
min_size = max(1, int(median_area * 0.25)) # 中位数的25%,最少1像素
|
||||||
|
|
||||||
result = binary.copy()
|
result = binary.copy()
|
||||||
for i in range(1, num + 1):
|
for i in range(1, num + 1):
|
||||||
if np.sum(labeled == i) < min_size:
|
if areas[i - 1] < min_size:
|
||||||
result[labeled == i] = 0
|
result[labeled == i] = 0
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def keep_largest_object(binary: np.ndarray, cell_area: int = 1225, pct: float = 0.01) -> np.ndarray:
|
def keep_largest_object(binary: np.ndarray) -> np.ndarray:
|
||||||
"""只保留最大连通域,若最大块面积 < cell_area*pct 则整块抹黑(pct默认1%)"""
|
"""每个格子里只保留面积最大的连通域(无需设定阈值)"""
|
||||||
min_size = int(cell_area * pct)
|
|
||||||
labeled, num = ndimage.label(binary)
|
labeled, num = ndimage.label(binary)
|
||||||
if num == 0:
|
if num == 0:
|
||||||
return binary
|
|
||||||
areas = [int(np.sum(labeled == i)) for i in range(1, num + 1)]
|
|
||||||
max_area = max(areas)
|
|
||||||
if max_area < min_size:
|
|
||||||
return np.zeros_like(binary)
|
return np.zeros_like(binary)
|
||||||
|
|
||||||
|
areas = [int(np.sum(labeled == i)) for i in range(1, num + 1)]
|
||||||
max_idx = int(np.argmax(areas)) + 1
|
max_idx = int(np.argmax(areas)) + 1
|
||||||
return (labeled == max_idx).astype(np.uint8)
|
return (labeled == max_idx).astype(np.uint8)
|
||||||
|
|
||||||
@@ -375,11 +384,6 @@ def main() -> None:
|
|||||||
|
|
||||||
# ---- 步骤3: 全图逐块分割(Otsu + TV去噪) ----
|
# ---- 步骤3: 全图逐块分割(Otsu + TV去噪) ----
|
||||||
print("\n[步骤3] 全图逐块分割...")
|
print("\n[步骤3] 全图逐块分割...")
|
||||||
# 计算每个格子的面积,用于自适应后处理阈值
|
|
||||||
cell_h = int(np.median(np.diff(y_grid))) if len(y_grid) > 1 else 35
|
|
||||||
cell_w = int(np.median(np.diff(x_grid))) if len(x_grid) > 1 else 35
|
|
||||||
cell_area = cell_h * cell_w
|
|
||||||
|
|
||||||
bw_full = np.zeros_like(gray)
|
bw_full = np.zeros_like(gray)
|
||||||
if len(x_grid) >= 2 and len(y_grid) >= 2:
|
if len(x_grid) >= 2 and len(y_grid) >= 2:
|
||||||
for i in range(len(y_grid) - 1):
|
for i in range(len(y_grid) - 1):
|
||||||
@@ -403,12 +407,12 @@ def main() -> None:
|
|||||||
bw_blk = (blk_denoised > T).astype(np.uint8)
|
bw_blk = (blk_denoised > T).astype(np.uint8)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
bw_blk = np.zeros(blk.shape, dtype=np.uint8)
|
bw_blk = np.zeros(blk.shape, dtype=np.uint8)
|
||||||
# 后处理:保留最大连通域(最小面积 = 格子面积的1%)
|
# 后处理:保留最大连通域(无需阈值)
|
||||||
bw_blk = keep_largest_object(bw_blk, cell_area=cell_area)
|
bw_blk = keep_largest_object(bw_blk)
|
||||||
bw_full[r1:r2, c1:c2] = bw_blk
|
bw_full[r1:r2, c1:c2] = bw_blk
|
||||||
|
|
||||||
# 全局去小连通域(最小面积 = 格子面积的2%)
|
# 全局去除小连通域(中位数的25%以下自动判为噪声)
|
||||||
bw_full = remove_small_objects(bw_full, cell_area=cell_area)
|
bw_full = remove_small_objects(bw_full)
|
||||||
|
|
||||||
fig_full = plot_full_segmentation(gray, bw_full, "全图逐块Otsu分割结果")
|
fig_full = plot_full_segmentation(gray, bw_full, "全图逐块Otsu分割结果")
|
||||||
fig_full.savefig(os.path.join(OUTPUT_DIR, 'result_full_segmentation.png'), dpi=150, bbox_inches='tight')
|
fig_full.savefig(os.path.join(OUTPUT_DIR, 'result_full_segmentation.png'), dpi=150, bbox_inches='tight')
|
||||||
|
|||||||
Reference in New Issue
Block a user