2. 声呐图像阴影区域预处理与增强
任务对象与处理链
本节处理的是侧扫声呐或前视声呐的单帧灰度图像。目标阴影在图像中表现为目标回波亮带后方的低灰度暗区,其可辨识度取决于两个量:阴影区与背景海底的灰度差(决定信噪比)和阴影边界处的灰度跃变陡度(决定边缘清晰度)。预处理的目标不是"让图变好看",而是在不破坏阴影几何形状的前提下,同时抬高这两项指标。
处理链固定为四步:灰度归一化 → 自适应直方图均衡(CLAHE)→ 形态学滤波 → 指标评估。归一化消除不同增益/量程下的灰度尺度差异;CLAHE 在局部窗口内拉伸对比度,避免全局均衡把大面积均匀海底噪声一并放大;形态学滤波用开运算去除孤立亮点、闭运算填补阴影内部小孔洞,使阴影区域成为连通的暗块,便于后续分割。
关键实现与参数选择
下面给出可直接运行的 Python 实现,使用 OpenCV 与 NumPy。输入为单通道 8 位灰度图,输出为增强图与四项指标。所有参数均为教学示例值,实际使用时需按声呐型号与量程调整。
import cv2
import numpy as np
def normalize_gray(img, lo_pct=2, hi_pct=98):
"""百分位截断归一化,抑制极亮回波与极暗噪声对尺度的影响"""
lo, hi = np.percentile(img, (lo_pct, hi_pct))
hi = max(hi, lo + 1e-6)
out = (img.astype(np.float32) - lo) / (hi - lo)
return np.clip(out, 0, 1)
def enhance_shadow(img_u8, tile=8, clip=2.0, k_open=3, k_close=5):
"""完整预处理链:归一化 -> CLAHE -> 形态学滤波"""
norm = normalize_gray(img_u8)
norm_u8 = (norm * 255).astype(np.uint8)
clahe = cv2.createCLAHE(clipLimit=clip, tileGridSize=(tile, tile))
eq = clahe.apply(norm_u8)
ko = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (k_open, k_open))
kc = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (k_close, k_close))
opened = cv2.morphologyEx(eq, cv2.MORPH_OPEN, ko) # 去孤立亮点
closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kc) # 补阴影内孔洞
return closed
def shadow_metrics(img, mask):
"""mask 为阴影区域二值掩膜;背景取掩膜外区域"""
img_f = img.astype(np.float32)
sh = img_f[mask > 0]
bg = img_f[mask == 0]
mu_sh, mu_bg = sh.mean(), bg.mean()
sigma_bg = bg.std() + 1e-6
snr = (mu_bg - mu_sh) / sigma_bg
# 边缘清晰度:阴影边界处 Sobel 梯度幅值均值
gx = cv2.Sobel(img_f, cv2.CV_32F, 1, 0, ksize=3)
gy = cv2.Sobel(img_f, cv2.CV_32F, 0, 1, ksize=3)
mag = np.sqrt(gx**2 + gy**2)
edge = cv2.Canny((mask * 255).astype(np.uint8), 50, 150) > 0
edge = cv2.dilate(edge.astype(np.uint8), np.ones((3, 3), np.uint8)) > 0
edge_grad = mag[edge].mean() if edge.any() else 0.0
return snr, edge_grad
# 使用示例(img 为读入的灰度声呐图,mask 为人工标注或粗分割的阴影掩膜)
# enhanced = enhance_shadow(img)
# snr0, eg0 = shadow_metrics(img, mask)
# snr1, eg1 = shadow_metrics(enhanced, mask)
# print(f"SNR: {snr0:.2f} -> {snr1:.2f} dB, 边缘梯度: {eg0:.2f} -> {eg1:.2f}")
参数影响与判读
三个参数对结果的影响方向不同,调参时需分开观察:
- clipLimit:过小(如 1.0)时局部对比度提升有限,SNR 改善不明显;过大(如 5.0 以上)会把海底纹理噪声放大成伪阴影,导致背景标准差 σ_bg 上升,反而压低 SNR。教学示例中 2.0 是较稳的起点。
- tileGridSize:tile 越小,局部适应性越强,但块间插值可能产生块状伪影,破坏阴影边界的连续性;tile 越大越接近全局均衡。8×8 适合 512×512 左右的图像。
- 形态学核尺寸:开运算核应小于目标阴影的最小宽度,否则会侵蚀阴影本身;闭运算核应大于阴影内部孔洞尺寸。核过大将导致阴影面积被系统性改变,影响后续几何特征计算。
形态学滤波会改变阴影的边界位置与面积。若后续需要精确的几何特征(面积、长宽比),应在滤波前的图像上计算,或记录滤波前后的面积偏差作为修正量。
验证与通过条件
验证采用同一掩膜在增强前后分别计算指标,避免因掩膜变化引入偏差。通过条件为:SNR 提升不低于 3 dB,且阴影边界平均梯度幅值提升不低于 20%。若 SNR 提升但边缘梯度下降,说明 CLAHE 过度平滑了边界,应降低 clipLimit 或减小 tile 尺寸;若边缘梯度提升但 SNR 下降,通常是背景噪声被放大,应减小 clipLimit 并检查归一化的百分位截断是否过窄。
| 观察现象 | 可能原因 | 调整方向 |
|---|---|---|
| 阴影内部出现亮斑 | 闭运算核过小,孔洞未填补 | 增大 k_close |
| 阴影边界被侵蚀变窄 | 开运算核大于阴影宽度 | 减小 k_open |
| 背景出现块状纹理 | tile 过小或 clipLimit 过高 | 增大 tile,降低 clip |
| 整体偏暗、对比度不足 | 归一化百分位截断过宽 | 收窄至 p5–p95 |