diff --git a/results_simple/gridding_simple.png b/results_simple/gridding_simple.png index 3493df0..f7eb087 100644 Binary files a/results_simple/gridding_simple.png and b/results_simple/gridding_simple.png differ diff --git a/src/cDNA_gridding_simple.py b/src/cDNA_gridding_simple.py index 752a996..00eb2d1 100644 --- a/src/cDNA_gridding_simple.py +++ b/src/cDNA_gridding_simple.py @@ -1,10 +1,10 @@ """ -cDNA微阵列图像处理 —— 简化版网格划分 +cDNA微阵列图像处理 —— 简化版 ====================================== D:\ProgramData\anaconda3\envs\my_env\python.exe src/cDNA_gridding_simple.py -算法步骤: +算法步骤(划线): 1. 彩色图 → 灰度图 2. 横轴投影:对每一列的所有像素灰度值求和 → 得到一条曲线 @@ -17,6 +17,15 @@ D:\ProgramData\anaconda3\envs\my_env\python.exe src/cDNA_gridding_simple.py - 等于 0 的地方 = 斑点与空隙的分界线(过零点) 6. 配对相邻的过零点(离开斑点 + 进入下一个斑点), 中点就是空隙的中心 = 划线位置 + +算法步骤(Otsu阈值分割): + +1. 遍历所有可能的 T (0~255) +2. 计算前景(>T)的方差 + 背景(≤T)的方差 → 类内方差 +3. 计算前景均值 vs 背景均值的差距 → 类间方差 +4. 选 T 使 类内方差最小 / 类间方差最大 +5. 相当于在灰度直方图上找一个"谷底",两座山之间的最低处就是最佳分割线。 + """ import os @@ -154,6 +163,45 @@ def draw_grid_lines(gray: np.ndarray, pct: float = 0.10): return x_lines, y_lines +def otsu_threshold(gray: np.ndarray) -> tuple[np.ndarray, float]: + """ + Otsu 自动阈值分割。 + + 原理: + 遍历灰度值 0~255,对每个候选 T: + - 将像素分为两组:前景(>T) 和 背景(≤T) + - 计算两组各自的权重 w 和方差 σ² + - 类内方差 = w_bg*σ²_bg + w_fg*σ²_fg + 选使类内方差最小的 T = 最佳分割线。 + """ + best_T = 0 + best_cost = float('inf') + total = gray.size + + for T in range(1, 255): + # 背景(灰度 ≤ T) + bg = gray[gray <= T] + w_bg = len(bg) / total + # 前景(灰度 > T) + fg = gray[gray > T] + w_fg = len(fg) / total + + if w_bg == 0 or w_fg == 0: + continue + + var_bg = np.var(bg) + var_fg = np.var(fg) + # 类内方差 = 加权平均 + cost = w_bg * var_bg + w_fg * var_fg + + if cost < best_cost: + best_cost = cost + best_T = T + + binary = (gray > best_T).astype(np.uint8) + return binary, best_T + + def main(): os.makedirs(OUTPUT_DIR, exist_ok=True) @@ -162,24 +210,30 @@ def main(): # 原图是 RGBA(红绿蓝+透明通道),取前三个通道转为灰度 gray = (color.rgb2gray(img[:, :, :3]) * 255).astype(np.uint8) - # ---- 执行网格检测 ---- + # ---- 1. 网格划线 ---- x_lines, y_lines = draw_grid_lines(gray) print(f"检测到 {len(x_lines)} 条纵线, {len(y_lines)} 条横线") - # ---- 在原图上划线并保存 ---- - fig, ax = plt.subplots(figsize=(8, 8)) - ax.imshow(gray, cmap='gray') + # ---- 2. Otsu 阈值分割 ---- + binary, T_otsu = otsu_threshold(gray) + print(f"Otsu 最佳阈值: {T_otsu}") - # 画纵向分割线(竖线) + # ---- 3. 输出:左右对比(划线 vs 分割)---- + fig, axes = plt.subplots(1, 2, figsize=(14, 7)) + + # 左:网格划线 + axes[0].imshow(gray, cmap='gray') for x in x_lines: - ax.axvline(x=x, color='lime', linewidth=0.5) - - # 画横向分割线(横线) + axes[0].axvline(x=x, color='lime', linewidth=0.5) for y in y_lines: - ax.axhline(y=y, color='lime', linewidth=0.5) + axes[0].axhline(y=y, color='lime', linewidth=0.5) + axes[0].set_title(f'网格划分 ({len(x_lines)}×{len(y_lines)})', fontsize=13) + axes[0].axis('off') - ax.set_title(f'cDNA微阵列网格划分 ({len(x_lines)}×{len(y_lines)})', fontsize=14) - ax.axis('off') + # 右:Otsu 分割结果 + axes[1].imshow(binary, cmap='gray') + axes[1].set_title(f'Otsu 阈值分割 (T={T_otsu})', fontsize=13) + axes[1].axis('off') out_path = os.path.join(OUTPUT_DIR, 'gridding_simple.png') fig.savefig(out_path, dpi=150, bbox_inches='tight')